import '@vechain/connex-framework/dist/driver-interface' ; import Deferred from './deferred' ; import * as Helper from './helper' ; const DEFAULT_TOS_URL = 'https://tos.vecha.in/' ; const ACCEPTED_SUFFIX = '.accepted' ; const RESP_SUFFIX = '.resp' ; /** sign request relayed by tos */ type RelayedRequest = { type : 'tx' | 'cert' ; gid : string ; payload : { message : object ; options : object ; } ; nonce : string ; } ; /** sign response relayed by tos */ type RelayedResponse = { error ? : string ; payload ? : object ; } ; function sleep ( ms ) { return new Promise ( resolve => { setTimeout ( resolve , ms ) ; } ) ; } async function submitRequest ( rid , json , tosUrl , abort ) { const src = new URL ( rid , tosUrl ) . href ; for ( let i = 0 ; i < 3 ; i ++ ) { try { return await Promise . race ( [ abort , fetch ( src , { method : 'POST' , body : json , headers : new Headers ( { 'Content-Type' : 'application/json' } ) } ) ] ) ; } catch { await Promise . race ( [ abort , sleep ( 2000 ) ] ) ; } } throw new Error ( 'failed to submit request' ) ; } async function pollResponse ( rid , suffix , timeout , tosUrl , abort ) { let errCount = 0 ; const deadline = Date . now ( ) + timeout ; while ( Date . now ( ) < deadline ) { try { const resp = await Promise . race ( [ abort , fetch ( new URL ( `${ rid }${ suffix }?wait=1` , tosUrl ) . href ) ] ) ; const text = await Promise . race ( [ abort , resp . text ( ) ] ) ; if ( text ) { return text ; } } catch ( err ) { if ( ++ errCount > 2 ) { throw new Error ( 'failed fetch response' ) ; } await Promise . race ( [ abort , sleep ( 3000 ) ] ) ; } } throw new Error ( 'timeout' ) ; } let _abort = null ; async function sign < T extends 'tx' | 'cert' > ( type , msg , options , genesisId , nonce , blake2b256 , tosUrl ) { if ( _abort ) { _abort . reject ( new Error ( 'aborted' ) ) ; } const abort = _abort = new Deferred < never > ( ) ; const onAccepted = options . onAccepted ; const req = { type , gid : genesisId , payload : { message : msg , options : { ... options , onAccepted : undefined } } , nonce : nonce ( ) } ; const json = JSON . stringify ( req ) ; const rid = blake2b256 ( json ) ; const src = new URL ( rid , tosUrl ) . href ; const helper = Helper . connect ( src ) ; let accepted = false ; try { await submitRequest ( rid , json , tosUrl , abort ) ; void ( async ( ) => { try { await Promise . race ( [ abort , sleep ( 1500 ) ] ) ; ! accepted && helper . show ( ) ; } catch { } } ) ( ) ; void ( async ( ) => { try { await pollResponse ( rid , ACCEPTED_SUFFIX , 60 * 1000 , tosUrl , abort ) ; accepted = true ; helper . hide ( ) ; onAccepted && onAccepted ( ) ; } catch { } } ) ( ) ; const respJson = await pollResponse ( rid , RESP_SUFFIX , 10 * 60 * 1000 , tosUrl , abort ) ; const resp = JSON . parse ( respJson ) ; if ( resp . error ) { throw new Error ( resp . error ) ; } return resp . payload as any ; } finally { abort . reject ( new Error ( 'aborted' ) ) ; helper . hide ( ) ; } } param genesisId param nonce param blake2b256 param tosUrl export function create ( genesisId , nonce , blake2b256 , tosUrl ? ) { return { signTx ( msg , options ) { return sign ( 'tx' , msg , options , genesisId , nonce , blake2b256 , tosUrl || DEFAULT_TOS_URL ) ; } , signCert ( msg , options ) { return sign ( 'cert' , msg , options , genesisId , nonce , blake2b256 , tosUrl || DEFAULT_TOS_URL ) ; } } ; }