test random trade amounts, fix conversion utils using decimal.js

This commit is contained in:
woodser 2024-04-07 08:13:09 -04:00
parent b4a5749378
commit b304139779
18 changed files with 1790 additions and 1143 deletions

15
dist/HavenoClient.js vendored
View File

@ -1,4 +1,19 @@
"use strict";
/*
* Copyright Haveno
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};

File diff suppressed because one or more lines are too long

View File

@ -1603,11 +1603,14 @@ export class OfferInfo extends jspb.Message {
getMinAmount(): string;
setMinAmount(value: string): OfferInfo;
getVolume(): string;
setVolume(value: string): OfferInfo;
getMakerFeePct(): number;
setMakerFeePct(value: number): OfferInfo;
getMinVolume(): string;
setMinVolume(value: string): OfferInfo;
getTakerFeePct(): number;
setTakerFeePct(value: number): OfferInfo;
getPenaltyFeePct(): number;
setPenaltyFeePct(value: number): OfferInfo;
getBuyerSecurityDepositPct(): number;
setBuyerSecurityDepositPct(value: number): OfferInfo;
@ -1615,6 +1618,12 @@ export class OfferInfo extends jspb.Message {
getSellerSecurityDepositPct(): number;
setSellerSecurityDepositPct(value: number): OfferInfo;
getVolume(): string;
setVolume(value: string): OfferInfo;
getMinVolume(): string;
setMinVolume(value: string): OfferInfo;
getTriggerPrice(): string;
setTriggerPrice(value: string): OfferInfo;
@ -1639,9 +1648,6 @@ export class OfferInfo extends jspb.Message {
getState(): string;
setState(value: string): OfferInfo;
getMakerFee(): string;
setMakerFee(value: string): OfferInfo;
getIsActivated(): boolean;
setIsActivated(value: boolean): OfferInfo;
@ -1686,10 +1692,13 @@ export namespace OfferInfo {
marketPriceMarginPct: number,
amount: string,
minAmount: string,
volume: string,
minVolume: string,
makerFeePct: number,
takerFeePct: number,
penaltyFeePct: number,
buyerSecurityDepositPct: number,
sellerSecurityDepositPct: number,
volume: string,
minVolume: string,
triggerPrice: string,
paymentAccountId: string,
paymentMethodId: string,
@ -1698,7 +1707,6 @@ export namespace OfferInfo {
counterCurrencyCode: string,
date: number,
state: string,
makerFee: string,
isActivated: boolean,
isMyOffer: boolean,
ownerNodeAddress: string,
@ -2647,12 +2655,15 @@ export class TradeInfo extends jspb.Message {
getRole(): string;
setRole(value: string): TradeInfo;
getTakerFee(): string;
setTakerFee(value: string): TradeInfo;
getAmount(): string;
setAmount(value: string): TradeInfo;
getMakerFee(): string;
setMakerFee(value: string): TradeInfo;
getTakerFee(): string;
setTakerFee(value: string): TradeInfo;
getBuyerSecurityDeposit(): string;
setBuyerSecurityDeposit(value: string): TradeInfo;
@ -2763,8 +2774,9 @@ export namespace TradeInfo {
shortId: string,
date: number,
role: string,
takerFee: string,
amount: string,
makerFee: string,
takerFee: string,
buyerSecurityDeposit: string,
sellerSecurityDeposit: string,
buyerDepositTxFee: string,

1038
dist/protobuf/grpc_pb.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -191,6 +191,11 @@ export class NetworkEnvelope extends jspb.Message {
hasMediatedPayoutTxPublishedMessage(): boolean;
clearMediatedPayoutTxPublishedMessage(): NetworkEnvelope;
getFileTransferPart(): FileTransferPart | undefined;
setFileTransferPart(value?: FileTransferPart): NetworkEnvelope;
hasFileTransferPart(): boolean;
clearFileTransferPart(): NetworkEnvelope;
getMessageCase(): NetworkEnvelope.MessageCase;
serializeBinary(): Uint8Array;
@ -241,6 +246,7 @@ export namespace NetworkEnvelope {
chatMessage?: ChatMessage.AsObject,
mediatedPayoutTxSignatureMessage?: MediatedPayoutTxSignatureMessage.AsObject,
mediatedPayoutTxPublishedMessage?: MediatedPayoutTxPublishedMessage.AsObject,
fileTransferPart?: FileTransferPart.AsObject,
}
export enum MessageCase {
@ -282,6 +288,7 @@ export namespace NetworkEnvelope {
CHAT_MESSAGE = 36,
MEDIATED_PAYOUT_TX_SIGNATURE_MESSAGE = 37,
MEDIATED_PAYOUT_TX_PUBLISHED_MESSAGE = 38,
FILE_TRANSFER_PART = 39,
}
}
@ -417,6 +424,48 @@ export namespace GetUpdatedDataRequest {
}
}
export class FileTransferPart extends jspb.Message {
getSenderNodeAddress(): NodeAddress | undefined;
setSenderNodeAddress(value?: NodeAddress): FileTransferPart;
hasSenderNodeAddress(): boolean;
clearSenderNodeAddress(): FileTransferPart;
getUid(): string;
setUid(value: string): FileTransferPart;
getTradeId(): string;
setTradeId(value: string): FileTransferPart;
getTraderId(): number;
setTraderId(value: number): FileTransferPart;
getSeqNumOrFileLength(): number;
setSeqNumOrFileLength(value: number): FileTransferPart;
getMessageData(): Uint8Array | string;
getMessageData_asU8(): Uint8Array;
getMessageData_asB64(): string;
setMessageData(value: Uint8Array | string): FileTransferPart;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FileTransferPart.AsObject;
static toObject(includeInstance: boolean, msg: FileTransferPart): FileTransferPart.AsObject;
static serializeBinaryToWriter(message: FileTransferPart, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FileTransferPart;
static deserializeBinaryFromReader(message: FileTransferPart, reader: jspb.BinaryReader): FileTransferPart;
}
export namespace FileTransferPart {
export type AsObject = {
senderNodeAddress?: NodeAddress.AsObject,
uid: string,
tradeId: string,
traderId: number,
seqNumOrFileLength: number,
messageData: Uint8Array | string,
}
}
export class GetPeersRequest extends jspb.Message {
getSenderNodeAddress(): NodeAddress | undefined;
setSenderNodeAddress(value?: NodeAddress): GetPeersRequest;
@ -962,8 +1011,8 @@ export namespace PrefixedSealedAndSignedMessage {
}
export class InitTradeRequest extends jspb.Message {
getTradeId(): string;
setTradeId(value: string): InitTradeRequest;
getOfferId(): string;
setOfferId(value: string): InitTradeRequest;
getSenderNodeAddress(): NodeAddress | undefined;
setSenderNodeAddress(value?: NodeAddress): InitTradeRequest;
@ -981,9 +1030,6 @@ export class InitTradeRequest extends jspb.Message {
getTradePrice(): number;
setTradePrice(value: number): InitTradeRequest;
getTradeFee(): number;
setTradeFee(value: number): InitTradeRequest;
getAccountId(): string;
setAccountId(value: string): InitTradeRequest;
@ -1046,12 +1092,11 @@ export class InitTradeRequest extends jspb.Message {
export namespace InitTradeRequest {
export type AsObject = {
tradeId: string,
offerId: string,
senderNodeAddress?: NodeAddress.AsObject,
pubKeyRing?: PubKeyRing.AsObject,
tradeAmount: number,
tradePrice: number,
tradeFee: number,
accountId: string,
paymentAccountId: string,
paymentMethodId: string,
@ -2736,6 +2781,21 @@ export class OfferPayload extends jspb.Message {
getMinAmount(): number;
setMinAmount(value: number): OfferPayload;
getMakerFeePct(): number;
setMakerFeePct(value: number): OfferPayload;
getTakerFeePct(): number;
setTakerFeePct(value: number): OfferPayload;
getPenaltyFeePct(): number;
setPenaltyFeePct(value: number): OfferPayload;
getBuyerSecurityDepositPct(): number;
setBuyerSecurityDepositPct(value: number): OfferPayload;
getSellerSecurityDepositPct(): number;
setSellerSecurityDepositPct(value: number): OfferPayload;
getBaseCurrencyCode(): string;
setBaseCurrencyCode(value: string): OfferPayload;
@ -2770,15 +2830,6 @@ export class OfferPayload extends jspb.Message {
getBlockHeightAtOfferCreation(): number;
setBlockHeightAtOfferCreation(value: number): OfferPayload;
getMakerFee(): number;
setMakerFee(value: number): OfferPayload;
getBuyerSecurityDepositPct(): number;
setBuyerSecurityDepositPct(value: number): OfferPayload;
getSellerSecurityDepositPct(): number;
setSellerSecurityDepositPct(value: number): OfferPayload;
getMaxTradeLimit(): number;
setMaxTradeLimit(value: number): OfferPayload;
@ -2844,6 +2895,11 @@ export namespace OfferPayload {
useMarketBasedPrice: boolean,
amount: number,
minAmount: number,
makerFeePct: number,
takerFeePct: number,
penaltyFeePct: number,
buyerSecurityDepositPct: number,
sellerSecurityDepositPct: number,
baseCurrencyCode: string,
counterCurrencyCode: string,
paymentMethodId: string,
@ -2854,9 +2910,6 @@ export namespace OfferPayload {
acceptedBankIdsList: Array<string>,
versionNr: string,
blockHeightAtOfferCreation: number,
makerFee: number,
buyerSecurityDepositPct: number,
sellerSecurityDepositPct: number,
maxTradeLimit: number,
maxTradePeriod: number,
useAutoClose: boolean,
@ -6027,9 +6080,6 @@ export class Trade extends jspb.Message {
getAmount(): number;
setAmount(value: number): Trade;
getTakerFee(): number;
setTakerFee(value: number): Trade;
getTakeOfferDate(): number;
setTakeOfferDate(value: number): Trade;
@ -6124,7 +6174,6 @@ export namespace Trade {
payoutTxHex: string,
payoutTxKey: string,
amount: number,
takerFee: number,
takeOfferDate: number,
price: number,
state: Trade.State,

1563
dist/protobuf/pb_pb.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,19 @@
"use strict";
/*
* Copyright Haveno
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Haveno error with message and code.

View File

@ -1 +1 @@
{"version":3,"file":"HavenoError.js","sourceRoot":"","sources":["../../src/types/HavenoError.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,MAAqB,WAAY,SAAQ,KAAK;IAI5C;;;;;OAKG;IACH,YAAY,GAAW,EAAE,IAAa;QACpC,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAdD,8BAcC"}
{"version":3,"file":"HavenoError.js","sourceRoot":"","sources":["../../src/types/HavenoError.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAGH;;GAEG;AACH,MAAqB,WAAY,SAAQ,KAAK;IAI5C;;;;;OAKG;IACH,YAAY,GAAW,EAAE,IAAa;QACpC,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAdD,8BAcC"}

View File

@ -1,10 +1,26 @@
"use strict";
/*
* Copyright Haveno
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const console_1 = __importDefault(require("console"));
const decimal_js_1 = __importDefault(require("decimal.js"));
const pb_pb_1 = require("../protobuf/pb_pb");
/**
* Collection of utilities for working with Haveno.
@ -132,7 +148,7 @@ class HavenoUtils {
amountAtomicUnits = BigInt(amountAtomicUnits);
const quotient = amountAtomicUnits / HavenoUtils.AU_PER_XMR;
const remainder = amountAtomicUnits % HavenoUtils.AU_PER_XMR;
return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR);
return new decimal_js_1.default(quotient.toString()).plus(new decimal_js_1.default(remainder.toString()).div(HavenoUtils.AU_PER_XMR.toString())).toNumber();
}
// ------------------------- PAYMENT ACCOUNT FORMS --------------------------
/**

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,19 @@
"use strict";
/*
* Copyright Haveno
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Run a task in a fixed period loop.

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,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"}
{"version":3,"file":"TaskLooper.js","sourceRoot":"","sources":["../../src/utils/TaskLooper.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAGH;;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"}

1
package-lock.json generated
View File

@ -22,6 +22,7 @@
"@typescript-eslint/eslint-plugin": "5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"babel-jest": "^29.4.3",
"decimal.js": "^10.4.3",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.6.0",
"eslint-import-resolver-typescript": "^3.5.3",

View File

@ -48,6 +48,7 @@
"@typescript-eslint/eslint-plugin": "5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"babel-jest": "^29.4.3",
"decimal.js": "^10.4.3",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.6.0",
"eslint-import-resolver-typescript": "^3.5.3",

View File

@ -125,7 +125,7 @@ const defaultTradeConfig: Partial<TradeContext> = {
takeOffer: true,
awaitFundsToMakeOffer: true,
direction: OfferDirection.BUY, // buy or sell xmr
offerAmount: HavenoUtils.xmrToAtomicUnits(.2), // amount of xmr to trade
offerAmount: 193312996088n,
offerMinAmount: undefined,
assetCode: "usd", // counter asset to trade
makerPaymentAccountId: undefined,
@ -309,8 +309,12 @@ class TradeContext {
str += "\nSecurity deposit percent: " + this.securityDepositPct;
str += "\nMaker balance before offer: " + this.maker.balancesBeforeOffer?.getBalance();
str += "\nMaker split output tx fee: " + this.maker.splitOutputTxFee;
if (this.offer) str += "\nMaker trade fee: " + this.offer!.getMakerFee();
if (this.offer) {
str += "\nMaker fee percent: " + this.offer!.getMakerFeePct();
str += "\nTaker fee percent: " + this.offer!.getTakerFeePct();
}
if (this.arbitrator && this.arbitrator!.trade) {
str += "\nMaker trade fee: " + this.arbitrator?.trade?.getMakerFee();
str += "\nMaker deposit tx id: " + this.arbitrator!.trade!.getMakerDepositTxId();
if (this.arbitrator!.trade!.getMakerDepositTxId()) {
let tx = await monerod.getTx(this.arbitrator!.trade!.getMakerDepositTxId());
@ -1612,11 +1616,14 @@ test("Can complete a trade within a range", async () => {
let takerPaymentAccount = await createPaymentAccount(user2, assetCode, paymentMethodId);
// execute trade
const offerAmount = HavenoUtils.xmrToAtomicUnits(2);
const offerMinAmount = HavenoUtils.xmrToAtomicUnits(.15);
const tradeAmount = getRandomBigIntWithinRange(offerMinAmount, offerAmount);
await executeTrade({
price: 142.23,
offerAmount: HavenoUtils.xmrToAtomicUnits(2),
offerMinAmount: HavenoUtils.xmrToAtomicUnits(.15),
tradeAmount: HavenoUtils.xmrToAtomicUnits(1),
offerAmount: offerAmount,
offerMinAmount: offerMinAmount,
tradeAmount: tradeAmount,
testPayoutUnlocked: true, // override to test unlock
makerPaymentAccountId: makerPaymentAccount.getId(),
takerPaymentAccountId: takerPaymentAccount.getId(),
@ -1627,10 +1634,11 @@ test("Can complete a trade within a range", async () => {
test("Can complete trades at the same time (CI, sanity check)", async () => {
// create trade contexts with customized payment methods
// create trade contexts with customized payment methods and random amounts
const ctxs = getTradeContexts(TestConfig.assetCodes.length);
for (let i = 0; i < ctxs.length; i++) {
ctxs[i].assetCode = TestConfig.assetCodes[i]; // test each asset code
ctxs[i].offerAmount = getRandomBigIntWithinPercent(TestConfig.trade.offerAmount!, 0.15);
let paymentMethodId;
if (ctxs[i].assetCode === "USD") paymentMethodId = "zelle";
if (ctxs[i].assetCode === "EUR") paymentMethodId = "revolut";
@ -1666,7 +1674,8 @@ test("Can complete all trade combinations (stress)", async () => {
buyerDisputeContext: BUYER_DISPUTE_OPTS[k],
sellerDisputeContext: SELLER_DISPUTE_OPTS[l],
disputeWinner: DISPUTE_WINNER_OPTS[m],
disputeSummary: "After much deliberation, " + (DISPUTE_WINNER_OPTS[m] === DisputeResult.Winner.BUYER ? "buyer" : "seller") + " is winner"
disputeSummary: "After much deliberation, " + (DISPUTE_WINNER_OPTS[m] === DisputeResult.Winner.BUYER ? "buyer" : "seller") + " is winner",
offerAmount: getRandomBigIntWithinPercent(TestConfig.trade.offerAmount!, 0.15)
};
ctxs.push(Object.assign({}, new TradeContext(TestConfig.trade), ctx));
}
@ -2768,7 +2777,7 @@ async function takeOffer(ctxP: Partial<TradeContext>): Promise<TradeInfo> {
ctx.taker.balancesAfterTake = await ctx.taker.havenod!.getBalances();
ctx.maker.depositTxFee = BigInt(ctx.maker.depositTx!.getFee());
ctx.taker.depositTxFee = BigInt(ctx.taker.depositTx!.getFee());
ctx.maker.tradeFee = BigInt(trade.getOffer()!.getMakerFee());
ctx.maker.tradeFee = BigInt(trade.getMakerFee());
ctx.taker.tradeFee = BigInt(trade.getTakerFee());
ctx.getBuyer().securityDepositActual = BigInt(trade.getBuyerSecurityDeposit()!);
ctx.getSeller().securityDepositActual = BigInt(trade.getSellerSecurityDeposit()!);
@ -2832,7 +2841,7 @@ async function testTrade(trade: TradeInfo, ctx: TradeContext) {
expect(BigInt(trade.getAmount())).toEqual(ctx!.tradeAmount);
// test security deposit = max(.1, trade amount * security deposit pct)
const expectedSecurityDeposit = HavenoUtils.xmrToAtomicUnits(Math.max(.1, HavenoUtils.atomicUnitsToXmr(ctx.tradeAmount!) * ctx.securityDepositPct!));
const expectedSecurityDeposit = HavenoUtils.max(HavenoUtils.xmrToAtomicUnits(.1), HavenoUtils.multiply(ctx.tradeAmount!, ctx.securityDepositPct!));
expect(BigInt(trade.getBuyerSecurityDeposit())).toEqual(expectedSecurityDeposit - ctx.getBuyer().depositTxFee);
expect(BigInt(trade.getSellerSecurityDeposit())).toEqual(expectedSecurityDeposit - ctx.getSeller().depositTxFee);
@ -3772,6 +3781,18 @@ function testDestination(destination: XmrDestination) {
expect(BigInt(destination.getAmount())).toBeGreaterThan(0n);
}
function getRandomBigIntWithinPercent(base: bigint, percent: number): bigint {
return getRandomBigIntWithinRange(base - multiply(base, percent), base + multiply(base, percent));
}
function multiply(amount: bigint, multiplier: number): bigint {
return BigInt(Math.round(Number(amount) * multiplier));
}
function getRandomBigIntWithinRange(min: bigint, max: bigint): bigint {
return BigInt(Math.floor(Math.random() * (Number(max) - Number(min))) + Number(min));
}
function getRandomAssetCode() {
return TestConfig.assetCodes[moneroTs.GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)];
}

View File

@ -17,6 +17,7 @@
import assert from "assert";
import console from "console";
import Decimal from 'decimal.js';
import { PaymentAccountForm, PaymentAccountFormField } from "../protobuf/pb_pb";
/**
@ -101,14 +102,45 @@ export default class HavenoUtils {
}
/**
* Divide one bigint by another.
* Convert XMR to atomic units.
*
* @param {bigint} a dividend
* @param {bigint} b divisor
* @param {number | string} amountXmr - amount in XMR to convert to atomic units
* @return {bigint} amount in atomic units
*/
static xmrToAtomicUnits(amountXmr: number | string): bigint {
return BigInt(new Decimal(amountXmr).mul(HavenoUtils.AU_PER_XMR.toString()).toFixed(0));
}
/**
* Convert atomic units to XMR.
*
* @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR
* @return {number} amount in XMR
*/
static atomicUnitsToXmr(amountAtomicUnits: bigint | string): number {
return new Decimal(amountAtomicUnits.toString()).div(HavenoUtils.AU_PER_XMR.toString()).toNumber();
}
/**
* Divide one atomic units by another.
*
* @param {bigint} au1 dividend
* @param {bigint} au2 divisor
* @returns {number} the result
*/
static divideBI(a: bigint, b: bigint): number {
return Number(a * 10000000000000n / b) / 10000000000000;
static divide(au1: bigint, au2: bigint): number {
return this.atomicUnitsToXmr(au1) / this.atomicUnitsToXmr(au2);
}
/**
* Multiply a bigint by a number or bigint.
*
* @param a bigint to multiply
* @param b bigint or number to multiply by
* @returns the product as a bigint
*/
static multiply(a: bigint, b: number | bigint): bigint {
return BigInt((new Decimal(a.toString()).mul(new Decimal(b.toString())).toFixed(0)));
}
/**
@ -119,7 +151,7 @@ export default class HavenoUtils {
* @returns {number} the percentage difference as a float
*/
static percentageDiff(a: bigint, b: bigint): number {
return HavenoUtils.divideBI(a - b, a);
return HavenoUtils.divide(a - b, a);
}
/**
@ -133,33 +165,14 @@ export default class HavenoUtils {
}
/**
* Convert XMR to atomic units.
* Return the maximum of two bigints.
*
* @param {number | string} amountXmr - amount in XMR to convert to atomic units
* @return {bigint} amount in atomic units
* @param {bigint} bi1 first bigint
* @param {bigint} bi2 second bigint
* @returns {bigint} the maximum of the two bigints
*/
static xmrToAtomicUnits(amountXmr: number | string): bigint {
if (typeof amountXmr === "number") amountXmr = "" + amountXmr;
let decimalDivisor = 1;
let decimalIdx = amountXmr.indexOf('.');
if (decimalIdx > -1) {
decimalDivisor = Math.pow(10, amountXmr.length - decimalIdx - 1);
amountXmr = amountXmr.slice(0, decimalIdx) + amountXmr.slice(decimalIdx + 1);
}
return BigInt(amountXmr) * BigInt(HavenoUtils.AU_PER_XMR) / BigInt(decimalDivisor);
}
/**
* Convert atomic units to XMR.
*
* @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR
* @return {number} amount in XMR
*/
static atomicUnitsToXmr(amountAtomicUnits: bigint | string) {
if (typeof amountAtomicUnits === "string") amountAtomicUnits = BigInt(amountAtomicUnits);
const quotient: bigint = amountAtomicUnits / HavenoUtils.AU_PER_XMR;
const remainder: bigint = amountAtomicUnits % HavenoUtils.AU_PER_XMR;
return Number(quotient) + Number(remainder) / Number(HavenoUtils.AU_PER_XMR);
static max(bi1: bigint, bi2: bigint): bigint {
return bi1 > bi2 ? bi1 : bi2;
}
// ------------------------- PAYMENT ACCOUNT FORMS --------------------------