update to monero-ts v0.9.2 with typescript

This commit is contained in:
woodser 2023-10-02 08:16:54 -04:00
parent c6eb746e86
commit 90f59155e1
11 changed files with 365 additions and 345 deletions

View File

@ -74,17 +74,17 @@ export default class HavenoUtils {
/** /**
* Convert XMR to atomic units. * Convert XMR to atomic units.
* *
* @param {number|string} amountXmr - amount in XMR to convert to atomic units * @param {number | string} amountXmr - amount in XMR to convert to atomic units
* @return {BigInt} amount in atomic units * @return {bigint} amount in atomic units
*/ */
static xmrToAtomicUnits(amountXmr: number | string): bigint; static xmrToAtomicUnits(amountXmr: number | string): bigint;
/** /**
* Convert atomic units to XMR. * Convert atomic units to XMR.
* *
* @param {BigInt|string} amountAtomicUnits - amount in atomic units to convert to XMR * @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR
* @return {number} amount in XMR * @return {number} amount in XMR
*/ */
static atomicUnitsToXmr(amountAtomicUnits: BigInt | string): number; static atomicUnitsToXmr(amountAtomicUnits: bigint | string): number;
/** /**
* Get a validated payment method id from a string or form id. * Get a validated payment method id from a string or form id.
* *

View File

@ -107,14 +107,12 @@ class HavenoUtils {
/** /**
* Convert XMR to atomic units. * Convert XMR to atomic units.
* *
* @param {number|string} amountXmr - amount in XMR to convert to atomic units * @param {number | string} amountXmr - amount in XMR to convert to atomic units
* @return {BigInt} amount in atomic units * @return {bigint} amount in atomic units
*/ */
static xmrToAtomicUnits(amountXmr) { static xmrToAtomicUnits(amountXmr) {
if (typeof amountXmr === "number") if (typeof amountXmr === "number")
amountXmr = "" + amountXmr; amountXmr = "" + amountXmr;
else if (typeof amountXmr !== "string")
throw new Error("Must provide XMR amount as a string or js number to convert to atomic units");
let decimalDivisor = 1; let decimalDivisor = 1;
let decimalIdx = amountXmr.indexOf('.'); let decimalIdx = amountXmr.indexOf('.');
if (decimalIdx > -1) { if (decimalIdx > -1) {
@ -126,14 +124,12 @@ class HavenoUtils {
/** /**
* Convert atomic units to XMR. * Convert atomic units to XMR.
* *
* @param {BigInt|string} amountAtomicUnits - amount in atomic units to convert to XMR * @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR
* @return {number} amount in XMR * @return {number} amount in XMR
*/ */
static atomicUnitsToXmr(amountAtomicUnits) { static atomicUnitsToXmr(amountAtomicUnits) {
if (typeof amountAtomicUnits === "string") if (typeof amountAtomicUnits === "string")
amountAtomicUnits = BigInt(amountAtomicUnits); amountAtomicUnits = BigInt(amountAtomicUnits);
else if (typeof amountAtomicUnits !== "bigint")
throw new Error("Must provide atomic units as BigInt or string to convert to XMR");
const quotient = amountAtomicUnits / HavenoUtils.AU_PER_XMR; const quotient = amountAtomicUnits / HavenoUtils.AU_PER_XMR;
const remainder = amountAtomicUnits % HavenoUtils.AU_PER_XMR; const remainder = amountAtomicUnits % HavenoUtils.AU_PER_XMR;
return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR); return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR);

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,8 @@ class TaskLooper {
* @param {int} periodInMs the loop period in milliseconds * @param {int} periodInMs the loop period in milliseconds
*/ */
start(periodInMs) { start(periodInMs) {
if (periodInMs <= 0)
throw new Error("Looper period must be greater than 0 ms");
if (this._isStarted) if (this._isStarted)
return; return;
this._isStarted = true; this._isStarted = true;

View File

@ -1 +1 @@
{"version":3,"file":"TaskLooper.js","sourceRoot":"","sources":["../../src/utils/TaskLooper.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,MAAqB,UAAU;IAO7B;;;;OAIG;IACH,YAAY,EAAuB;QACjC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAkB;QACtB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACzF,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,YAAY,CAAC,IAAI,CAAC,QAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,UAAkB;QAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,UAAU,EAAE;YACtB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACtI;QACD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;CACF;AAhDD,6BAgDC"} {"version":3,"file":"TaskLooper.js","sourceRoot":"","sources":["../../src/utils/TaskLooper.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,MAAqB,UAAU;IAO7B;;;;OAIG;IACH,YAAY,EAAuB;QACjC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAkB;QACtB,IAAI,UAAU,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAChF,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACzF,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,YAAY,CAAC,IAAI,CAAC,QAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,UAAkB;QAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,UAAU,EAAE;YACtB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACtI;QACD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;CACF;AAjDD,6BAiDC"}

534
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",
"jest": "^26.6.0", "jest": "^26.6.0",
"monero-javascript": "^0.8.0", "monero-ts": "0.9.2",
"typedoc": "^0.23.26", "typedoc": "^0.23.26",
"typedoc-plugin-missing-exports": "^1.0.0", "typedoc-plugin-missing-exports": "^1.0.0",
"typedoc-plugin-rename-defaults": "^0.6.4", "typedoc-plugin-rename-defaults": "^0.6.4",

View File

@ -1,6 +1,6 @@
// --------------------------------- IMPORTS ---------------------------------- // --------------------------------- IMPORTS ----------------------------------
// import haveno types // haveno imports
import HavenoClient from "./HavenoClient"; import HavenoClient from "./HavenoClient";
import HavenoError from "./utils/HavenoError"; import HavenoError from "./utils/HavenoError";
import HavenoUtils from "./utils/HavenoUtils"; import HavenoUtils from "./utils/HavenoUtils";
@ -10,15 +10,6 @@ import { XmrDestination, XmrTx, XmrIncomingTransfer, XmrOutgoingTransfer } from
import AuthenticationStatus = UrlConnection.AuthenticationStatus; import AuthenticationStatus = UrlConnection.AuthenticationStatus;
import OnlineStatus = UrlConnection.OnlineStatus; import OnlineStatus = UrlConnection.OnlineStatus;
// import monero-javascript
const monerojs = require("monero-javascript"); // TODO (woodser): support typescript and `npm install @types/monero-javascript` in monero-javascript
const GenUtils = monerojs.GenUtils;
const BigInteger = monerojs.BigInteger;
const MoneroTxConfig = monerojs.MoneroTxConfig;
const MoneroDestination = monerojs.MoneroDestination;
const MoneroUtils = monerojs.MoneroUtils;
const TaskLooper = monerojs.TaskLooper;
// other imports // other imports
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
@ -26,6 +17,7 @@ import net from "net";
import assert from "assert"; import assert from "assert";
import console from "console"; // import console because jest swallows messages in real time import console from "console"; // import console because jest swallows messages in real time
import * as os from 'os'; import * as os from 'os';
import * as moneroTs from "monero-ts";
// ------------------------------ TEST CONFIG --------------------------------- // ------------------------------ TEST CONFIG ---------------------------------
@ -40,16 +32,16 @@ const startupHavenods: HavenoClient[] = [];
let arbitrator: HavenoClient; let arbitrator: HavenoClient;
let user1: HavenoClient; let user1: HavenoClient;
let user2: HavenoClient; let user2: HavenoClient;
let monerod: any; let monerod: moneroTs.MoneroDaemon;
let fundingWallet: any; let fundingWallet: moneroTs.MoneroWalletRpc;
let user1Wallet: any; let user1Wallet: moneroTs.MoneroWalletRpc;
let user2Wallet: any; let user2Wallet: moneroTs.MoneroWalletRpc;
// default test configuration // default test configuration
const TestConfig = { const TestConfig = {
logLevel: 2, logLevel: 2,
baseCurrencyNetwork: getBaseCurrencyNetwork(), baseCurrencyNetwork: getBaseCurrencyNetwork(),
networkType: getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? monerojs.MoneroNetworkType.MAINNET : getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? monerojs.MoneroNetworkType.TESTNET : monerojs.MoneroNetworkType.STAGENET, networkType: getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? moneroTs.MoneroNetworkType.MAINNET : getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? moneroTs.MoneroNetworkType.TESTNET : moneroTs.MoneroNetworkType.STAGENET,
moneroBinsDir: "../haveno/.localnet", moneroBinsDir: "../haveno/.localnet",
testDataDir: "./testdata", testDataDir: "./testdata",
haveno: { haveno: {
@ -113,7 +105,7 @@ const TestConfig = {
} }
], ],
maxFee: HavenoUtils.xmrToAtomicUnits(0.5), // local testnet fees can be relatively high maxFee: HavenoUtils.xmrToAtomicUnits(0.5), // local testnet fees can be relatively high
minSecurityDeposit: MoneroUtils.xmrToAtomicUnits(0.1), minSecurityDeposit: moneroTs.MoneroUtils.xmrToAtomicUnits(0.1),
maxAdjustmentPct: 0.2, maxAdjustmentPct: 0.2,
daemonPollPeriodMs: 5000, daemonPollPeriodMs: 5000,
maxWalletStartupMs: 10000, // TODO (woodser): make shorter by switching to jni maxWalletStartupMs: 10000, // TODO (woodser): make shorter by switching to jni
@ -322,7 +314,7 @@ beforeAll(async () => {
// initialize monerod // initialize monerod
try { try {
monerod = await monerojs.connectToDaemonRpc(TestConfig.monerod.url, TestConfig.monerod.username, TestConfig.monerod.password); monerod = await moneroTs.connectToDaemonRpc(TestConfig.monerod.url, TestConfig.monerod.username, TestConfig.monerod.password);
await mineToHeight(160); // initialize blockchain to latest block type await mineToHeight(160); // initialize blockchain to latest block type
} catch (err) { } catch (err) {
HavenoUtils.log(0, "Error initializing internal monerod: " + err.message); // allowed in order to test starting and stopping local node HavenoUtils.log(0, "Error initializing internal monerod: " + err.message); // allowed in order to test starting and stopping local node
@ -347,8 +339,8 @@ beforeAll(async () => {
TestConfig.trade.taker = user2; TestConfig.trade.taker = user2;
// connect client wallets // connect client wallets
user1Wallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[1].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[1].accountPasswordRequired ? TestConfig.startupHavenods[1].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); user1Wallet = await moneroTs.connectToWalletRpc(TestConfig.startupHavenods[1].walletUrl!, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[1].accountPasswordRequired ? TestConfig.startupHavenods[1].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword);
user2Wallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[2].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[2].accountPasswordRequired ? TestConfig.startupHavenods[2].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); user2Wallet = await moneroTs.connectToWalletRpc(TestConfig.startupHavenods[2].walletUrl!, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[2].accountPasswordRequired ? TestConfig.startupHavenods[2].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword);
// register arbitrator dispute agent // register arbitrator dispute agent
await arbitrator.registerDisputeAgent("arbitrator", getArbitratorPrivKey(0)); await arbitrator.registerDisputeAgent("arbitrator", getArbitratorPrivKey(0));
@ -379,7 +371,7 @@ async function shutDown() {
await Promise.all(promises); await Promise.all(promises);
// terminate monero-javascript worker // terminate monero-javascript worker
await monerojs.LibraryUtils.terminateWorker(); await moneroTs.LibraryUtils.terminateWorker();
} }
// ----------------------------------- TESTS ---------------------------------- // ----------------------------------- TESTS ----------------------------------
@ -531,7 +523,7 @@ test("Can manage an account (CI)", async () => {
}); });
test("Can manage Monero daemon connections (CI)", async () => { test("Can manage Monero daemon connections (CI)", async () => {
let monerod2: any; let monerod2: moneroTs.MoneroDaemonRpc | undefined = undefined;
let user3: HavenoClient|undefined; let user3: HavenoClient|undefined;
let err: any; let err: any;
try { try {
@ -578,9 +570,9 @@ test("Can manage Monero daemon connections (CI)", async () => {
"--rpc-bind-port", TestConfig.monerod2.rpcBindPort, "--rpc-bind-port", TestConfig.monerod2.rpcBindPort,
"--no-zmq" "--no-zmq"
]; ];
if (getBaseCurrencyNetwork() !== BaseCurrencyNetwork.XMR_MAINNET) cmd.push("--" + monerojs.MoneroNetworkType.toString(TestConfig.networkType).toLowerCase()); if (getBaseCurrencyNetwork() !== BaseCurrencyNetwork.XMR_MAINNET) cmd.push("--" + moneroTs.MoneroNetworkType.toString(TestConfig.networkType).toLowerCase());
if (TestConfig.monerod2.username) cmd.push("--rpc-login", TestConfig.monerod2.username + ":" + TestConfig.monerod2.password); if (TestConfig.monerod2.username) cmd.push("--rpc-login", TestConfig.monerod2.username + ":" + TestConfig.monerod2.password);
monerod2 = await monerojs.connectToDaemonRpc(cmd); monerod2 = await moneroTs.connectToDaemonRpc(cmd);
// connection is online and not authenticated // connection is online and not authenticated
connection = await user3.checkMoneroConnection(); connection = await user3.checkMoneroConnection();
@ -624,7 +616,7 @@ test("Can manage Monero daemon connections (CI)", async () => {
// stop monerod // stop monerod
//await monerod2.stopProcess(); // TODO (monero-javascript): monerod remains available after await monerod.stopProcess() for up to 40 seconds //await monerod2.stopProcess(); // TODO (monero-javascript): monerod remains available after await monerod.stopProcess() for up to 40 seconds
await GenUtils.killProcess(monerod2.process, "SIGKILL"); await moneroTs.GenUtils.killProcess(monerod2.getProcess(), "SIGKILL");
// test auto switch after periodic connection check // test auto switch after periodic connection check
await wait(TestConfig.daemonPollPeriodMs * 2); await wait(TestConfig.daemonPollPeriodMs * 2);
@ -744,7 +736,7 @@ test("Can start and stop a local Monero node (CI)", async() => {
// expect connection to local monero node to succeed // expect connection to local monero node to succeed
const rpcUrl = "http://127.0.0.1:" + rpcPort.toString(); const rpcUrl = "http://127.0.0.1:" + rpcPort.toString();
let daemon = await monerojs.connectToDaemonRpc(rpcUrl, "superuser", "abctesting123"); let daemon = await moneroTs.connectToDaemonRpc(rpcUrl, "superuser", "abctesting123");
let height = await daemon.getHeight(); let height = await daemon.getHeight();
assert(height >= 0); assert(height >= 0);
@ -762,7 +754,7 @@ test("Can start and stop a local Monero node (CI)", async() => {
isMoneroNodeOnline = await user1.isMoneroNodeOnline(); isMoneroNodeOnline = await user1.isMoneroNodeOnline();
assert(!isMoneroNodeOnline); assert(!isMoneroNodeOnline);
try { try {
daemon = await monerojs.connectToDaemonRpc(rpcUrl); daemon = await moneroTs.connectToDaemonRpc(rpcUrl);
height = await daemon.getHeight(); height = await daemon.getHeight();
throw new Error("should have thrown"); throw new Error("should have thrown");
} catch (err: any) { } catch (err: any) {
@ -776,11 +768,11 @@ test("Has a Monero wallet (CI)", async () => {
// get seed phrase // get seed phrase
const seed = await user1.getXmrSeed(); const seed = await user1.getXmrSeed();
await MoneroUtils.validateMnemonic(seed); await moneroTs.MoneroUtils.validateMnemonic(seed);
// get primary address // get primary address
const primaryAddress = await user1.getXmrPrimaryAddress(); const primaryAddress = await user1.getXmrPrimaryAddress();
await MoneroUtils.validateAddress(primaryAddress, TestConfig.networkType); await moneroTs.MoneroUtils.validateAddress(primaryAddress, TestConfig.networkType);
// wait for user1 to have unlocked balance // wait for user1 to have unlocked balance
const tradeAmount = BigInt("250000000000"); const tradeAmount = BigInt("250000000000");
@ -801,7 +793,7 @@ test("Has a Monero wallet (CI)", async () => {
// get new subaddresses // get new subaddresses
for (let i = 0; i < 0; i++) { for (let i = 0; i < 0; i++) {
const address = await user1.getXmrNewSubaddress(); const address = await user1.getXmrNewSubaddress();
await MoneroUtils.validateAddress(address, TestConfig.networkType); await moneroTs.MoneroUtils.validateAddress(address, TestConfig.networkType);
} }
// create withdraw tx // create withdraw tx
@ -1051,7 +1043,7 @@ test("Can validate payment account forms (CI, sanity check)", async () => {
const paymentMethods = await user1.getPaymentMethods(); const paymentMethods = await user1.getPaymentMethods();
expect(paymentMethods.length).toEqual(expectedPaymentMethods.length); expect(paymentMethods.length).toEqual(expectedPaymentMethods.length);
for (const paymentMethod of paymentMethods) { for (const paymentMethod of paymentMethods) {
assert(GenUtils.arrayContains(expectedPaymentMethods, paymentMethod.getId()), "Payment method is not expected: " + paymentMethod.getId()); assert(moneroTs.GenUtils.arrayContains(expectedPaymentMethods, paymentMethod.getId()), "Payment method is not expected: " + paymentMethod.getId());
} }
// test form for each payment method // test form for each payment method
@ -1105,7 +1097,7 @@ test("Can create fiat payment accounts (CI)", async () => {
const accountForm = await user1.getPaymentAccountForm(paymentMethodId); const accountForm = await user1.getPaymentAccountForm(paymentMethodId);
// edit form // edit form
HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, "Revolut account " + GenUtils.getUUID(), accountForm); HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, "Revolut account " + moneroTs.GenUtils.getUUID(), accountForm);
HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.USER_NAME, "user123", accountForm); HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.USER_NAME, "user123", accountForm);
HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.TRADE_CURRENCIES, "gbp,eur,usd", accountForm); HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.TRADE_CURRENCIES, "gbp,eur,usd", accountForm);
@ -1134,7 +1126,7 @@ test("Can create crypto payment accounts (CI)", async () => {
for (const testAccount of TestConfig.cryptoAddresses) { for (const testAccount of TestConfig.cryptoAddresses) {
// create payment account // create payment account
const name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(); const name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + moneroTs.GenUtils.getUUID();
const paymentAccount: PaymentAccount = await user1.createCryptoPaymentAccount(name, testAccount.currencyCode, testAccount.address); const paymentAccount: PaymentAccount = await user1.createCryptoPaymentAccount(name, testAccount.currencyCode, testAccount.address);
testCryptoPaymentAccount(paymentAccount); testCryptoPaymentAccount(paymentAccount);
testCryptoPaymentAccountEquals(paymentAccount, testAccount, name); testCryptoPaymentAccountEquals(paymentAccount, testAccount, name);
@ -1163,7 +1155,7 @@ test("Can create crypto payment accounts (CI)", async () => {
.toThrow('123 is not a valid eth address'); .toThrow('123 is not a valid eth address');
// test address duplicity // test address duplicity
let uid = "Unique account name " + GenUtils.getUUID(); let uid = "Unique account name " + moneroTs.GenUtils.getUUID();
await user1.createCryptoPaymentAccount(uid, TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address) await user1.createCryptoPaymentAccount(uid, TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address)
await expect(async () => { await user1.createCryptoPaymentAccount(uid, TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address); }) await expect(async () => { await user1.createCryptoPaymentAccount(uid, TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address); })
.rejects .rejects
@ -1250,7 +1242,7 @@ test("Can schedule offers with locked funds (CI)", async () => {
// start user3 // start user3
user3 = await initHaveno(); user3 = await initHaveno();
const user3Wallet = await monerojs.connectToWalletRpc("http://127.0.0.1:" + user3.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); const user3Wallet = await moneroTs.connectToWalletRpc("http://127.0.0.1:" + user3.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword);
// fund user3 with 2 outputs of 0.5 XMR // fund user3 with 2 outputs of 0.5 XMR
const outputAmt = BigInt("500000000000"); const outputAmt = BigInt("500000000000");
@ -1708,7 +1700,7 @@ test("Can handle unexpected errors during trade initialization", async () => {
let paymentAccount = await createCryptoPaymentAccount(traders[1]); let paymentAccount = await createCryptoPaymentAccount(traders[1]);
wait(3000).then(async function() { wait(3000).then(async function() {
try { try {
const traderWallet = await monerojs.connectToWalletRpc("http://localhost:" + traders[1].getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); const traderWallet = await moneroTs.connectToWalletRpc("http://localhost:" + traders[1].getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword);
for (const frozenOutput of await traderWallet.getOutputs({isFrozen: true})) await traderWallet.thawOutput(frozenOutput.getKeyImage().getHex()); for (const frozenOutput of await traderWallet.getOutputs({isFrozen: true})) await traderWallet.thawOutput(frozenOutput.getKeyImage().getHex());
HavenoUtils.log(1, "Sweeping trade funds"); HavenoUtils.log(1, "Sweeping trade funds");
await traderWallet.sweepUnlocked({address: await traderWallet.getPrimaryAddress(), relay: true}); await traderWallet.sweepUnlocked({address: await traderWallet.getPrimaryAddress(), relay: true});
@ -1742,7 +1734,7 @@ test("Can handle unexpected errors during trade initialization", async () => {
// trader 0 spends trade funds after trader 2 takes offer // trader 0 spends trade funds after trader 2 takes offer
wait(3000).then(async function() { wait(3000).then(async function() {
try { try {
const traderWallet = await monerojs.connectToWalletRpc("http://localhost:" + traders[0].getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); const traderWallet = await moneroTs.connectToWalletRpc("http://localhost:" + traders[0].getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword);
for (const frozenOutput of await traderWallet.getOutputs({isFrozen: true})) await traderWallet.thawOutput(frozenOutput.getKeyImage().getHex()); for (const frozenOutput of await traderWallet.getOutputs({isFrozen: true})) await traderWallet.thawOutput(frozenOutput.getKeyImage().getHex());
HavenoUtils.log(1, "Sweeping offer funds"); HavenoUtils.log(1, "Sweeping offer funds");
await traderWallet.sweepUnlocked({address: await traderWallet.getPrimaryAddress(), relay: true}); await traderWallet.sweepUnlocked({address: await traderWallet.getPrimaryAddress(), relay: true});
@ -1972,7 +1964,7 @@ async function executeTrades(ctxs: TradeContext[], executionCtx?: TradeContext):
// execute trades in thread pool unless serial // execute trades in thread pool unless serial
if (executionCtx.concurrentTrades) { if (executionCtx.concurrentTrades) {
const tradePromises: Promise<string>[] = []; const tradePromises: Promise<string>[] = [];
const pool = new monerojs.ThreadPool(executionCtx.maxConcurrency!); const pool = new moneroTs.ThreadPool(executionCtx.maxConcurrency!);
for (const ctx of ctxs) tradePromises.push(pool.submit(() => executeTrade(Object.assign(ctx, {concurrentTrades: executionCtx!.concurrentTrades})))); for (const ctx of ctxs) tradePromises.push(pool.submit(() => executeTrade(Object.assign(ctx, {concurrentTrades: executionCtx!.concurrentTrades}))));
try { try {
offerIds = await Promise.all(tradePromises); offerIds = await Promise.all(tradePromises);
@ -2299,7 +2291,7 @@ async function testTradePayoutUnlock(ctx: TradeContext) {
async function testTradeState(trade: TradeInfo, ctx: TradeContext) { async function testTradeState(trade: TradeInfo, ctx: TradeContext) {
assert.equal(trade.getPhase(), ctx.phase, "expected trade phase to be " + ctx.phase + " but was " + trade.getPhase() + " for trade " + trade.getTradeId()); assert.equal(trade.getPhase(), ctx.phase, "expected trade phase to be " + ctx.phase + " but was " + trade.getPhase() + " for trade " + trade.getTradeId());
assert(GenUtils.arrayContains(ctx.payoutState, trade.getPayoutState()), "expected one of payout state " + ctx.payoutState + " but was " + trade.getPayoutState() + " for trade " + trade.getTradeId()); assert(moneroTs.GenUtils.arrayContains(ctx.payoutState, trade.getPayoutState()), "expected one of payout state " + ctx.payoutState + " but was " + trade.getPayoutState() + " for trade " + trade.getTradeId());
if (ctx.disputeState) expect(trade.getDisputeState()).toEqual(ctx.disputeState); if (ctx.disputeState) expect(trade.getDisputeState()).toEqual(ctx.disputeState);
if (ctx.isCompleted !== undefined) expect(trade.getIsCompleted()).toEqual(ctx.isCompleted); if (ctx.isCompleted !== undefined) expect(trade.getIsCompleted()).toEqual(ctx.isCompleted);
if (ctx.isPayoutPublished !== undefined) expect(trade.getIsPayoutPublished()).toEqual(ctx.isPayoutPublished); if (ctx.isPayoutPublished !== undefined) expect(trade.getIsPayoutPublished()).toEqual(ctx.isPayoutPublished);
@ -2423,7 +2415,7 @@ async function takeOffer(ctx: TradeContext): Promise<TradeInfo> {
await wait(ctx.maxTimePeerNoticeMs!); await wait(ctx.maxTimePeerNoticeMs!);
const tradeNotifications = getNotifications(makerNotifications, NotificationMessage.NotificationType.TRADE_UPDATE, trade.getTradeId()); const tradeNotifications = getNotifications(makerNotifications, NotificationMessage.NotificationType.TRADE_UPDATE, trade.getTradeId());
expect(tradeNotifications.length).toBe(1); expect(tradeNotifications.length).toBe(1);
assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], tradeNotifications[0].getTrade()!.getPhase(), "Unexpected trade phase: " + tradeNotifications[0].getTrade()!.getPhase())); assert(moneroTs.GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], tradeNotifications[0].getTrade()!.getPhase()), "Unexpected trade phase: " + tradeNotifications[0].getTrade()!.getPhase());
expect(tradeNotifications[0].getTitle()).toEqual("Offer Taken"); expect(tradeNotifications[0].getTitle()).toEqual("Offer Taken");
expect(tradeNotifications[0].getMessage()).toEqual("Your offer " + ctx.offerId + " has been accepted"); expect(tradeNotifications[0].getMessage()).toEqual("Your offer " + ctx.offerId + " has been accepted");
@ -2431,7 +2423,7 @@ async function takeOffer(ctx: TradeContext): Promise<TradeInfo> {
// taker can get trade // taker can get trade
let fetchedTrade: TradeInfo = await ctx.taker!.getTrade(trade.getTradeId()); let fetchedTrade: TradeInfo = await ctx.taker!.getTrade(trade.getTradeId());
assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], fetchedTrade.getPhase()), "Unexpected trade phase: " + fetchedTrade.getPhase()); assert(moneroTs.GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], fetchedTrade.getPhase()), "Unexpected trade phase: " + fetchedTrade.getPhase());
// TODO: more fetched trade tests // TODO: more fetched trade tests
// market-priced offer amounts are unadjusted, fixed-priced offer amounts are adjusted (e.g. cash at atm is $10 increments) // market-priced offer amounts are unadjusted, fixed-priced offer amounts are adjusted (e.g. cash at atm is $10 increments)
@ -2445,7 +2437,7 @@ async function takeOffer(ctx: TradeContext): Promise<TradeInfo> {
// maker can get trade // maker can get trade
fetchedTrade = await ctx.maker!.getTrade(trade.getTradeId()); fetchedTrade = await ctx.maker!.getTrade(trade.getTradeId());
assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], fetchedTrade.getPhase()), "Unexpected trade phase: " + fetchedTrade.getPhase()); assert(moneroTs.GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED", "DEPOSITS_UNLOCKED"], fetchedTrade.getPhase()), "Unexpected trade phase: " + fetchedTrade.getPhase());
return trade; return trade;
} }
@ -2802,7 +2794,7 @@ async function initHavenos(numDaemons: number, config?: any) {
async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> { async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> {
if (!ctx) ctx = {}; if (!ctx) ctx = {};
Object.assign(ctx, TestConfig.defaultHavenod, Object.assign({}, ctx)); Object.assign(ctx, TestConfig.defaultHavenod, Object.assign({}, ctx));
if (!ctx.appName) ctx.appName = "haveno-" + TestConfig.baseCurrencyNetwork + "_instance_" + GenUtils.getUUID(); if (!ctx.appName) ctx.appName = "haveno-" + TestConfig.baseCurrencyNetwork + "_instance_" + moneroTs.GenUtils.getUUID();
// connect to existing server or start new process // connect to existing server or start new process
let havenod: HavenoClient; let havenod: HavenoClient;
@ -2818,7 +2810,7 @@ async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> {
if (!ctx.port) { if (!ctx.port) {
for (const httpPort of Array.from(TestConfig.ports.keys())) { for (const httpPort of Array.from(TestConfig.ports.keys())) {
if (httpPort === "8079" || httpPort === "8080" || httpPort === "8081") continue; // reserved for arbitrator, user1, and user2 if (httpPort === "8079" || httpPort === "8080" || httpPort === "8081") continue; // reserved for arbitrator, user1, and user2
if (!GenUtils.arrayContains(HAVENO_PROCESS_PORTS, httpPort) && (!ctx.excludePorts || !GenUtils.arrayContains(ctx.excludePorts, httpPort))) { if (!moneroTs.GenUtils.arrayContains(HAVENO_PROCESS_PORTS, httpPort) && (!ctx.excludePorts || !moneroTs.GenUtils.arrayContains(ctx.excludePorts, httpPort))) {
HAVENO_PROCESS_PORTS.push(httpPort); HAVENO_PROCESS_PORTS.push(httpPort);
ctx.port = httpPort; ctx.port = httpPort;
break; break;
@ -2876,8 +2868,8 @@ async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> {
* Release a Haveno process for reuse and try to shutdown. * Release a Haveno process for reuse and try to shutdown.
*/ */
async function releaseHavenoProcess(havenod: HavenoClient, deleteAppDir?: boolean) { async function releaseHavenoProcess(havenod: HavenoClient, deleteAppDir?: boolean) {
GenUtils.remove(HAVENO_PROCESSES, havenod); moneroTs.GenUtils.remove(HAVENO_PROCESSES, havenod);
GenUtils.remove(HAVENO_PROCESS_PORTS, getPort(havenod.getUrl())); moneroTs.GenUtils.remove(HAVENO_PROCESS_PORTS, getPort(havenod.getUrl()));
try { try {
await havenod.shutdownServer(); await havenod.shutdownServer();
} catch (err: any) { } catch (err: any) {
@ -2916,7 +2908,7 @@ async function initHavenoAccount(havenod: HavenoClient, password: string) {
async function initFundingWallet() { async function initFundingWallet() {
// init client connected to monero-wallet-rpc // init client connected to monero-wallet-rpc
fundingWallet = await monerojs.connectToWalletRpc(TestConfig.fundingWallet.url, TestConfig.fundingWallet.username, TestConfig.fundingWallet.password); fundingWallet = await moneroTs.connectToWalletRpc(TestConfig.fundingWallet.url, TestConfig.fundingWallet.username, TestConfig.fundingWallet.password);
// check if wallet is open // check if wallet is open
let walletIsOpen = false let walletIsOpen = false
@ -2934,7 +2926,7 @@ async function initFundingWallet() {
try { try {
await fundingWallet.openWallet({path: TestConfig.fundingWallet.defaultPath, password: TestConfig.fundingWallet.walletPassword}); await fundingWallet.openWallet({path: TestConfig.fundingWallet.defaultPath, password: TestConfig.fundingWallet.walletPassword});
} catch (err: any) { } catch (err: any) {
if (!(err instanceof monerojs.MoneroRpcError)) throw err; if (!(err instanceof moneroTs.MoneroRpcError)) throw err;
// -1 returned when wallet does not exist or fails to open e.g. it's already open by another application // -1 returned when wallet does not exist or fails to open e.g. it's already open by another application
if (err.getCode() === -1) { if (err.getCode() === -1) {
@ -2976,7 +2968,7 @@ async function getWallet(havenod: HavenoClient) {
let wallet: any; let wallet: any;
if (havenod === user1) wallet = user1Wallet; if (havenod === user1) wallet = user1Wallet;
else if (havenod === user2) wallet = user2Wallet; else if (havenod === user2) wallet = user2Wallet;
else wallet = await monerojs.connectToWalletRpc("http://127.0.0.1:" + havenod.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); else wallet = await moneroTs.connectToWalletRpc("http://127.0.0.1:" + havenod.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword);
HAVENO_WALLETS.set(havenod, wallet); HAVENO_WALLETS.set(havenod, wallet);
} }
return HAVENO_WALLETS.get(havenod); return HAVENO_WALLETS.get(havenod);
@ -3008,7 +3000,7 @@ async function mineToHeight(height: number) {
if (await monerod.getHeight() >= height) return; if (await monerod.getHeight() >= height) return;
const miningStarted = await startMining(); const miningStarted = await startMining();
while (await monerod.getHeight() < height) { while (await monerod.getHeight() < height) {
await GenUtils.waitFor(TestConfig.trade.walletSyncPeriodMs); await moneroTs.GenUtils.waitFor(TestConfig.trade.walletSyncPeriodMs);
} }
if (miningStarted) await stopMining(); if (miningStarted) await stopMining();
} }
@ -3048,7 +3040,7 @@ async function waitForAvailableBalance(amount: bigint, ...wallets: any[]) {
// fund wallets with insufficient balance // fund wallets with insufficient balance
let miningNeeded = false; let miningNeeded = false;
const fundConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(true); const fundConfig: moneroTs.MoneroTxConfig = new moneroTs.MoneroTxConfig({accountIndex: 0, relay: true});
for (const wallet of wallets) { for (const wallet of wallets) {
const availableBalance = await wallet.getAvailableBalance(); const availableBalance = await wallet.getAvailableBalance();
if (availableBalance < amount) miningNeeded = true; if (availableBalance < amount) miningNeeded = true;
@ -3079,7 +3071,7 @@ async function waitForAvailableBalance(amount: bigint, ...wallets: any[]) {
} }
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
promises.push(new Promise(async (resolve) => { promises.push(new Promise(async (resolve) => {
const taskLooper: any = new TaskLooper(async function() { const taskLooper: any = new moneroTs.TaskLooper(async function() {
if (await wallet.getAvailableBalance() >= amount) { if (await wallet.getAvailableBalance() >= amount) {
taskLooper.stop(); taskLooper.stop();
resolve(); resolve();
@ -3101,10 +3093,10 @@ async function waitForUnlockedTxs(...txHashes: string[]) {
for (const txHash of txHashes) { for (const txHash of txHashes) {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
promises.push(new Promise(async (resolve) => { promises.push(new Promise(async (resolve) => {
const taskLooper = new TaskLooper(async function() { const taskLooper = new moneroTs.TaskLooper(async function() {
const tx = await monerod.getTx(txHash); const tx = await monerod.getTx(txHash);
if (!tx) HavenoUtils.log(1, "WARNING: tx hash " + txHash + " not found"); if (!tx) HavenoUtils.log(1, "WARNING: tx hash " + txHash + " not found");
else if (tx.isConfirmed() && tx.getBlock().getHeight() <= await monerod.getHeight() - 10) { else if (tx.getIsConfirmed() && tx.getBlock().getHeight() <= await monerod.getHeight() - 10) {
taskLooper.stop(); taskLooper.stop();
resolve(); resolve();
} }
@ -3128,7 +3120,7 @@ async function waitForUnlockedTxs(...txHashes: string[]) {
async function hasUnspentOutputs(wallets: any[], amt: BigInt, numOutputs?: number, isLocked?: boolean): Promise<boolean> { async function hasUnspentOutputs(wallets: any[], amt: BigInt, numOutputs?: number, isLocked?: boolean): Promise<boolean> {
if (numOutputs === undefined) numOutputs = 1; if (numOutputs === undefined) numOutputs = 1;
for (const wallet of wallets) { for (const wallet of wallets) {
const unspentOutputs = await wallet.getOutputs({isSpent: false, isFrozen: false, minAmount: monerojs.BigInteger(amt.toString()), txQuery: {isLocked: isLocked}}); const unspentOutputs = await wallet.getOutputs({isSpent: false, isFrozen: false, minAmount: BigInt(amt.toString()), txQuery: {isLocked: isLocked}});
if (unspentOutputs.length < numOutputs) return false; if (unspentOutputs.length < numOutputs) return false;
} }
return true; return true;
@ -3139,36 +3131,36 @@ async function hasUnspentOutputs(wallets: any[], amt: BigInt, numOutputs?: numbe
* *
* @param {MoneroWallet} wallets - monerojs wallets * @param {MoneroWallet} wallets - monerojs wallets
* @param {BigInt} amt - the amount to fund * @param {BigInt} amt - the amount to fund
* @param {number?} numOutputs - the number of outputs of the given amount (default 1) * @param {number} [numOutputs] - the number of outputs of the given amount (default 1)
* @param {boolean?} waitForUnlock - wait for outputs to unlock (default false) * @param {boolean} [waitForUnlock] - wait for outputs to unlock (default false)
*/ */
async function fundOutputs(wallets: any[], amt: bigint, numOutputs?: number, waitForUnlock?: boolean): Promise<void> { async function fundOutputs(wallets: any[], amt: bigint, numOutputs?: number, waitForUnlock?: boolean): Promise<void> {
if (numOutputs === undefined) numOutputs = 1; if (numOutputs === undefined) numOutputs = 1;
if (waitForUnlock === undefined) waitForUnlock = true; if (waitForUnlock === undefined) waitForUnlock = true;
// collect destinations // collect destinations
const destinations: any[] = []; const destinations: moneroTs.MoneroDestination[] = [];
for (const wallet of wallets) { for (const wallet of wallets) {
if (await hasUnspentOutputs([wallet], amt, numOutputs, undefined)) continue; if (await hasUnspentOutputs([wallet], amt, numOutputs, undefined)) continue;
for (let i = 0; i < numOutputs; i++) { for (let i = 0; i < numOutputs; i++) {
destinations.push(new MoneroDestination((await wallet.createSubaddress()).getAddress(), monerojs.BigInteger(amt.toString()))); destinations.push(new moneroTs.MoneroDestination((await wallet.createSubaddress()).getAddress(), BigInt(amt.toString())));
} }
} }
if (!destinations.length) return; if (!destinations.length) return;
// fund destinations // fund destinations
let txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(true); let txConfig = new moneroTs.MoneroTxConfig().setAccountIndex(0).setRelay(true);
const txHashes: string[] = []; const txHashes: string[] = [];
let sendAmt = BigInteger("0"); let sendAmt = 0n;
for (let i = 0; i < destinations.length; i++) { for (let i = 0; i < destinations.length; i++) {
txConfig.addDestination(destinations[i]); txConfig.addDestination(destinations[i], undefined); // TODO: remove once converted to MoneroTxConfig.ts
sendAmt = sendAmt.add(destinations[i].getAmount()); sendAmt = sendAmt + destinations[i].getAmount();
if (i === destinations.length - 1 || (i > 0 && i % 15 === 0)) { if (i === destinations.length - 1 || (i > 0 && i % 15 === 0)) {
await waitForAvailableBalance(toBigInt(sendAmt), fundingWallet); await waitForAvailableBalance(toBigInt(sendAmt), fundingWallet);
const txs = await fundingWallet.createTxs(txConfig); const txs = await fundingWallet.createTxs(txConfig);
for (const tx of txs) txHashes.push(tx.getHash()); for (const tx of txs) txHashes.push(tx.getHash());
txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(true); txConfig = new moneroTs.MoneroTxConfig().setAccountIndex(0).setRelay(true);
sendAmt = BigInteger("0"); sendAmt = 0n;
} }
} }
@ -3296,7 +3288,7 @@ function testDestination(destination: XmrDestination) {
} }
function getRandomAssetCode() { function getRandomAssetCode() {
return TestConfig.assetCodes[GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)]; return TestConfig.assetCodes[moneroTs.GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)];
} }
async function hasPaymentAccount(trader: HavenoClient, assetCode: string): Promise<boolean> { async function hasPaymentAccount(trader: HavenoClient, assetCode: string): Promise<boolean> {
@ -3328,7 +3320,7 @@ async function createCryptoPaymentAccount(trader: HavenoClient, currencyCode = "
for (const cryptoAddress of TestConfig.cryptoAddresses) { for (const cryptoAddress of TestConfig.cryptoAddresses) {
if (cryptoAddress.currencyCode.toLowerCase() !== currencyCode.toLowerCase()) continue; if (cryptoAddress.currencyCode.toLowerCase() !== currencyCode.toLowerCase()) continue;
return trader.createCryptoPaymentAccount( return trader.createCryptoPaymentAccount(
cryptoAddress.currencyCode + " " + cryptoAddress.address.substr(0, 8) + "... " + GenUtils.getUUID(), cryptoAddress.currencyCode + " " + cryptoAddress.address.substr(0, 8) + "... " + moneroTs.GenUtils.getUUID(),
cryptoAddress.currencyCode, cryptoAddress.currencyCode,
cryptoAddress.address); cryptoAddress.address);
} }
@ -3402,7 +3394,7 @@ function getValidFormInput(form: PaymentAccountForm, fieldId: PaymentAccountForm
case PaymentAccountFormField.FieldId.ACCOUNT_ID: case PaymentAccountFormField.FieldId.ACCOUNT_ID:
return "jdoe@no.com"; return "jdoe@no.com";
case PaymentAccountFormField.FieldId.ACCOUNT_NAME: case PaymentAccountFormField.FieldId.ACCOUNT_NAME:
return "Form_" + form.getId() + " " + GenUtils.getUUID(); // TODO: rename to form.getPaymentMethodId() return "Form_" + form.getId() + " " + moneroTs.GenUtils.getUUID(); // TODO: rename to form.getPaymentMethodId()
case PaymentAccountFormField.FieldId.ACCOUNT_NR: case PaymentAccountFormField.FieldId.ACCOUNT_NR:
return "12345678"; return "12345678";
case PaymentAccountFormField.FieldId.ACCOUNT_OWNER: case PaymentAccountFormField.FieldId.ACCOUNT_OWNER:
@ -3503,7 +3495,7 @@ function getValidFormInput(form: PaymentAccountForm, fieldId: PaymentAccountForm
return "asap plz"; return "asap plz";
case PaymentAccountFormField.FieldId.STATE: case PaymentAccountFormField.FieldId.STATE:
const country = HavenoUtils.getFormValue(form, PaymentAccountFormField.FieldId.COUNTRY); const country = HavenoUtils.getFormValue(form, PaymentAccountFormField.FieldId.COUNTRY);
return GenUtils.arrayContains(field.getRequiredForCountriesList(), country) ? "My state" : ""; return moneroTs.GenUtils.arrayContains(field.getRequiredForCountriesList(), country) ? "My state" : "";
case PaymentAccountFormField.FieldId.TRADE_CURRENCIES: case PaymentAccountFormField.FieldId.TRADE_CURRENCIES:
if (field.getComponent() === PaymentAccountFormField.Component.SELECT_ONE) { if (field.getComponent() === PaymentAccountFormField.Component.SELECT_ONE) {
if (form.getId() === PaymentAccountForm.FormId.F2F) return "XAU"; if (form.getId() === PaymentAccountForm.FormId.F2F) return "XAU";
@ -3638,7 +3630,7 @@ function getInvalidFormInput(form: PaymentAccountForm, fieldId: PaymentAccountFo
throw new Error("Special instructions have no invalid input"); throw new Error("Special instructions have no invalid input");
case PaymentAccountFormField.FieldId.STATE: { case PaymentAccountFormField.FieldId.STATE: {
const country = HavenoUtils.getFormValue(form, PaymentAccountFormField.FieldId.COUNTRY); const country = HavenoUtils.getFormValue(form, PaymentAccountFormField.FieldId.COUNTRY);
return GenUtils.arrayContains(field.getRequiredForCountriesList(), country) ? "" : "My state"; return moneroTs.GenUtils.arrayContains(field.getRequiredForCountriesList(), country) ? "" : "My state";
} }
case PaymentAccountFormField.FieldId.TRADE_CURRENCIES: case PaymentAccountFormField.FieldId.TRADE_CURRENCIES:
return "abc,def"; return "abc,def";

View File

@ -118,12 +118,11 @@ export default class HavenoUtils {
/** /**
* Convert XMR to atomic units. * Convert XMR to atomic units.
* *
* @param {number|string} amountXmr - amount in XMR to convert to atomic units * @param {number | string} amountXmr - amount in XMR to convert to atomic units
* @return {BigInt} amount in atomic units * @return {bigint} amount in atomic units
*/ */
static xmrToAtomicUnits(amountXmr: number|string): bigint { static xmrToAtomicUnits(amountXmr: number | string): bigint {
if (typeof amountXmr === "number") amountXmr = "" + amountXmr; if (typeof amountXmr === "number") amountXmr = "" + amountXmr;
else if (typeof amountXmr !== "string") throw new Error("Must provide XMR amount as a string or js number to convert to atomic units");
let decimalDivisor = 1; let decimalDivisor = 1;
let decimalIdx = amountXmr.indexOf('.'); let decimalIdx = amountXmr.indexOf('.');
if (decimalIdx > -1) { if (decimalIdx > -1) {
@ -136,14 +135,13 @@ export default class HavenoUtils {
/** /**
* Convert atomic units to XMR. * Convert atomic units to XMR.
* *
* @param {BigInt|string} amountAtomicUnits - amount in atomic units to convert to XMR * @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR
* @return {number} amount in XMR * @return {number} amount in XMR
*/ */
static atomicUnitsToXmr(amountAtomicUnits: BigInt|string) { static atomicUnitsToXmr(amountAtomicUnits: bigint | string) {
if (typeof amountAtomicUnits === "string") amountAtomicUnits = BigInt(amountAtomicUnits); if (typeof amountAtomicUnits === "string") amountAtomicUnits = BigInt(amountAtomicUnits);
else if (typeof amountAtomicUnits !== "bigint") throw new Error("Must provide atomic units as BigInt or string to convert to XMR"); const quotient: bigint = amountAtomicUnits / HavenoUtils.AU_PER_XMR;
const quotient: bigint = amountAtomicUnits as bigint / HavenoUtils.AU_PER_XMR; const remainder: bigint = amountAtomicUnits % HavenoUtils.AU_PER_XMR;
const remainder: bigint = amountAtomicUnits as bigint % HavenoUtils.AU_PER_XMR;
return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR); return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR);
} }

View File

@ -25,6 +25,7 @@ export default class TaskLooper {
* @param {int} periodInMs the loop period in milliseconds * @param {int} periodInMs the loop period in milliseconds
*/ */
start(periodInMs: number) { start(periodInMs: number) {
if (periodInMs <= 0) throw new Error("Looper period must be greater than 0 ms");
if (this._isStarted) return; if (this._isStarted) return;
this._isStarted = true; this._isStarted = true;
this._runLoop(periodInMs); this._runLoop(periodInMs);

View File

@ -19,8 +19,7 @@
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true
"jsx": "react-jsx"
}, },
"include": ["src"], "include": ["src"],
"exclude": ["node_modules", "**/*.test.ts"] "exclude": ["node_modules", "**/*.test.ts"]