2022-01-15 15:43:10 -05:00
import { HavenoUtils } from "./utils/HavenoUtils" ;
import { TaskLooper } from "./utils/TaskLooper" ;
2021-09-14 08:27:45 -04:00
import * as grpcWeb from 'grpc-web' ;
2022-04-04 12:29:35 -07:00
import { GetVersionClient , AccountClient , MoneroConnectionsClient , DisputesClient , DisputeAgentsClient , NotificationsClient , WalletsClient , PriceClient , OffersClient , PaymentAccountsClient , TradesClient , ShutdownServerClient , MoneroNodeClient } from './protobuf/GrpcServiceClientPb' ;
2022-04-05 14:34:19 -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 , 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-04-04 12:29:35 -07:00
import { PaymentMethod , PaymentAccount , AvailabilityResult , Attachment , DisputeResult , Dispute , ChatMessage , MoneroNodeSettings } from './protobuf/pb_pb' ;
2022-03-07 09:57:00 -08:00
2021-12-08 06:22:36 -05:00
const console = require ( 'console' ) ;
2021-09-12 09:39:21 -04:00
/ * *
* Haveno daemon client using gRPC .
* /
2022-04-05 15:18:36 -04:00
class haveno {
2021-09-12 09:39:21 -04:00
2022-01-15 15:43:10 -05:00
// grpc clients
2022-02-09 01:41:00 -08: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-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 ;
2021-09-12 09:39:21 -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-01-15 15:43:10 -05:00
_walletRpcPort : number | undefined ;
_notificationListeners : ( ( notification : NotificationMessage ) = > void ) [ ] = [ ] ;
2022-02-09 01:41:00 -08:00
_registerNotificationListenerCalled = false ;
_keepAliveLooper : any ;
2022-01-15 15:43:10 -05:00
_keepAlivePeriodMs : number = 60000 ;
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-02-09 01:41:00 -08:00
2021-09-12 09:39:21 -04:00
/ * *
* Construct a client connected to a Haveno daemon .
*
* @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
}
2021-12-08 06:22:36 -05:00
/ * *
* Start a new Haveno process .
*
* @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-05 15:18:36 -04:00
static async startProcess ( havenoPath : string , cmd : string [ ] , url : string , enableLogging : boolean ) : Promise < haveno > {
2021-12-08 06:22:36 -05:00
// return promise which resolves after starting havenod
return new Promise ( function ( resolve , reject ) {
2021-12-14 13:04:02 -05:00
HavenoUtils . log ( 2 , "Starting Haveno process: " + cmd + " on proxy url: " + url ) ;
// state variables
let output = "" ;
2022-02-09 01:41:00 -08:00
let isStarted = false ;
2022-04-05 15:18:36 -04:00
let daemon : haveno | undefined = undefined ;
2021-12-14 13:04:02 -05:00
// start process
let childProcess = require ( 'child_process' ) . spawn ( cmd [ 0 ] , cmd . slice ( 1 ) , { cwd : havenoPath } ) ;
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 ) {
2021-12-08 06:22:36 -05:00
let 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-05 15:18:36 -04:00
if ( ! daemon && ( line . indexOf ( haveno . _fullyInitializedMessage ) >= 0 || line . indexOf ( haveno . _loginRequiredMessage ) >= 0 ) ) {
2021-12-08 06:22:36 -05:00
// get api password
let passwordIdx = cmd . indexOf ( "--apiPassword" ) ;
if ( passwordIdx < 0 ) {
reject ( "Must provide API password to start Haveno daemon" ) ;
return ;
}
let 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-05 15:18:36 -04:00
daemon = new haveno ( 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
let walletRpcPortIdx = cmd . indexOf ( "--walletRpcBindPort" ) ;
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _getVersionClient . getVersion ( new GetVersionRequest ( ) , { password : that._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-01-08 05:21:32 -08:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-09 01:41:00 -08:00
that . _accountClient . accountExists ( new AccountExistsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : AccountExistsReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . isAccountOpen ( new IsAccountOpenRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : IsAccountOpenReply ) {
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 > {
let that = this ;
await new Promise ( function ( resolve , reject ) {
that . _accountClient . createAccount ( new CreateAccountRequest ( ) . setPassword ( password ) , { password : that._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 > {
let that = this ;
await new Promise ( function ( resolve , reject ) {
that . _accountClient . openAccount ( new OpenAccountRequest ( ) . setPassword ( password ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . changePassword ( new ChangePasswordRequest ( ) . setPassword ( password ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Close the currently open account .
* /
async closeAccount ( ) : Promise < void > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . closeAccount ( new CloseAccountRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . deleteAccount ( new DeleteAccountRequest ( ) , { password : that._password } , async function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else setTimeout ( resolve , 5000 ) ;
} ) ;
} ) ;
}
/ * *
* Backup the account to the given stream . TODO : stream type ?
* /
async backupAccount ( stream : any ) : Promise < number > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let total = 0 ;
let response = that . _accountClient . backupAccount ( new BackupAccountRequest ( ) , { password : that._password } ) ;
response . on ( 'data' , ( chunk ) = > {
let bytes = ( chunk as BackupAccountReply ) . getZipBytes ( ) ; // TODO: right api?
total += bytes . length ;
stream . write ( bytes ) ;
} ) ;
response . on ( 'error' , function ( err ) {
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" )
let totalLength = zipBytes . byteLength ;
let offset = 0 ;
let chunkSize = 4000000 ; // the max frame size is 4194304 but leave room for http headers
let hasMore = true ;
while ( true ) {
if ( zipBytes . byteLength <= offset + 1 ) return ;
if ( zipBytes . byteLength <= offset + chunkSize ) {
chunkSize = zipBytes . byteLength - offset - 1 ;
hasMore = false ;
}
let subArray = zipBytes . subarray ( offset , offset + chunkSize ) ;
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
* /
async addNotificationListener ( listener : ( notification : NotificationMessage ) = > void ) : Promise < void > {
this . _notificationListeners . push ( listener ) ;
2022-02-09 01:41:00 -08:00
return this . _registerNotificationListenerOnce ( ) ;
}
/ * *
* 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
* /
async removeNotificationListener ( listener : ( notification : NotificationMessage ) = > void ) : Promise < void > {
let idx = this . _notificationListeners . indexOf ( listener ) ;
if ( idx > - 1 ) this . _notificationListeners . splice ( idx , 1 ) ;
else throw new Error ( "Notification listener is not registered" ) ;
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 > {
let connection = await this . getMoneroConnection ( ) ;
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 > {
2021-10-22 13:51:57 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-09 01:41:00 -08:00
that . _moneroConnectionsClient . addConnection ( new AddConnectionRequest ( ) . setConnection ( typeof connection === "string" ? new UrlConnection ( ) . setUrl ( connection ) : connection ) , { password : that._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 > {
2021-11-19 23:29:44 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-09 01:41:00 -08:00
that . _moneroConnectionsClient . removeConnection ( new RemoveConnectionRequest ( ) . setUrl ( url ) , { password : that._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-01-24 19:37:18 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . getConnection ( new GetConnectionRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetConnectionReply ) {
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-01-24 19:37:18 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . getConnections ( new GetConnectionsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetConnectionsReply ) {
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-01-24 19:37:18 +01:00
let that = this ;
let 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 ) ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . setConnection ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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-01-24 19:37:18 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . checkConnection ( new CheckConnectionRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : CheckConnectionReply ) {
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-01-24 19:37:18 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . checkConnections ( new CheckConnectionsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : CheckConnectionsReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . startCheckingConnections ( new StartCheckingConnectionsRequest ( ) . setRefreshPeriod ( refreshPeriod ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Stop checking the connection status periodically .
* /
async stopCheckingConnection ( ) : Promise < void > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . stopCheckingConnections ( new StopCheckingConnectionsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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-01-24 19:37:18 +01:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . getBestAvailableConnection ( new GetBestAvailableConnectionRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetBestAvailableConnectionReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroConnectionsClient . setAutoSwitch ( new SetAutoSwitchRequest ( ) . setAutoSwitch ( autoSwitch ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroNodeClient . isMoneroNodeRunning ( new IsMoneroNodeRunningRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : IsMoneroNodeRunningReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getIsRunning ( ) ) ;
} ) ;
} ) ;
}
/ * *
* Gets the current local monero node settings .
* /
async getMoneroNodeSettings ( ) : Promise < MoneroNodeSettings | undefined > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new GetMoneroNodeSettingsRequest ( ) ;
that . _moneroNodeClient . getMoneroNodeSettings ( request , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetMoneroNodeSettingsReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new StartMoneroNodeRequest ( ) . setSettings ( settings ) ;
that . _moneroNodeClient . startMoneroNode ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
/ * *
* Stops the local monero node .
* /
async stopMoneroNode ( ) : Promise < void > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _moneroNodeClient . stopMoneroNode ( new StopMoneroNodeRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
let request = new RegisterDisputeAgentRequest ( )
. setDisputeAgentType ( disputeAgentType )
. setRegistrationKey ( registrationKey ) ;
return new Promise ( function ( resolve , reject ) {
that . _disputeAgentsClient . registerDisputeAgent ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
2021-09-12 09:39:21 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-04-06 11:28:56 -04:00
that . _walletsClient . getBalances ( new GetBalancesRequest ( ) . setCurrencyCode ( "XMR" ) , { password : that._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
} ) ;
} ) ;
}
/ * *
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 > {
2021-09-12 09:39:21 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-04-05 14:34:19 -04:00
that . _walletsClient . getNewDepositAddress ( new GetNewDepositAddressRequest ( ) , { password : that._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 [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _walletsClient . getXmrTxs ( new GetXmrTxsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetXmrTxsReply ) {
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 > {
let txs = await this . getXmrTxs ( ) ; // TODO (woodser): implement getXmrTx(hash) grpc call
for ( let tx of txs ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _walletsClient . createXmrTx ( new CreateXmrTxRequest ( ) . setDestinationsList ( destinations ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : CreateXmrTxReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _walletsClient . relayXmrTx ( new RelayXmrTxRequest ( ) . setMetadata ( metadata ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : RelayXmrTxReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getHash ( ) ) ;
} ) ;
} ) ;
}
2022-02-11 17:13:56 -06:00
2022-01-24 19:37:18 +01:00
/ * *
* Get the current market price per 1 XMR in the given currency .
*
* @param { string } currencyCode - currency code ( fiat or crypto ) to get the price of
* @return { number } the current market price per 1 XMR in the given currency
* /
async getPrice ( currencyCode : string ) : Promise < number > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _priceClient . getMarketPrice ( new MarketPriceRequest ( ) . setCurrencyCode ( currencyCode ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : MarketPriceReply ) {
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-02-11 17:13:56 -06:00
* Get the current market prices of all currencies .
2022-01-24 19:37:18 +01:00
*
* @return { MarketPrice [ ] } price per 1 XMR in all supported currencies ( fiat & crypto )
* /
async getPrices ( ) : Promise < MarketPriceInfo [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _priceClient . getMarketPrices ( new MarketPricesRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : MarketPricesReply ) {
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-02-16 10:09:59 -05:00
async getMarketDepth ( assetCode : string ) : Promise < MarketDepthInfo > {
2022-02-11 17:13:56 -06:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-16 10:09:59 -05:00
that . _priceClient . getMarketDepth ( new MarketDepthRequest ( ) . setCurrencyCode ( assetCode ) , { password : that._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 .
*
* @return { PaymentMethod [ ] } the payment methods
* /
async getPaymentMethods ( ) : Promise < PaymentMethod [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _paymentAccountsClient . getPaymentMethods ( new GetPaymentMethodsRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetPaymentMethodsReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getPaymentMethodsList ( ) ) ;
} ) ;
} ) ;
}
2021-09-14 08:27:45 -04:00
/ * *
* Get payment accounts .
*
* @return { PaymentAccount [ ] } the payment accounts
* /
async getPaymentAccounts ( ) : Promise < PaymentAccount [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _paymentAccountsClient . getPaymentAccounts ( new GetPaymentAccountsRequest ( ) , { password : that._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-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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _paymentAccountsClient . getPaymentAccountForm ( new GetPaymentAccountFormRequest ( ) . setPaymentMethodId ( paymentMethodId ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetPaymentAccountFormReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _paymentAccountsClient . createPaymentAccount ( new CreatePaymentAccountRequest ( ) . setPaymentAccountForm ( JSON . stringify ( paymentAccountForm ) ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : CreatePaymentAccountReply ) {
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 > {
2021-09-14 08:30:22 -04:00
let that = this ;
let request = new CreateCryptoCurrencyPaymentAccountRequest ( )
2021-09-15 08:06:50 -04:00
. setAccountName ( accountName )
2022-02-16 10:09:59 -05:00
. setCurrencyCode ( assetCode )
2021-09-14 08:30:22 -04:00
. setAddress ( address )
2021-11-10 17:47:30 -05:00
. setTradeInstant ( false ) ; // not using instant trades
2021-09-14 08:30:22 -04:00
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _paymentAccountsClient . createCryptoCurrencyPaymentAccount ( request , { password : that._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
2021-09-19 14:00:22 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-16 10:09:59 -05:00
that . _offersClient . getOffers ( new GetOffersRequest ( ) . setDirection ( direction ) . setCurrencyCode ( assetCode ) , { password : that._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
2021-09-19 14:00:22 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2022-02-16 10:09:59 -05:00
that . _offersClient . getMyOffers ( new GetOffersRequest ( ) . setDirection ( direction ) . setCurrencyCode ( assetCode ) , { password : that._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-02-16 10:09:59 -05:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _offersClient . getMyOffer ( new GetMyOfferRequest ( ) . setId ( offerId ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetMyOfferReply ) {
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-02-16 10:09:59 -05:00
* @param { string } assetCode - traded asset code
* @param { string } direction - one of "buy" or "sell"
2021-09-14 08:30:22 -04:00
* @param { number } price - trade price
2022-02-11 17:13:56 -06:00
* @param { bool } useMarketBasedPrice - base trade on market price // TODO: this field redundant with price
2021-09-14 08:30:22 -04:00
* @param { number } marketPriceMargin - % from market price to tolerate
* @param { bigint } amount - amount to trade
* @param { bigint } minAmount - minimum amount to trade
* @param { number } buyerSecurityDeposit - buyer security deposit as % of trade amount
* @param { string } paymentAccountId - payment account id
* @param { number } triggerPrice - price to remove offer
2021-12-08 06:22:36 -05:00
* @return { OfferInfo } the posted offer
2021-09-14 08:30:22 -04:00
* /
2022-02-16 10:09:59 -05:00
async postOffer ( assetCode : string ,
2021-09-14 08:30:22 -04:00
direction : string ,
price : number ,
useMarketBasedPrice : boolean ,
marketPriceMargin : number ,
amount : bigint ,
minAmount : bigint ,
buyerSecurityDeposit : number ,
paymentAccountId : string ,
triggerPrice? : number ) : Promise < OfferInfo > {
let that = this ;
let request = new CreateOfferRequest ( )
2022-02-16 10:09:59 -05:00
. setCurrencyCode ( assetCode )
2021-09-14 08:30:22 -04:00
. setDirection ( direction )
2021-09-15 08:06:50 -04:00
. setUseMarketBasedPrice ( useMarketBasedPrice )
2022-02-11 17:13:56 -06:00
. setPrice ( useMarketBasedPrice ? "1.0" : price . toString ( ) ) // TODO: positive price required even if using market price
2021-09-15 08:06:50 -04:00
. setMarketPriceMargin ( marketPriceMargin )
2021-09-14 08:30:22 -04:00
. setAmount ( amount . toString ( ) )
2021-09-15 08:06:50 -04:00
. setMinAmount ( minAmount . toString ( ) )
. setBuyerSecurityDeposit ( buyerSecurityDeposit )
2021-10-15 13:17:52 -04:00
. setPaymentAccountId ( paymentAccountId ) ;
2021-09-15 08:06:50 -04:00
if ( triggerPrice ) request . setTriggerPrice ( BigInt ( triggerPrice . toString ( ) ) . toString ( ) ) ;
2021-09-14 08:30:22 -04:00
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _offersClient . createOffer ( request , { password : that._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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _offersClient . cancelOffer ( new CancelOfferRequest ( ) . setId ( offerId ) , { password : that._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 > {
let that = this ;
let request = new TakeOfferRequest ( )
2022-01-15 15:43:10 -05:00
. setOfferId ( offerId )
. setPaymentAccountId ( paymentAccountId ) ;
2021-09-19 14:00:22 -04:00
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _tradesClient . takeOffer ( request , { password : that._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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _tradesClient . getTrade ( new GetTradeRequest ( ) . setTradeId ( tradeId ) , { password : that._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 [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _tradesClient . getTrades ( new GetTradesRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetTradesReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _tradesClient . confirmPaymentStarted ( new ConfirmPaymentStartedRequest ( ) . setTradeId ( tradeId ) , { password : that._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 > {
2021-09-14 08:30:22 -04:00
let that = this ;
return new Promise ( function ( resolve , reject ) {
2021-11-11 13:48:31 -05:00
that . _tradesClient . confirmPaymentReceived ( new ConfirmPaymentReceivedRequest ( ) . setTradeId ( tradeId ) , { password : that._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 [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new GetChatMessagesRequest ( ) . setTradeId ( tradeId ) ;
that . _tradesClient . getChatMessages ( request , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetChatMessagesReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new SendChatMessageRequest ( )
. setTradeId ( tradeId )
. setMessage ( message ) ;
that . _tradesClient . sendChatMessage ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _disputesClient . getDispute ( new GetDisputeRequest ( ) . setTradeId ( tradeId ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetDisputeReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getDispute ( ) ! ) ;
} ) ;
} ) ;
}
/ * *
* Get all disputes .
* /
async getDisputes ( ) : Promise < Dispute [ ] > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _disputesClient . getDisputes ( new GetDisputesRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : GetDisputesReply ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _disputesClient . openDispute ( new OpenDisputeRequest ( ) . setTradeId ( tradeId ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new ResolveDisputeRequest ( )
. setTradeId ( tradeId )
. setWinner ( winner )
. setReason ( reason )
. setSummaryNotes ( summaryNotes )
. setCustomPayoutAmount ( customWinnerAmount ? customWinnerAmount . toString ( ) : "0" ) ;
that . _disputesClient . resolveDispute ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
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 > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
let request = new SendDisputeChatMessageRequest ( )
. setDisputeId ( disputeId )
. setMessage ( message )
. setAttachmentsList ( attachments ) ;
that . _disputesClient . sendDisputeChatMessage ( request , { password : that._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-02-09 01:41:00 -08:00
/ * *
* Shutdown the Haveno daemon server and stop the process if applicable .
* /
async shutdownServer() {
if ( this . _keepAliveLooper ) this . _keepAliveLooper . stop ( ) ;
let that = this ;
await new Promise ( function ( resolve , reject ) {
that . _shutdownServerClient . stop ( new StopRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError ) { // process receives 'exit' event
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 .
* /
async _awaitAppInitialized ( ) : Promise < void > {
let that = this ;
return new Promise ( async function ( resolve ) {
let isResolved = false ;
let listener = async function ( notification : NotificationMessage ) {
if ( notification . getType ( ) === NotificationMessage . NotificationType . APP_INITIALIZED ) await resolveOnce ( ) ;
}
await that . addNotificationListener ( listener ) ;
if ( await that . _isAppInitialized ( ) ) await resolveOnce ( ) ;
async function resolveOnce() {
if ( isResolved ) return ;
isResolved = true ;
await that . removeNotificationListener ( listener ) ;
resolve ( ) ;
}
} ) ;
}
async _isAppInitialized ( ) : Promise < boolean > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . isAppInitialized ( new IsAppInitializedRequest ( ) , { password : that._password } , function ( err : grpcWeb.RpcError , response : IsAppInitializedReply ) {
if ( err ) reject ( err ) ;
else resolve ( response . getIsAppInitialized ( ) ) ;
} ) ;
} ) ;
}
2022-01-09 17:02:43 +01:00
/ * *
* Register a listener to receive notifications .
* Due to the nature of grpc streaming , this method returns a promise
* which may be resolved before the listener is actually registered .
* /
2022-02-09 01:41:00 -08:00
async _registerNotificationListenerOnce ( ) : Promise < void > {
if ( this . _registerNotificationListenerCalled ) return ;
else this . _registerNotificationListenerCalled = true ;
2022-01-09 17:02:43 +01:00
let that = this ;
return new Promise ( function ( resolve ) {
2022-02-09 01:41:00 -08:00
2022-01-15 15:43:10 -05:00
// send request to register client listener
2022-01-09 17:02:43 +01:00
that . _notificationsClient . registerNotificationListener ( new RegisterNotificationListenerRequest ( ) , { password : that._password } )
2022-02-09 01:41:00 -08:00
. on ( 'data' , ( data ) = > {
2022-01-09 17:02:43 +01:00
if ( data instanceof NotificationMessage ) {
for ( let listener of that . _notificationListeners ) listener ( data ) ;
}
} ) ;
2022-02-09 01:41:00 -08:00
2022-01-15 15:43:10 -05:00
// periodically send keep alive requests // TODO (woodser): better way to keep notification stream alive?
let firstRequest = true ;
2022-02-09 01:41:00 -08:00
that . _keepAliveLooper = new TaskLooper ( async function ( ) {
2022-01-15 15:43:10 -05:00
if ( firstRequest ) {
firstRequest = false ;
return ;
}
await that . _sendNotification ( new NotificationMessage ( )
. setType ( NotificationMessage . NotificationType . KEEP_ALIVE )
. setTimestamp ( Date . now ( ) ) ) ;
} ) ;
2022-02-09 01:41:00 -08:00
that . _keepAliveLooper . start ( that . _keepAlivePeriodMs ) ;
setTimeout ( resolve , 1000 ) ; // TODO: call returns before listener registered
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-02-09 01:41:00 -08:00
*
2022-01-09 17:02:43 +01:00
* @param { NotificationMessage } notification - notification to send
* /
async _sendNotification ( notification : NotificationMessage ) : Promise < void > {
let that = this ;
return new Promise ( function ( resolve , reject ) {
that . _notificationsClient . sendNotification ( new SendNotificationRequest ( ) . setNotification ( notification ) , { password : that._password } , function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2022-02-09 01:41:00 -08:00
/ * *
* Restore an account chunk from zip bytes .
* /
async _restoreAccountChunk ( zipBytes : Uint8Array , offset : number , totalLength : number , hasMore : boolean ) : Promise < void > {
let that = this ;
let request = new RestoreAccountRequest ( )
. setZipBytes ( zipBytes )
. setOffset ( offset )
. setTotalLength ( totalLength )
. setHasMore ( hasMore ) ;
return new Promise ( function ( resolve , reject ) {
that . _accountClient . restoreAccount ( request , { password : that._password } , function ( err : grpcWeb.RpcError ) {
if ( err ) reject ( err ) ;
else resolve ( ) ;
} ) ;
} ) ;
}
2021-09-12 09:39:21 -04:00
}
2022-04-05 15:18:36 -04:00
export { haveno } ;