import axios , { AxiosResponse } from "axios" ; import { Logger , LogLevel , nullLogger , SyncOrPromise , utils , } from "@renproject/utils" ; const generatePayload = ( method , params ? ) => ( { id : 1 , jsonrpc : "2.0" , method , params , } ) ; export interface Provider < Requests extends { [ event ] : any ; } = { } , Responses extends { [ event ] : any ; } = { } > { sendMessage < Method extends keyof Requests & string > ( method , request , retry ? , timeout ? ) : SyncOrPromise < Responses [ Method ] > ; } export class HttpProvider < Requests extends { [ event ] : any ; } = { } , Responses extends { [ event ] : any ; } = { } > implements Provider < Requests , Responses > { public readonly endpointOrProvider : Provider < Requests , Responses > | string ; public readonly logger : Logger ; param endpointOrProvider param logger public constructor ( endpointOrProvider , logger = nullLogger ) { this . logger = logger ; if ( typeof endpointOrProvider === "string" && endpointOrProvider . indexOf ( "://" ) === - 1 ) { throw new Error ( `Invalid node URL without protocol: ${ endpointOrProvider }.` ) ; } this . endpointOrProvider = endpointOrProvider ; } public sendMessage = async < Method extends keyof Requests & string > ( method , request , retry = 2 , timeout = 120 * utils . sleep . SECONDS ) : Promise < Responses [ Method ] > => { const endpoint = this . endpointOrProvider ; if ( typeof endpoint !== "string" ) { return endpoint . sendMessage ( method , request , retry , timeout ) ; } const payload = generatePayload ( method , request ) ; if ( this . logger . getLevel && this . logger . getLevel ( ) <= LogLevel . Debug ) { this . logger . debug ( "[request]" , JSON . stringify ( payload , null , "    " ) ) ; } try { const response = await utils . tryNTimes ( async ( ) => axios . post < JSONRPCResponse < Responses [ Method ] > > ( endpoint , payload , { timeout } ) , retry , 1 * utils . sleep . SECONDS ) ; if ( response . status !== 200 ) { throw this . responseError ( `Unexpected status code ${ String ( response . status ) } returned from node.` , response ) ; } if ( response . data . error ) { throw new Error ( response . data . error ) ; } if ( response . data . result === undefined ) { throw new Error ( `Empty result returned from node.` ) ; } if ( this . logger . getLevel && this . logger . getLevel ( ) <= LogLevel . Debug ) { this . logger . debug ( "[response]" , JSON . stringify ( response . data . result , null , "    " ) ) ; } return response . data . result ; } catch ( error ) { throw new Error ( utils . extractError ( error ) ) ; } } ; private readonly responseError = ( msg , response ) : ResponseError => { const error = new Error ( msg ) as ResponseError ; error . response = response ; return error ; } ; } interface ResponseError extends Error { response : AxiosResponse ; } export type JSONRPCResponse < T > = { jsonrpc : string ; version : string ; result : T ; error : undefined ; id : number ; } | { jsonrpc : string ; version : string ; result : undefined ; error : any ; id : number ; } ;