import axios , { AxiosInstance , AxiosPromise , AxiosRequestConfig , CancelToken , CancelTokenSource , } from "axios" ; const URL = require ( "url-parse" ) ; import debug from "debug" ; const log = debug ( "qtumjs:rpc" ) ; import { sleep } from "./sleep" ; export interface IJSONRPCRequest { id : any ; method : string ; params : any [ ] ; auth ? : string ; } export interface IAuthorization { id : string ; state : "pending" | "accepted" | "denied" | "consumed" ; request : IJSONRPCRequest ; createdAt : string ; } export interface IRPCCallOption { cancelToken ? : CancelToken ; } export class QtumRPCRaw { private idNonce : number ; private _api : AxiosInstance ; constructor ( private _baseURL ) { this . idNonce = 0 ; const url = new URL ( _baseURL ) ; const config = { baseURL : url . origin , validateStatus : ( ) => true , } ; if ( url . username !== "" && url . password !== "" ) { config . auth = { username : url . username , password : url . password , } ; } this . _api = axios . create ( config ) ; } public cancelTokenSource ( ) { return axios . CancelToken . source ( ) ; } public async rawCall ( method , params = [ ] , opts = { } ) { const rpcCall = { method , params , id : this . idNonce ++ , } ; log ( "%O" , { method , params , } ) ; let res = await this . makeRPCCall ( rpcCall ) ; if ( res . status === 402 ) { const auth = res . data ; res = await this . authCall ( auth . id , rpcCall ) ; } if ( res . status === 401 ) { throw new Error ( await res . statusText ) ; } if ( res . status === 404 ) { throw new Error ( `unknown method: ${ method }` ) ; } if ( res . status !== 200 ) { if ( res . headers [ "content-type" ] !== "application/json" ) { const body = await res . data ; throw new Error ( `${ res . status } ${ res . statusText }\n${ res . data }` ) ; } const eresult = await res . data ; if ( eresult . error ) { const { code , message } = eresult . error ; throw new Error ( `RPC Error: [${ code }] ${ message }` ) ; } else { throw new Error ( String ( eresult ) ) ; } } const { result } = await res . data ; return result ; } private makeRPCCall ( rpcCall ) { return this . _api . post ( "/" , rpcCall ) ; } private async authCall ( authID , rpcCall ) { const res = await this . _api . get ( `/api/authorizations/${ authID }/onchange` ) ; const { data } = res ; if ( res . status !== 200 ) { throw new Error ( data . message ) ; } const auth = data ; if ( auth . state === "denied" ) { throw new Error ( `Authorization denied: ${ authID }` ) ; } if ( auth . state === "accepted" ) { return this . makeRPCCall ( { ... rpcCall , auth : auth . id , } ) ; } } }