2022-05-01 13:30:11 -04:00
import console from "console" ;
import HavenoUtils from "./utils/HavenoUtils" ;
import TaskLooper from "./utils/TaskLooper" ;
import type * as grpcWeb from "grpc-web" ;
import { GetVersionClient , AccountClient , MoneroConnectionsClient , DisputesClient , DisputeAgentsClient , NotificationsClient , WalletsClient , PriceClient , OffersClient , PaymentAccountsClient , TradesClient , ShutdownServerClient , MoneroNodeClient } from './protobuf/GrpcServiceClientPb' ;
2022-05-10 14:53:10 -04:00
import { GetVersionRequest , GetVersionReply , IsAppInitializedRequest , IsAppInitializedReply , RegisterDisputeAgentRequest , MarketPriceRequest , MarketPriceReply , MarketPricesRequest , MarketPricesReply , MarketPriceInfo , MarketDepthRequest , MarketDepthReply , MarketDepthInfo , GetBalancesRequest , GetBalancesReply , XmrBalanceInfo , GetMyOfferRequest , GetMyOfferReply , GetOffersRequest , GetOffersReply , OfferInfo , GetPaymentMethodsRequest , GetPaymentMethodsReply , GetPaymentAccountFormRequest , CreatePaymentAccountRequest , CreatePaymentAccountReply , GetPaymentAccountFormReply , GetPaymentAccountsRequest , GetPaymentAccountsReply , CreateCryptoCurrencyPaymentAccountRequest , CreateCryptoCurrencyPaymentAccountReply , CreateOfferRequest , CreateOfferReply , CancelOfferRequest , TakeOfferRequest , TakeOfferReply , TradeInfo , GetTradeRequest , GetTradeReply , GetTradesRequest , GetTradesReply , GetXmrSeedRequest , GetXmrSeedReply , GetNewDepositAddressRequest , GetNewDepositAddressReply , ConfirmPaymentStartedRequest , ConfirmPaymentReceivedRequest , XmrTx , GetXmrTxsRequest , GetXmrTxsReply , XmrDestination , CreateXmrTxRequest , CreateXmrTxReply , RelayXmrTxRequest , RelayXmrTxReply , CreateAccountRequest , AccountExistsRequest , AccountExistsReply , DeleteAccountRequest , OpenAccountRequest , IsAccountOpenRequest , IsAccountOpenReply , CloseAccountRequest , ChangePasswordRequest , BackupAccountRequest , BackupAccountReply , RestoreAccountRequest , StopRequest , NotificationMessage , RegisterNotificationListenerRequest , SendNotificationRequest , UrlConnection , AddConnectionRequest , RemoveConnectionRequest , GetConnectionRequest , GetConnectionsRequest , SetConnectionRequest , CheckConnectionRequest , CheckConnectionsReply , CheckConnectionsRequest , StartCheckingConnectionsRequest , StopCheckingConnectionsRequest , GetBestAvailableConnectionRequest , SetAutoSwitchRequest , CheckConnectionReply , GetConnectionsReply , GetConnectionReply , GetBestAvailableConnectionReply , GetDisputeRequest , GetDisputeReply , GetDisputesRequest , GetDisputesReply , OpenDisputeRequest , ResolveDisputeRequest , SendDisputeChatMessageRequest , SendChatMessageRequest , GetChatMessagesRequest , GetChatMessagesReply , StartMoneroNodeRequest , StopMoneroNodeRequest , IsMoneroNodeRunningRequest , IsMoneroNodeRunningReply , GetMoneroNodeSettingsRequest , GetMoneroNodeSettingsReply } from "./protobuf/grpc_pb" ;
2022-05-01 13:30:11 -04:00
import { PaymentMethod , PaymentAccount , AvailabilityResult , Attachment , DisputeResult , Dispute , ChatMessage , MoneroNodeSettings } from "./protobuf/pb_pb" ;
2021-09-12 09:39:21 -04:00
/ * *
2022-05-10 09:55:41 -04:00
* Haveno daemon client .
2021-09-12 09:39:21 -04:00
* /
2022-05-04 18:14:28 -04:00
export default class HavenoClient {
2022-05-01 13:30:11 -04:00
2022-01-15 15:43:10 -05:00
// grpc clients
2022-05-01 13:30:11 -04:00
_appName : string | undefined ;
2021-09-14 08:27:45 -04:00
_getVersionClient : GetVersionClient ;
2022-01-08 05:21:32 -08:00
_disputeAgentsClient : DisputeAgentsClient ;
2022-03-07 09:57:00 -08:00
_disputesClient : DisputesClient ;
2022-01-09 17:02:43 +01:00
_notificationsClient : NotificationsClient ;
2022-05-04 18:14:28 -04:00
_notificationStream : grpcWeb.ClientReadableStream < NotificationMessage > | undefined ;
2022-01-24 19:37:18 +01:00
_moneroConnectionsClient : MoneroConnectionsClient ;
2022-04-04 12:29:35 -07:00
_moneroNodeClient : MoneroNodeClient ;
2021-09-14 08:27:45 -04:00
_walletsClient : WalletsClient ;
2022-01-24 19:37:18 +01:00
_priceClient : PriceClient ;
2021-09-14 08:27:45 -04:00
_paymentAccountsClient : PaymentAccountsClient ;
2021-09-19 14:00:22 -04:00
_offersClient : OffersClient ;
_tradesClient : TradesClient ;
2022-02-09 01:41:00 -08:00
_accountClient : AccountClient ;
_shutdownServerClient : ShutdownServerClient ;
2022-05-01 13:30:11 -04:00
2022-02-09 01:41:00 -08:00
// state variables
2022-01-15 15:43:10 -05:00
_url : string ;
_password : string ;
_process : any ;
2022-02-09 01:41:00 -08:00
_processLogging = false ;
2022-05-01 13:30:11 -04:00
_walletRpcPort : number | undefined ;
2022-05-04 18:14:28 -04:00
_notificationListeners : ( ( _notification : NotificationMessage ) = > void ) [ ] = [ ] ;
2022-02-09 01:41:00 -08:00
_registerNotificationListenerCalled = false ;
_keepAliveLooper : any ;
2022-05-01 13:30:11 -04:00
_keepAlivePeriodMs = 60000 ;
2022-05-11 07:53:42 -04:00
_paymentMethods : PaymentMethod [ ] | undefined ; // cached for performance
2022-05-01 13:30:11 -04:00
2022-02-09 01:41:00 -08:00
// constants
2022-04-05 15:18:36 -04:00
static readonly _fullyInitializedMessage = "Application fully initialized" ;
static readonly _loginRequiredMessage = "Interactive login required" ;
2022-05-01 13:30:11 -04:00
2021-09-12 09:39:21 -04:00
/ * *
* Construct a client connected to a Haveno daemon .
2022-05-01 13:30:11 -04:00
*
2021-09-12 09:39:21 -04:00
* @param { string } url - Haveno daemon url
2021-12-08 06:22:36 -05:00
* @param { string } password - Haveno daemon password
2021-09-12 09:39:21 -04:00
* /
2021-09-14 08:27:45 -04:00
constructor ( url : string , password : string ) {
2021-12-08 06:22:36 -05:00
if ( ! url ) throw new Error ( "Must provide URL of Haveno daemon" ) ;
if ( ! password ) throw new Error ( "Must provide password of Haveno daemon" ) ;
2022-04-05 15:18:36 -04:00
HavenoUtils . log ( 2 , "Creating Haveno client connected to " + url ) ;
2021-09-12 09:39:21 -04:00
this . _url = url ;
this . _password = password ;
2021-09-14 08:27:45 -04:00
this . _getVersionClient = new GetVersionClient ( this . _url ) ;
2022-02-09 01:41:00 -08:00
this . _accountClient = new AccountClient ( this . _url ) ;
2022-04-04 12:29:35 -07:00
this . _moneroConnectionsClient = new MoneroConnectionsClient ( this . _url ) ;
this . _moneroNodeClient = new MoneroNodeClient ( this . _url ) ;
2022-02-09 01:41:00 -08:00
this . _disputeAgentsClient = new DisputeAgentsClient ( this . _url ) ;
2022-03-07 09:57:00 -08:00
this . _disputesClient = new DisputesClient ( this . _url ) ;
2021-09-14 08:27:45 -04:00
this . _walletsClient = new WalletsClient ( this . _url ) ;
2022-01-24 19:37:18 +01:00
this . _priceClient = new PriceClient ( this . _url ) ;
2021-09-14 08:27:45 -04:00
this . _paymentAccountsClient = new PaymentAccountsClient ( this . _url ) ;
2021-09-19 14:00:22 -04:00
this . _offersClient = new OffersClient ( this . _url ) ;
this . _tradesClient = new TradesClient ( this . _url ) ;
2022-01-09 17:02:43 +01:00
this . _notificationsClient = new NotificationsClient ( this . _url ) ;
2022-02-09 01:41:00 -08:00
this . _shutdownServerClient = new ShutdownServerClient ( this . _url ) ;
2021-09-12 09:39:21 -04:00
}
2022-05-01 13:30:11 -04:00
2021-12-08 06:22:36 -05:00
/ * *
* Start a new Haveno process .
2022-05-01 13:30:11 -04:00
*
2021-12-08 06:22:36 -05:00
* @param { string } havenoPath - path to Haveno binaries
* @param { string [ ] } cmd - command to start the process
* @param { string } url - Haveno daemon url ( must proxy to api port )
2021-12-14 13:04:02 -05:00
* @param { boolean } enableLogging - specifies if logging is enabled or disabled at log level 3
2022-04-05 15:18:36 -04:00
* @return { haveno } a client connected to the newly started Haveno process
2021-12-08 06:22:36 -05:00
* /
2022-04-07 16:35:48 -04:00
static async startProcess ( havenoPath : string , cmd : string [ ] , url : string , enableLogging : boolean ) : Promise < HavenoClient > {
2022-05-01 13:30:11 -04:00
2021-12-08 06:22:36 -05:00
// return promise which resolves after starting havenod
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
2021-12-14 13:04:02 -05:00
HavenoUtils . log ( 2 , "Starting Haveno process: " + cmd + " on proxy url: " + url ) ;
2022-05-01 13:30:11 -04:00
2021-12-14 13:04:02 -05:00
// state variables
let output = "" ;
2022-02-09 01:41:00 -08:00
let isStarted = false ;
2022-05-01 13:30:11 -04:00
let daemon : HavenoClient | undefined = undefined ;
2021-12-14 13:04:02 -05:00
// start process
2022-05-01 13:30:11 -04:00
const childProcess = require ( 'child_process' ) . spawn ( cmd [ 0 ] , cmd . slice ( 1 ) , { cwd : havenoPath } ) ;
2021-12-14 13:04:02 -05:00
childProcess . stdout . setEncoding ( 'utf8' ) ;
childProcess . stderr . setEncoding ( 'utf8' ) ;
2021-12-08 06:22:36 -05:00
// handle stdout
2021-12-14 13:04:02 -05:00
childProcess . stdout . on ( 'data' , async function ( data : any ) {
2022-05-01 13:30:11 -04:00
const line = data . toString ( ) ;
2022-02-09 01:41:00 -08:00
if ( loggingEnabled ( ) ) process . stdout . write ( line ) ;
2021-12-08 06:22:36 -05:00
output += line + '\n' ; // capture output in case of error
2022-02-09 01:41:00 -08:00
// initialize daemon on success or login required message
2022-04-07 16:35:48 -04:00
if ( ! daemon && ( line . indexOf ( HavenoClient . _fullyInitializedMessage ) >= 0 || line . indexOf ( HavenoClient . _loginRequiredMessage ) >= 0 ) ) {
2021-12-08 06:22:36 -05:00
// get api password
2022-05-01 13:30:11 -04:00
const passwordIdx = cmd . indexOf ( "--apiPassword" ) ;
2021-12-08 06:22:36 -05:00
if ( passwordIdx < 0 ) {
reject ( "Must provide API password to start Haveno daemon" ) ;
return ;
}
2022-05-01 13:30:11 -04:00
const password = cmd [ passwordIdx + 1 ] ;
2021-12-14 13:04:02 -05:00
2021-12-08 06:22:36 -05:00
// create client connected to internal process
2022-04-07 16:35:48 -04:00
daemon = new HavenoClient ( url , password ) ;
2021-12-14 13:04:02 -05:00
daemon . _process = childProcess ;
daemon . _processLogging = enableLogging ;
2022-01-24 19:37:18 +01:00
daemon . _appName = cmd [ cmd . indexOf ( "--appName" ) + 1 ] ;
2021-12-14 13:04:02 -05:00
// get wallet rpc port
2022-05-01 13:30:11 -04:00
const walletRpcPortIdx = cmd . indexOf ( "--walletRpcBindPort" ) ;
2021-12-14 13:04:02 -05:00
if ( walletRpcPortIdx >= 0 ) daemon . _walletRpcPort = parseInt ( cmd [ walletRpcPortIdx + 1 ] ) ;
2021-12-08 06:22:36 -05:00
// resolve promise with client connected to internal process
2022-02-09 01:41:00 -08:00
isStarted = true ;
2021-12-08 06:22:36 -05:00
resolve ( daemon ) ;
}
2021-12-16 20:10:40 -05:00
// read error message
if ( line . indexOf ( "[HavenoDaemonMain] ERROR" ) >= 0 ) {
2022-02-09 01:41:00 -08:00
if ( ! isStarted ) await rejectStartup ( new Error ( line ) ) ;
2021-12-16 20:10:40 -05:00
}
2021-12-08 06:22:36 -05:00
} ) ;
// handle stderr
2021-12-14 13:04:02 -05:00
childProcess . stderr . on ( 'data' , function ( data : any ) {
2022-02-09 01:41:00 -08:00
if ( loggingEnabled ( ) ) process . stderr . write ( data ) ;
2021-12-08 06:22:36 -05:00
} ) ;
// handle exit
2021-12-16 20:10:40 -05:00
childProcess . on ( "exit" , async function ( code : any ) {
2022-02-09 01:41:00 -08:00
if ( ! isStarted ) await rejectStartup ( new Error ( "Haveno process terminated with exit code " + code + ( output ? ":\n\n" + output : "" ) ) ) ;
2021-12-08 06:22:36 -05:00
} ) ;
// handle error
2021-12-16 20:10:40 -05:00
childProcess . on ( "error" , async function ( err : any ) {
2021-12-08 06:22:36 -05:00
if ( err . message . indexOf ( "ENOENT" ) >= 0 ) reject ( new Error ( "haveno-daemon does not exist at path '" + cmd [ 0 ] + "'" ) ) ;
2022-02-09 01:41:00 -08:00
if ( ! isStarted ) await rejectStartup ( err ) ;
2021-12-08 06:22:36 -05:00
} ) ;
// handle uncaught exception
2021-12-16 20:10:40 -05:00
childProcess . on ( "uncaughtException" , async function ( err : any , origin : any ) {
2021-12-08 06:22:36 -05:00
console . error ( "Uncaught exception in Haveno process: " + err . message ) ;
console . error ( origin ) ;
2022-02-09 01:41:00 -08:00
await rejectStartup ( err ) ;
2021-12-08 06:22:36 -05:00
} ) ;
2021-12-14 13:04:02 -05:00
2022-02-09 01:41:00 -08:00
async function rejectStartup ( err : any ) {
2021-12-16 20:10:40 -05:00
await HavenoUtils . kill ( childProcess ) ;
reject ( err ) ;
}
2021-12-14 13:04:02 -05:00
function loggingEnabled ( ) : boolean {
return ( daemon && daemon . _processLogging ) || ( ! daemon && enableLogging ) ;
}
2021-12-08 06:22:36 -05:00
} ) ;
}
2021-12-16 20:10:40 -05:00
/ * *
* Return the process running the haveno daemon .
*
* @return the process running the haveno daemon
* /
getProcess() {
return this . _process ;
2021-12-08 06:22:36 -05:00
}
2021-12-14 13:04:02 -05:00
/ * *
* Enable or disable process logging .
*
* @param { boolean } enabled - specifies if logging is enabled or disabled
* /
setProcessLogging ( enabled : boolean ) {
2022-04-05 15:18:36 -04:00
if ( this . _process === undefined ) throw new Error ( "haveno instance not created from new process" ) ;
2021-12-14 13:04:02 -05:00
this . _processLogging = enabled ;
}
2021-12-08 06:22:36 -05:00
/ * *
* Get the URL of the Haveno daemon .
*
* @return { string } the URL of the Haveno daemon
* /
getUrl ( ) : string {
return this . _url ;
}
2021-12-14 13:04:02 -05:00
/ * *
* Get the port of the primary wallet rpc instance if known .
*
* @return { number | undefined } the port of the primary wallet rpc instance if known
* /
getWalletRpcPort ( ) : number | undefined {
return this . _walletRpcPort ;
}
2022-01-24 19:37:18 +01:00
/ * *
* Get the name of the Haveno application folder .
* /
getAppName ( ) : string | undefined {
return this . _appName ;
}
2022-02-11 17:13:56 -06:00
2021-09-12 09:39:21 -04:00
/ * *
* Get the Haveno version .
*
2022-02-11 17:13:56 -06:00
* @return { string } the Haveno daemon version
2021-09-12 09:39:21 -04:00
* /
async getVersion ( ) : Promise < string > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _getVersionClient . getVersion ( new GetVersionRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetVersionReply ) {
2021-09-12 09:39:21 -04:00
if ( err ) reject ( err ) ;
else resolve ( response . getVersion ( ) ) ;
} ) ;
} ) ;
}
2022-01-08 05:21:32 -08:00
/ * *
2022-02-09 01:41:00 -08:00
* Indicates if connected and authenticated with the Haveno daemon .
2022-01-08 05:21:32 -08:00
*
2022-02-09 01:41:00 -08:00
* @return { boolean } true if connected with the Haveno daemon , false otherwise
2022-01-08 05:21:32 -08:00
* /
2022-02-09 01:41:00 -08:00
async isConnectedToDaemon ( ) : Promise < boolean > {
try {
await this . getVersion ( ) ;
return true ;
} catch ( err ) {
return false ;
}
}
/ * *
* Indicates if the Haveno account is created .
*
* @return { boolean } true if the account is created , false otherwise
* /
async accountExists ( ) : Promise < boolean > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _accountClient . accountExists ( new AccountExistsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : AccountExistsReply ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getAccountExists ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Indicates if the Haveno account is open and authenticated with the correct password .
*
* @return { boolean } true if the account is open and authenticated , false otherwise
* /
async isAccountOpen ( ) : Promise < boolean > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _accountClient . isAccountOpen ( new IsAccountOpenRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : IsAccountOpenReply ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getIsAccountOpen ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Create and open a new Haveno account .
*
* @param { string } password - the password to encrypt the account
* /
async createAccount ( password : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
await new Promise < void > ( ( resolve , reject ) = > {
this . _accountClient . createAccount ( new CreateAccountRequest ( ) . setPassword ( password ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-08 05:21:32 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
2022-02-09 01:41:00 -08:00
return this . _awaitAppInitialized ( ) ; // TODO: grpc should not return before setup is complete
2022-01-08 05:21:32 -08:00
}
2022-01-09 17:02:43 +01:00
/ * *
2022-02-09 01:41:00 -08:00
* Open existing Haveno account .
*
* @param { string } password - the account password
* /
async openAccount ( password : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
await new Promise < void > ( ( resolve , reject ) = > {
this . _accountClient . openAccount ( new OpenAccountRequest ( ) . setPassword ( password ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
return this . _awaitAppInitialized ( ) ; // TODO: grpc should not return before setup is complete
}
/ * *
* Change the Haveno account password .
*
* @param { string } password - the new account password
* /
async changePassword ( password : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise < void > ( ( resolve , reject ) = > {
this . _accountClient . changePassword ( new ChangePasswordRequest ( ) . setPassword ( password ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Close the currently open account .
* /
async closeAccount ( ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise < void > ( ( resolve , reject ) = > {
this . _accountClient . closeAccount ( new CloseAccountRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Permanently delete the Haveno account and shutdown the server . // TODO: possible to not shutdown server?
* /
async deleteAccount ( ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise < void > ( ( resolve , reject ) = > {
this . _accountClient . deleteAccount ( new DeleteAccountRequest ( ) , { password : this._password } , async function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else setTimeout ( resolve , 5000 ) ;
} ) ;
} ) ;
}
/ * *
* Backup the account to the given stream . TODO : stream type ?
* /
async backupAccount ( stream : any ) : Promise < number > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
2022-02-09 01:41:00 -08:00
let total = 0 ;
2022-05-01 13:30:11 -04:00
const response = this . _accountClient . backupAccount ( new BackupAccountRequest ( ) , { password : this._password } ) ;
response . on ( 'data' , ( chunk : any ) = > {
const bytes = ( chunk as BackupAccountReply ) . getZipBytes ( ) ; // TODO: right api?
2022-02-09 01:41:00 -08:00
total += bytes . length ;
stream . write ( bytes ) ;
} ) ;
2022-05-01 13:30:11 -04:00
response . on ( 'error' , function ( err : any ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
} ) ;
response . on ( 'end' , function ( ) {
resolve ( total ) ;
} ) ;
} ) ;
}
/ * *
* Restore the account from zip bytes .
*
* Sends chunked requests if size over max grpc envelope size ( 41943404 bytes ) .
2022-01-09 17:02:43 +01:00
*
2022-02-09 01:41:00 -08:00
* @param { Uint8Array } zipBytes - the bytes of the zipped account to restore
* /
async restoreAccount ( zipBytes : Uint8Array ) : Promise < void > {
if ( zipBytes . length === 0 ) throw new Error ( "Zip bytes must not be empty" )
2022-05-01 13:30:11 -04:00
const totalLength = zipBytes . byteLength ;
2022-02-09 01:41:00 -08:00
let offset = 0 ;
let chunkSize = 4000000 ; // the max frame size is 4194304 but leave room for http headers
let hasMore = true ;
2022-05-01 13:30:11 -04:00
// eslint-disable-next-line no-constant-condition
2022-02-09 01:41:00 -08:00
while ( true ) {
if ( zipBytes . byteLength <= offset + 1 ) return ;
if ( zipBytes . byteLength <= offset + chunkSize ) {
chunkSize = zipBytes . byteLength - offset - 1 ;
hasMore = false ;
}
2022-05-01 13:30:11 -04:00
const subArray = zipBytes . subarray ( offset , offset + chunkSize ) ;
2022-02-09 01:41:00 -08:00
await this . _restoreAccountChunk ( subArray , offset , totalLength , hasMore ) ;
offset += chunkSize ;
}
}
/ * *
* Add a listener to receive notifications from the Haveno daemon .
*
2022-04-05 15:18:36 -04:00
* @param { ( notification : NotificationMessage ) = > void } listener - the notification listener to add
2022-01-09 17:02:43 +01:00
* /
2022-05-04 18:14:28 -04:00
async addNotificationListener ( listener : ( _notification : NotificationMessage ) = > void ) : Promise < void > {
2022-01-09 17:02:43 +01:00
this . _notificationListeners . push ( listener ) ;
2022-05-04 18:14:28 -04:00
return this . _updateNotificationListenerRegistration ( ) ;
2022-02-09 01:41:00 -08:00
}
/ * *
* Remove a notification listener .
*
2022-04-05 15:18:36 -04:00
* @param { ( notification : NotificationMessage ) = > void } listener - the notification listener to remove
2022-02-09 01:41:00 -08:00
* /
2022-05-04 18:14:28 -04:00
async removeNotificationListener ( listener : ( _notification : NotificationMessage ) = > void ) : Promise < void > {
2022-05-01 13:30:11 -04:00
const idx = this . _notificationListeners . indexOf ( listener ) ;
2022-02-09 01:41:00 -08:00
if ( idx > - 1 ) this . _notificationListeners . splice ( idx , 1 ) ;
else throw new Error ( "Notification listener is not registered" ) ;
2022-05-04 18:14:28 -04:00
return this . _updateNotificationListenerRegistration ( ) ;
2022-01-09 17:02:43 +01:00
}
2022-02-09 01:41:00 -08:00
/ * *
* Indicates if connected to the Monero network based on last connection check .
*
* @return { boolean } true if connected to the Monero network , false otherwise
* /
async isConnectedToMonero ( ) : Promise < boolean > {
2022-05-01 13:30:11 -04:00
const connection = await this . getMoneroConnection ( ) ;
2022-02-09 01:41:00 -08:00
return connection !== undefined &&
connection . getOnlineStatus ( ) ! === UrlConnection . OnlineStatus . ONLINE &&
connection . getAuthenticationStatus ( ) ! !== UrlConnection . AuthenticationStatus . NOT_AUTHENTICATED ;
}
2021-10-22 13:51:57 -04:00
/ * *
2022-01-24 19:37:18 +01:00
* Add a Monero daemon connection .
*
2022-02-09 01:41:00 -08:00
* @param { string | UrlConnection } connection - daemon url or connection to add
2021-10-22 13:51:57 -04:00
* /
2022-02-09 01:41:00 -08:00
async addMoneroConnection ( connection : string | UrlConnection ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . addConnection ( new AddConnectionRequest ( ) . setConnection ( typeof connection === "string" ? new UrlConnection ( ) . setUrl ( connection ) : connection ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2021-10-22 13:51:57 -04:00
if ( err ) reject ( err ) ;
2022-01-24 19:37:18 +01:00
else resolve ( ) ;
2021-10-22 13:51:57 -04:00
} ) ;
} ) ;
}
2021-11-19 23:29:44 +01:00
/ * *
2022-01-24 19:37:18 +01:00
* Remove a Monero daemon connection .
*
2022-02-09 01:41:00 -08:00
* @param { string } url - url of the daemon connection to remove
2021-11-19 23:29:44 +01:00
* /
2022-02-09 01:41:00 -08:00
async removeMoneroConnection ( url : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . removeConnection ( new RemoveConnectionRequest ( ) . setUrl ( url ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2021-11-19 23:29:44 +01:00
if ( err ) reject ( err ) ;
2022-01-24 19:37:18 +01:00
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Get the current Monero daemon connection .
*
2022-02-09 01:41:00 -08:00
* @return { UrlConnection | undefined } the current daemon connection , undefined if no current connection
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async getMoneroConnection ( ) : Promise < UrlConnection | undefined > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . getConnection ( new GetConnectionRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetConnectionReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getConnection ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Get all Monero daemon connections .
*
2022-02-09 01:41:00 -08:00
* @return { UrlConnection [ ] } all daemon connections
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async getMoneroConnections ( ) : Promise < UrlConnection [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . getConnections ( new GetConnectionsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetConnectionsReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getConnectionsList ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Set the current Monero daemon connection .
2022-02-09 01:41:00 -08:00
*
2022-01-24 19:37:18 +01:00
* Add the connection if not previously seen .
* If the connection is provided as string , connect to the URI with any previously set credentials and priority .
2022-02-09 01:41:00 -08:00
* If the connection is provided as UrlConnection , overwrite any previously set credentials and priority .
2022-01-24 19:37:18 +01:00
* If undefined connection provided , disconnect the client .
*
2022-02-09 01:41:00 -08:00
* @param { string | UrlConnection } connection - connection to set as current
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async setMoneroConnection ( connection? : string | UrlConnection ) : Promise < void > {
2022-05-01 13:30:11 -04:00
const request = new SetConnectionRequest ( ) ;
2022-02-09 01:41:00 -08:00
if ( typeof connection === "string" ) request . setUrl ( connection ) ;
2022-01-24 19:37:18 +01:00
else request . setConnection ( connection ) ;
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . setConnection ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Check the current Monero daemon connection .
*
* If disconnected and auto switch enabled , switch to the best available connection and return its status .
*
2022-02-09 01:41:00 -08:00
* @return { UrlConnection | undefined } the current daemon connection status , undefined if no current connection
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async checkMoneroConnection ( ) : Promise < UrlConnection | undefined > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . checkConnection ( new CheckConnectionRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : CheckConnectionReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getConnection ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Check all Monero daemon connections .
*
2022-02-09 01:41:00 -08:00
* @return { UrlConnection [ ] } status of all managed connections .
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async checkMoneroConnections ( ) : Promise < UrlConnection [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . checkConnections ( new CheckConnectionsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : CheckConnectionsReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getConnectionsList ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Check the connection and start checking the connection periodically .
*
* @param { number } refreshPeriod - time between checks in milliseconds ( default 15000 ms or 15 seconds )
* /
async startCheckingConnection ( refreshPeriod : number ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . startCheckingConnections ( new StartCheckingConnectionsRequest ( ) . setRefreshPeriod ( refreshPeriod ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Stop checking the connection status periodically .
* /
async stopCheckingConnection ( ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . stopCheckingConnections ( new StopCheckingConnectionsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Get the best available connection in order of priority then response time .
*
2022-02-09 01:41:00 -08:00
* @return { UrlConnection | undefined } the best available connection in order of priority then response time , undefined if no connections available
2022-01-24 19:37:18 +01:00
* /
2022-02-09 01:41:00 -08:00
async getBestAvailableConnection ( ) : Promise < UrlConnection | undefined > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . getBestAvailableConnection ( new GetBestAvailableConnectionRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetBestAvailableConnectionReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getConnection ( ) ) ;
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
2022-01-24 19:37:18 +01:00
/ * *
* Automatically switch to the best available connection if current connection is disconnected after being checked .
*
* @param { boolean } autoSwitch - whether auto switch is enabled or disabled
* /
async setAutoSwitch ( autoSwitch : boolean ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroConnectionsClient . setAutoSwitch ( new SetAutoSwitchRequest ( ) . setAutoSwitch ( autoSwitch ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
2021-11-19 23:29:44 +01:00
} ) ;
} ) ;
}
2022-04-04 12:29:35 -07:00
/ * *
* Returns whether daemon is running a local monero node .
* /
async isMoneroNodeRunning ( ) : Promise < boolean > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroNodeClient . isMoneroNodeRunning ( new IsMoneroNodeRunningRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : IsMoneroNodeRunningReply ) {
2022-04-04 12:29:35 -07:00
if ( err ) reject ( err ) ;
else resolve ( response . getIsRunning ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Gets the current local monero node settings .
* /
async getMoneroNodeSettings ( ) : Promise < MoneroNodeSettings | undefined > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new GetMoneroNodeSettingsRequest ( ) ;
this . _moneroNodeClient . getMoneroNodeSettings ( request , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetMoneroNodeSettingsReply ) {
2022-04-04 12:29:35 -07:00
if ( err ) reject ( err ) ;
else resolve ( response . getSettings ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Starts the local monero node .
*
* @param { MoneroNodeSettings } settings - the settings to start the local node with
* /
async startMoneroNode ( settings : MoneroNodeSettings ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new StartMoneroNodeRequest ( ) . setSettings ( settings ) ;
this . _moneroNodeClient . startMoneroNode ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-04-04 12:29:35 -07:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Stops the local monero node .
* /
async stopMoneroNode ( ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _moneroNodeClient . stopMoneroNode ( new StopMoneroNodeRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-04-04 12:29:35 -07:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-02-09 01:41:00 -08:00
/ * *
* Register as a dispute agent .
*
* @param { string } disputeAgentType - type of dispute agent to register , e . g . mediator , refundagent
* @param { string } registrationKey - registration key
* /
async registerDisputeAgent ( disputeAgentType : string , registrationKey : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
const request = new RegisterDisputeAgentRequest ( )
2022-02-09 01:41:00 -08:00
. setDisputeAgentType ( disputeAgentType )
. setRegistrationKey ( registrationKey ) ;
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _disputeAgentsClient . registerDisputeAgent ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2021-09-12 09:39:21 -04:00
/ * *
* Get the user ' s balances .
*
2021-09-14 08:27:45 -04:00
* @return { XmrBalanceInfo } the user ' s balances
2021-09-12 09:39:21 -04:00
* /
2021-09-14 08:27:45 -04:00
async getBalances ( ) : Promise < XmrBalanceInfo > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . getBalances ( new GetBalancesRequest ( ) . setCurrencyCode ( "XMR" ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetBalancesReply ) {
2021-09-12 09:39:21 -04:00
if ( err ) reject ( err ) ;
2021-09-15 08:06:50 -04:00
else resolve ( response . getBalances ( ) ! . getXmr ( ) ! ) ;
2021-09-12 09:39:21 -04:00
} ) ;
} ) ;
}
2022-05-10 14:53:10 -04:00
/ * *
* Get the mnemonic seed phrase of the Monero wallet .
*
* @return { string } the mnemonic seed phrase of the Monero wallet
* /
async getXmrSeed ( ) : Promise < string > {
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . getXmrSeed ( new GetXmrSeedRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetXmrSeedReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getSeed ( ) ) ;
} ) ;
} ) ;
}
2021-09-12 09:39:21 -04:00
/ * *
2021-12-30 22:03:00 +02:00
* Get a new subaddress in the Monero wallet to receive deposits .
2021-09-12 09:39:21 -04:00
*
2021-09-19 14:00:22 -04:00
* @return { string } the deposit address ( a subaddress in the Haveno wallet )
2021-09-12 09:39:21 -04:00
* /
2022-04-05 14:34:19 -04:00
async getNewDepositAddress ( ) : Promise < string > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . getNewDepositAddress ( new GetNewDepositAddressRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetNewDepositAddressReply ) {
2021-09-12 09:39:21 -04:00
if ( err ) reject ( err ) ;
2021-09-19 14:00:22 -04:00
else resolve ( response . getSubaddress ( ) ) ;
2021-09-14 08:27:45 -04:00
} ) ;
} ) ;
2021-09-12 09:39:21 -04:00
}
2021-12-30 22:03:00 +02:00
/ * *
* Get all transactions in the Monero wallet .
*
* @return { XmrTx [ ] } the transactions
* /
async getXmrTxs ( ) : Promise < XmrTx [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . getXmrTxs ( new GetXmrTxsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetXmrTxsReply ) {
2021-12-30 22:03:00 +02:00
if ( err ) reject ( err ) ;
else resolve ( response . getTxsList ( ) ) ;
} ) ;
} ) ;
}
2021-09-14 08:27:45 -04:00
2021-12-30 22:03:00 +02:00
/ * *
* Get a transaction by hash in the Monero wallet .
*
* @param { String } txHash - hash of the transaction to get
* @return { XmrTx } the transaction with the hash
* /
async getXmrTx ( txHash : string ) : Promise < XmrTx > {
2022-05-01 13:30:11 -04:00
const txs = await this . getXmrTxs ( ) ; // TODO (woodser): implement getXmrTx(hash) grpc call
for ( const tx of txs ) {
2021-12-30 22:03:00 +02:00
if ( tx . getHash ( ) === txHash ) return tx ;
}
throw new Error ( "No transaction with hash " + txHash ) ;
}
2022-02-11 17:13:56 -06:00
2021-12-30 22:03:00 +02:00
/ * *
* Create but do not relay a transaction to send funds from the Monero wallet .
*
* @return { XmrTx } the created transaction
* /
async createXmrTx ( destinations : XmrDestination [ ] ) : Promise < XmrTx > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . createXmrTx ( new CreateXmrTxRequest ( ) . setDestinationsList ( destinations ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : CreateXmrTxReply ) {
2021-12-30 22:03:00 +02:00
if ( err ) reject ( err ) ;
2022-01-26 18:44:47 -08:00
else resolve ( response . getTx ( ) ! ) ;
2021-12-30 22:03:00 +02:00
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
2021-12-30 22:03:00 +02:00
/ * *
* Relay a previously created transaction to send funds from the Monero wallet .
*
* @return { string } the hash of the relayed transaction
* /
async relayXmrTx ( metadata : string ) : Promise < string > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _walletsClient . relayXmrTx ( new RelayXmrTxRequest ( ) . setMetadata ( metadata ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : RelayXmrTxReply ) {
2021-12-30 22:03:00 +02:00
if ( err ) reject ( err ) ;
else resolve ( response . getHash ( ) ) ;
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
2022-05-11 07:53:42 -04:00
/ * *
* Get all supported assets codes .
*
* TODO : replace this with getSupportedAssetCodes ( ) : Promise < TradeCurrency [ ] > )
*
* @return { Promise < string [ ] > } all supported trade assets
* /
async getSupportedAssetCodes ( ) : Promise < string [ ] > {
const assetCodes : string [ ] = [ ] ;
for ( const price of await this . getPrices ( ) ) assetCodes . push ( price . getCurrencyCode ( ) ) ;
return assetCodes ;
}
2022-01-24 19:37:18 +01:00
/ * *
* Get the current market price per 1 XMR in the given currency .
*
2022-05-11 07:53:42 -04:00
* @param { string } assetCode - asset code to get the price of
* @return { number } the price of the asset per 1 XMR
2022-01-24 19:37:18 +01:00
* /
2022-05-11 07:53:42 -04:00
async getPrice ( assetCode : string ) : Promise < number > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
2022-05-11 07:53:42 -04:00
this . _priceClient . getMarketPrice ( new MarketPriceRequest ( ) . setCurrencyCode ( assetCode ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : MarketPriceReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getPrice ( ) ) ;
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
2022-01-24 19:37:18 +01:00
/ * *
2022-05-11 07:53:42 -04:00
* Get the current market prices of all a .
2022-01-24 19:37:18 +01:00
*
2022-05-11 07:53:42 -04:00
* @return { MarketPrice [ ] } prices of the assets per 1 XMR
2022-01-24 19:37:18 +01:00
* /
async getPrices ( ) : Promise < MarketPriceInfo [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _priceClient . getMarketPrices ( new MarketPricesRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : MarketPricesReply ) {
2022-01-24 19:37:18 +01:00
if ( err ) reject ( err ) ;
else resolve ( response . getMarketPriceList ( ) ) ;
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
/ * *
* Get the market depth of a currency .
2022-02-16 10:09:59 -05:00
*
* @param { string } assetCode - asset to get the market depth of
2022-02-11 17:13:56 -06:00
* @return { MarketDepthInfo } market depth of the given currency
* /
2022-05-01 13:30:11 -04:00
async getMarketDepth ( assetCode : string ) : Promise < MarketDepthInfo > {
return new Promise ( ( resolve , reject ) = > {
this . _priceClient . getMarketDepth ( new MarketDepthRequest ( ) . setCurrencyCode ( assetCode ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : MarketDepthReply ) {
2022-02-11 17:13:56 -06:00
if ( err ) reject ( err ) ;
2022-03-07 09:57:00 -08:00
else resolve ( response . getMarketDepth ( ) ! ) ;
2022-02-11 17:13:56 -06:00
} ) ;
} ) ;
}
2022-02-16 10:09:59 -05:00
/ * *
* Get payment methods .
*
2022-05-11 07:53:42 -04:00
* @param { string } assetCode - get payment methods supporting this asset code ( optional )
2022-02-16 10:09:59 -05:00
* @return { PaymentMethod [ ] } the payment methods
* /
2022-05-11 07:53:42 -04:00
async getPaymentMethods ( assetCode? : string ) : Promise < PaymentMethod [ ] > {
if ( ! this . _paymentMethods ) {
this . _paymentMethods = await new Promise ( ( resolve , reject ) = > {
this . _paymentAccountsClient . getPaymentMethods ( new GetPaymentMethodsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetPaymentMethodsReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getPaymentMethodsList ( ) ) ;
} ) ;
2022-02-16 10:09:59 -05:00
} ) ;
2022-05-11 07:53:42 -04:00
}
if ( ! assetCode ) return this . _paymentMethods ! ;
const assetPaymentMethods : PaymentMethod [ ] = [ ] ;
for ( const paymentMethod of this . _paymentMethods ! ) {
if ( paymentMethod . getSupportedAssetCodesList ( ) . includes ( assetCode ) ) assetPaymentMethods . push ( paymentMethod ) ;
}
return assetPaymentMethods ;
2022-02-16 10:09:59 -05:00
}
2021-09-14 08:27:45 -04:00
/ * *
* Get payment accounts .
*
* @return { PaymentAccount [ ] } the payment accounts
* /
async getPaymentAccounts ( ) : Promise < PaymentAccount [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _paymentAccountsClient . getPaymentAccounts ( new GetPaymentAccountsRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetPaymentAccountsReply ) {
2021-09-14 08:27:45 -04:00
if ( err ) reject ( err ) ;
2021-09-15 08:06:50 -04:00
else resolve ( response . getPaymentAccountsList ( ) ) ;
2021-09-14 08:27:45 -04:00
} ) ;
} ) ;
2021-09-12 09:39:21 -04:00
}
2021-09-14 08:30:22 -04:00
2022-04-10 17:00:15 -04:00
/ * *
* Get a payment account by id .
*
* @param { string } paymentAccountId - the payment account id to get
* @return { PaymentAccount } the payment account
* /
async getPaymentAccount ( paymentAccountId : string ) : Promise < PaymentAccount > {
2022-05-01 13:30:11 -04:00
// TODO (woodser): implement this on the backend
const paymentAccounts = await this . getPaymentAccounts ( ) ;
for ( const paymentAccount of paymentAccounts ) {
2022-04-10 17:00:15 -04:00
if ( paymentAccount . getId ( ) === paymentAccountId ) return paymentAccount ;
}
throw new Error ( "No payment account with id " + paymentAccountId ) ;
}
2022-02-16 10:09:59 -05:00
/ * *
* Get a form for the given payment method to complete and create a new payment account .
*
* @return { object } the payment account form as JSON
* /
async getPaymentAccountForm ( paymentMethodId : string ) : Promise < any > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _paymentAccountsClient . getPaymentAccountForm ( new GetPaymentAccountFormRequest ( ) . setPaymentMethodId ( paymentMethodId ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetPaymentAccountFormReply ) {
2022-02-16 10:09:59 -05:00
if ( err ) reject ( err ) ;
else resolve ( JSON . parse ( response . getPaymentAccountFormJson ( ) ) ) ;
} ) ;
} ) ;
}
/ * *
* Create a payment account .
*
* @param { object } paymentAccountForm - the completed form as JSON to create the payment account
* @return { PaymentAccount } the created payment account
* /
async createPaymentAccount ( paymentAccountForm : any ) : Promise < PaymentAccount > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _paymentAccountsClient . createPaymentAccount ( new CreatePaymentAccountRequest ( ) . setPaymentAccountForm ( JSON . stringify ( paymentAccountForm ) ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : CreatePaymentAccountReply ) {
2022-02-16 10:09:59 -05:00
if ( err ) reject ( err ) ;
else resolve ( response . getPaymentAccount ( ) ! ) ;
} ) ;
} ) ;
}
2021-09-14 08:30:22 -04:00
/ * *
* Create a crypto payment account .
*
* @param { string } accountName - description of the account
2022-02-16 10:09:59 -05:00
* @param { string } assetCode - traded asset code
2021-09-14 08:30:22 -04:00
* @param { string } address - payment address of the account
* @return { PaymentAccount } the created payment account
* /
2022-02-16 10:09:59 -05:00
async createCryptoPaymentAccount ( accountName : string , assetCode : string , address : string ) : Promise < PaymentAccount > {
2022-05-01 13:30:11 -04:00
const request = new CreateCryptoCurrencyPaymentAccountRequest ( )
. setAccountName ( accountName )
. setCurrencyCode ( assetCode )
. setAddress ( address )
. setTradeInstant ( false ) ; // not using instant trades
return new Promise ( ( resolve , reject ) = > {
this . _paymentAccountsClient . createCryptoCurrencyPaymentAccount ( request , { password : this._password } , function ( err : grpcWeb.RpcError , response : CreateCryptoCurrencyPaymentAccountReply ) {
2021-09-14 08:30:22 -04:00
if ( err ) reject ( err ) ;
2021-09-15 08:06:50 -04:00
else resolve ( response . getPaymentAccount ( ) ! ) ;
2021-09-14 08:30:22 -04:00
} ) ;
} ) ;
}
2021-09-19 14:00:22 -04:00
/ * *
* Get available offers to buy or sell XMR .
*
2022-02-16 10:09:59 -05:00
* @param { string } assetCode - traded asset code
2022-02-11 17:13:56 -06:00
* @param { string | undefined } direction - "buy" or "sell" ( default all )
* @return { OfferInfo [ ] } the available offers
2021-09-19 14:00:22 -04:00
* /
2022-02-16 10:09:59 -05:00
async getOffers ( assetCode : string , direction? : string ) : Promise < OfferInfo [ ] > {
if ( ! direction ) return ( await this . getOffers ( assetCode , "buy" ) ) . concat ( await this . getOffers ( assetCode , "sell" ) ) ; // TODO: implement in backend
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _offersClient . getOffers ( new GetOffersRequest ( ) . setDirection ( direction ) . setCurrencyCode ( assetCode ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetOffersReply ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
else resolve ( response . getOffersList ( ) ) ;
} ) ;
} ) ;
}
/ * *
2022-02-11 17:13:56 -06:00
* Get the user ' s posted offers to buy or sell XMR .
2021-09-19 14:00:22 -04:00
*
2022-02-16 10:09:59 -05:00
* @param { string } assetCode - traded asset code
* @param { string | undefined } direction - "buy" or "sell" XMR ( default all )
2021-09-19 14:00:22 -04:00
* @return { OfferInfo [ ] } the user ' s created offers
* /
2022-02-16 10:09:59 -05:00
async getMyOffers ( assetCode : string , direction? : string ) : Promise < OfferInfo [ ] > {
if ( ! direction ) return ( await this . getMyOffers ( assetCode , "buy" ) ) . concat ( await this . getMyOffers ( assetCode , "sell" ) ) ; // TODO: implement in backend
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _offersClient . getMyOffers ( new GetOffersRequest ( ) . setDirection ( direction ) . setCurrencyCode ( assetCode ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetOffersReply ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
else resolve ( response . getOffersList ( ) ) ;
} ) ;
} ) ;
}
2021-12-14 13:04:02 -05:00
/ * *
* Get my offer by id .
*
* @param { string } offerId - id of the user ' s created offer
* @return { OfferInfo } the user ' s created offer
* /
async getMyOffer ( offerId : string ) : Promise < OfferInfo > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _offersClient . getMyOffer ( new GetMyOfferRequest ( ) . setId ( offerId ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetMyOfferReply ) {
2022-02-16 10:09:59 -05:00
if ( err ) reject ( err ) ;
else resolve ( response . getOffer ( ) ! ) ;
} ) ;
} ) ;
2021-12-14 13:04:02 -05:00
}
2021-09-14 08:30:22 -04:00
/ * *
* Post an offer .
*
2022-04-13 11:28:41 -04:00
* @param { string } direction - "buy" or "sell" XMR
* @param { bigint } amount - amount of XMR to trade
* @param { string } assetCode - asset code to trade for XMR
2021-09-14 08:30:22 -04:00
* @param { string } paymentAccountId - payment account id
2022-04-13 11:28:41 -04:00
* @param { number } buyerSecurityDeposit - buyer security deposit as % of trade amount
* @param { number } price - trade price ( optional , default to market price )
* @param { number } marketPriceMargin - if using market price , % from market price to accept ( optional , default 0 % )
* @param { bigint } minAmount - minimum amount to trade ( optional , default to fixed amount )
* @param { number } triggerPrice - price to remove offer ( optional )
* @return { OfferInfo } the posted offer
2021-09-14 08:30:22 -04:00
* /
2022-04-13 11:28:41 -04:00
async postOffer ( direction : string ,
amount : bigint ,
assetCode : string ,
paymentAccountId : string ,
buyerSecurityDeposit : number ,
price? : number ,
marketPriceMargin? : number ,
triggerPrice? : number ,
minAmount? : bigint ) : Promise < OfferInfo > {
2022-05-01 13:30:11 -04:00
const request = new CreateOfferRequest ( )
. setDirection ( direction )
. setAmount ( amount . toString ( ) )
. setCurrencyCode ( assetCode )
. setPaymentAccountId ( paymentAccountId )
. setBuyerSecurityDeposit ( buyerSecurityDeposit )
. setPrice ( price ? price . toString ( ) : "1.0" ) // TOOD (woodser): positive price required even if using market price?
. setUseMarketBasedPrice ( price === undefined ) // TODO (woodser): this field is redundant; remove from api
. setMinAmount ( minAmount ? minAmount . toString ( ) : amount . toString ( ) ) ;
2022-04-13 11:28:41 -04:00
if ( marketPriceMargin ) request . setMarketPriceMargin ( marketPriceMargin ) ;
if ( triggerPrice ) request . setTriggerPrice ( triggerPrice . toString ( ) ) ;
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _offersClient . createOffer ( request , { password : this._password } , function ( err : grpcWeb.RpcError , response : CreateOfferReply ) {
2021-09-14 08:30:22 -04:00
if ( err ) reject ( err ) ;
2021-09-15 08:06:50 -04:00
else resolve ( response . getOffer ( ) ! ) ;
2021-09-14 08:30:22 -04:00
} ) ;
} ) ;
}
/ * *
2021-09-17 09:33:58 -04:00
* Remove a posted offer , releasing its reserved funds .
2021-09-14 08:30:22 -04:00
*
2021-09-19 14:00:22 -04:00
* @param { string } offerId - the offer id to cancel
* /
async removeOffer ( offerId : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _offersClient . cancelOffer ( new CancelOfferRequest ( ) . setId ( offerId ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Take an offer .
*
* @param { string } offerId - id of the offer to take
* @param { string } paymentAccountId - id of the payment account
* @return { TradeInfo } the initialized trade
* /
async takeOffer ( offerId : string , paymentAccountId : string ) : Promise < TradeInfo > {
2022-05-01 13:30:11 -04:00
const request = new TakeOfferRequest ( )
. setOfferId ( offerId )
. setPaymentAccountId ( paymentAccountId ) ;
return new Promise ( ( resolve , reject ) = > {
this . _tradesClient . takeOffer ( request , { password : this._password } , function ( err : grpcWeb.RpcError , response : TakeOfferReply ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
2022-04-06 11:28:56 -04:00
else if ( response . getFailureReason ( ) && response . getFailureReason ( ) ! . getAvailabilityResult ( ) !== AvailabilityResult . AVAILABLE ) reject ( new Error ( response . getFailureReason ( ) ! . getDescription ( ) ) ) ; // TODO: api should throw grpcWeb.RpcError
2021-11-11 13:48:31 -05:00
else resolve ( response . getTrade ( ) ! ) ;
2021-09-19 14:00:22 -04:00
} ) ;
} ) ;
}
/ * *
* Get a trade by id .
*
* @param { string } tradeId - the id of the trade and its offer
* @return { TradeInfo } the trade with the given id
* /
async getTrade ( tradeId : string ) : Promise < TradeInfo > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _tradesClient . getTrade ( new GetTradeRequest ( ) . setTradeId ( tradeId ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetTradeReply ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
2021-11-11 13:48:31 -05:00
else resolve ( response . getTrade ( ) ! ) ;
2021-09-19 14:00:22 -04:00
} ) ;
} ) ;
}
2021-12-14 13:04:02 -05:00
/ * *
* Get all trades .
*
* @return { TradeInfo [ ] } all user trades
* /
async getTrades ( ) : Promise < TradeInfo [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _tradesClient . getTrades ( new GetTradesRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetTradesReply ) {
2021-12-14 13:04:02 -05:00
if ( err ) reject ( err ) ;
else resolve ( response . getTradesList ( ) ) ;
} ) ;
} ) ;
}
2021-09-19 14:00:22 -04:00
/ * *
* Confirm a payment is started .
*
* @param { string } tradeId - the id of the trade
* /
async confirmPaymentStarted ( tradeId : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _tradesClient . confirmPaymentStarted ( new ConfirmPaymentStartedRequest ( ) . setTradeId ( tradeId ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2021-09-19 14:00:22 -04:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Confirm a payment is received .
*
* @param { string } tradeId - the id of the trade
2021-09-14 08:30:22 -04:00
* /
2021-09-19 14:00:22 -04:00
async confirmPaymentReceived ( tradeId : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _tradesClient . confirmPaymentReceived ( new ConfirmPaymentReceivedRequest ( ) . setTradeId ( tradeId ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-03-07 09:57:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-03-09 04:43:30 -08:00
/ * *
* Get all chat messages for a trade .
*
* @param { string } tradeId - the id of the trade
* /
async getChatMessages ( tradeId : string ) : Promise < ChatMessage [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new GetChatMessagesRequest ( ) . setTradeId ( tradeId ) ;
this . _tradesClient . getChatMessages ( request , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetChatMessagesReply ) {
2022-03-09 04:43:30 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getMessageList ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Send a trade chat message .
*
* @param { string } tradeId - the id of the trade
* @param { string } message - the message
* /
async sendChatMessage ( tradeId : string , message : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new SendChatMessageRequest ( )
2022-03-09 04:43:30 -08:00
. setTradeId ( tradeId )
. setMessage ( message ) ;
2022-05-01 13:30:11 -04:00
this . _tradesClient . sendChatMessage ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-03-09 04:43:30 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-03-07 09:57:00 -08:00
/ * *
* Get a dispute by trade id .
*
* @param { string } tradeId - the id of the trade
* /
async getDispute ( tradeId : string ) : Promise < Dispute > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _disputesClient . getDispute ( new GetDisputeRequest ( ) . setTradeId ( tradeId ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetDisputeReply ) {
2022-03-07 09:57:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getDispute ( ) ! ) ;
} ) ;
} ) ;
}
/ * *
* Get all disputes .
* /
async getDisputes ( ) : Promise < Dispute [ ] > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _disputesClient . getDisputes ( new GetDisputesRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : GetDisputesReply ) {
2022-03-07 09:57:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getDisputesList ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Open a dispute for a trade .
*
* @param { string } tradeId - the id of the trade
* /
async openDispute ( tradeId : string ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _disputesClient . openDispute ( new OpenDisputeRequest ( ) . setTradeId ( tradeId ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-03-07 09:57:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Resolve a dispute . By default , the winner receives the trade amount and the security deposits are returned ,
* but the arbitrator may award a custom amount to the winner .
*
* @param { string } tradeId - the id of the trade
* @param { DisputeResult . Winner } winner - the winner of the dispute
* @param { DisputeResult . Reason } reason - the reason for the dispute
* @param { string } summaryNotes - summary of the dispute
* @param { bigint } customWinnerAmount - custom amount to award the winner ( optional )
* /
async resolveDispute ( tradeId : string , winner : DisputeResult.Winner , reason : DisputeResult.Reason , summaryNotes : string , customWinnerAmount? : bigint ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new ResolveDisputeRequest ( )
. setTradeId ( tradeId )
. setWinner ( winner )
. setReason ( reason )
. setSummaryNotes ( summaryNotes )
. setCustomPayoutAmount ( customWinnerAmount ? customWinnerAmount . toString ( ) : "0" ) ;
this . _disputesClient . resolveDispute ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-03-07 09:57:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Send a dispute chat message .
*
* @param { string } disputeId - the id of the dispute
* @param { string } message - the message
* @param { Attachment [ ] } attachments - attachments
* /
async sendDisputeChatMessage ( disputeId : string , message : string , attachments : Attachment [ ] ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
const request = new SendDisputeChatMessageRequest ( )
. setDisputeId ( disputeId )
. setMessage ( message )
. setAttachmentsList ( attachments ) ;
this . _disputesClient . sendDisputeChatMessage ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2021-09-14 08:30:22 -04:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-01-09 17:02:43 +01:00
2022-05-04 18:14:28 -04:00
/ * *
* Disconnect this client from the server .
* /
async disconnect() {
while ( this . _notificationListeners . length ) await this . removeNotificationListener ( this . _notificationListeners [ 0 ] ) ;
}
2022-02-09 01:41:00 -08:00
/ * *
* Shutdown the Haveno daemon server and stop the process if applicable .
* /
async shutdownServer() {
2022-05-04 18:14:28 -04:00
await this . disconnect ( ) ;
2022-05-01 13:30:11 -04:00
await new Promise < void > ( ( resolve , reject ) = > {
this . _shutdownServerClient . stop ( new StopRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError ) { // process receives 'exit' event
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
if ( this . _process ) return HavenoUtils . kill ( this . _process ) ;
}
2022-02-11 17:13:56 -06:00
2022-01-09 17:02:43 +01:00
// ------------------------------- HELPERS ----------------------------------
2022-02-09 01:41:00 -08:00
/ * *
* Wait for the application to be fully initialized with an account and a
* connection to the Haveno network .
*
* TODO :
*
* Currently when the application starts , the account is first initialized with createAccount ( )
* or openAccount ( ) which return immediately . A notification is sent after all setup is complete and
* the application is connected to the Haveno network .
*
* Ideally when the application starts , the system checks the Haveno network connection , supporting
* havenod . isHavenoConnectionInitialized ( ) and havenod . awaitHavenoConnectionInitialized ( ) .
* Independently , gRPC createAccount ( ) and openAccount ( ) return after all account setup and reading from disk .
2022-05-01 14:12:28 -04:00
*
* @hidden
2022-02-09 01:41:00 -08:00
* /
async _awaitAppInitialized ( ) : Promise < void > {
2022-05-01 13:30:11 -04:00
// eslint-disable-next-line no-async-promise-executor
return new Promise ( async ( resolve ) = > {
2022-02-09 01:41:00 -08:00
let isResolved = false ;
2022-05-01 13:30:11 -04:00
const resolveOnce = async ( ) = > {
2022-02-09 01:41:00 -08:00
if ( isResolved ) return ;
isResolved = true ;
2022-05-01 13:30:11 -04:00
await this . removeNotificationListener ( listener ) ;
2022-02-09 01:41:00 -08:00
resolve ( ) ;
2022-05-01 13:30:11 -04:00
} ;
const listener = async function ( notification : NotificationMessage ) {
if ( notification . getType ( ) === NotificationMessage . NotificationType . APP_INITIALIZED ) await resolveOnce ( ) ;
2022-02-09 01:41:00 -08:00
}
2022-05-01 13:30:11 -04:00
await this . addNotificationListener ( listener ) ;
if ( await this . _isAppInitialized ( ) ) await resolveOnce ( ) ;
2022-02-09 01:41:00 -08:00
} ) ;
}
2022-05-01 14:12:28 -04:00
// @hidden
2022-02-09 01:41:00 -08:00
async _isAppInitialized ( ) : Promise < boolean > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _accountClient . isAppInitialized ( new IsAppInitializedRequest ( ) , { password : this._password } , function ( err : grpcWeb.RpcError , response : IsAppInitializedReply ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( response . getIsAppInitialized ( ) ) ;
} ) ;
} ) ;
}
2022-05-04 18:14:28 -04:00
2022-01-09 17:02:43 +01:00
/ * *
2022-05-04 18:14:28 -04:00
* Update notification listener registration .
2022-01-09 17:02:43 +01:00
* Due to the nature of grpc streaming , this method returns a promise
* which may be resolved before the listener is actually registered .
* /
2022-05-04 18:14:28 -04:00
async _updateNotificationListenerRegistration ( ) : Promise < void > {
const listening = this . _notificationListeners . length > 0 ;
if ( listening && this . _notificationStream || ! listening && ! this . _notificationStream ) return ; // no difference
if ( listening ) {
return new Promise ( ( resolve ) = > {
2022-02-09 01:41:00 -08:00
2022-05-04 18:14:28 -04:00
// send request to register client listener
this . _notificationStream = this . _notificationsClient . registerNotificationListener ( new RegisterNotificationListenerRequest ( ) , { password : this._password } )
. on ( 'data' , ( data ) = > {
if ( data instanceof NotificationMessage ) {
for ( const listener of this . _notificationListeners ) listener ( data ) ;
}
} ) ;
// periodically send keep alive requests // TODO (woodser): better way to keep notification stream alive?
let firstRequest = true ;
this . _keepAliveLooper = new TaskLooper ( async ( ) = > {
if ( firstRequest ) {
firstRequest = false ;
return ;
2022-01-09 17:02:43 +01:00
}
2022-05-04 18:14:28 -04:00
await this . _sendNotification ( new NotificationMessage ( )
. setType ( NotificationMessage . NotificationType . KEEP_ALIVE )
. setTimestamp ( Date . now ( ) ) ) ;
2022-01-09 17:02:43 +01:00
} ) ;
2022-05-04 18:14:28 -04:00
this . _keepAliveLooper . start ( this . _keepAlivePeriodMs ) ;
setTimeout ( resolve , 1000 ) ; // TODO: call returns before listener registered
2022-01-15 15:43:10 -05:00
} ) ;
2022-05-04 18:14:28 -04:00
} else {
this . _keepAliveLooper . stop ( ) ;
this . _notificationStream ! . cancel ( ) ;
this . _notificationStream = undefined ;
}
2022-01-09 17:02:43 +01:00
}
2022-02-09 01:41:00 -08:00
2022-01-09 17:02:43 +01:00
/ * *
* Send a notification .
2022-05-01 14:12:28 -04:00
*
* @hidden
2022-01-09 17:02:43 +01:00
* @param { NotificationMessage } notification - notification to send
* /
async _sendNotification ( notification : NotificationMessage ) : Promise < void > {
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _notificationsClient . sendNotification ( new SendNotificationRequest ( ) . setNotification ( notification ) , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-01-09 17:02:43 +01:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-02-09 01:41:00 -08:00
/ * *
* Restore an account chunk from zip bytes .
2022-05-01 14:12:28 -04:00
*
* @hidden
2022-02-09 01:41:00 -08:00
* /
async _restoreAccountChunk ( zipBytes : Uint8Array , offset : number , totalLength : number , hasMore : boolean ) : Promise < void > {
2022-05-01 13:30:11 -04:00
const request = new RestoreAccountRequest ( )
2022-05-01 14:12:28 -04:00
. setZipBytes ( zipBytes )
. setOffset ( offset )
. setTotalLength ( totalLength )
. setHasMore ( hasMore ) ;
2022-05-01 13:30:11 -04:00
return new Promise ( ( resolve , reject ) = > {
this . _accountClient . restoreAccount ( request , { password : this._password } , function ( err : grpcWeb.RpcError ) {
2022-02-09 01:41:00 -08:00
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2021-09-12 09:39:21 -04:00
}