mirror of
https://github.com/haveno-dex/haveno-ts.git
synced 2024-12-25 23:39:38 -05:00
test support buying xmr without deposit or fee using passphrase
This commit is contained in:
parent
8153371cf2
commit
7b88f7d261
@ -63,7 +63,7 @@ enum BaseCurrencyNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clients
|
// clients
|
||||||
const startupHavenods: HavenoClient[] = [];
|
const startupHavenodUrls: string[] = [];
|
||||||
let arbitrator: HavenoClient;
|
let arbitrator: HavenoClient;
|
||||||
let user1: HavenoClient;
|
let user1: HavenoClient;
|
||||||
let user2: HavenoClient;
|
let user2: HavenoClient;
|
||||||
@ -148,7 +148,9 @@ const defaultTradeConfig: Partial<TradeContext> = {
|
|||||||
stopOnFailure: false, // TODO: setting to true can cause error: Http response at 400 or 500 level, http status code: 503
|
stopOnFailure: false, // TODO: setting to true can cause error: Http response at 400 or 500 level, http status code: 503
|
||||||
testPayoutConfirmed: true,
|
testPayoutConfirmed: true,
|
||||||
testPayoutUnlocked: false,
|
testPayoutUnlocked: false,
|
||||||
maxConcurrency: getMaxConcurrency()
|
maxConcurrency: getMaxConcurrency(),
|
||||||
|
isPrivateOffer: false,
|
||||||
|
buyerAsTakerWithoutDeposit: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,11 +190,14 @@ class TradeContext {
|
|||||||
priceMargin?: number;
|
priceMargin?: number;
|
||||||
triggerPrice?: number;
|
triggerPrice?: number;
|
||||||
reserveExactAmount?: boolean;
|
reserveExactAmount?: boolean;
|
||||||
|
isPrivateOffer?: boolean;
|
||||||
|
buyerAsTakerWithoutDeposit?: boolean; // buyer as taker security deposit is optional for private offers
|
||||||
|
|
||||||
// take offer
|
// take offer
|
||||||
awaitFundsToTakeOffer?: boolean;
|
awaitFundsToTakeOffer?: boolean;
|
||||||
offerId?: string;
|
offerId?: string;
|
||||||
takerPaymentAccountId?: string;
|
takerPaymentAccountId?: string;
|
||||||
|
challenge?: string;
|
||||||
testTraderChat?: boolean;
|
testTraderChat?: boolean;
|
||||||
|
|
||||||
// resolve dispute
|
// resolve dispute
|
||||||
@ -287,6 +292,10 @@ class TradeContext {
|
|||||||
return this.isPaymentReceived ? "PAYMENT_RECEIVED" : this.isPaymentSent ? "PAYMENT_SENT" : "DEPOSITS_UNLOCKED";
|
return this.isPaymentReceived ? "PAYMENT_RECEIVED" : this.isPaymentSent ? "PAYMENT_SENT" : "DEPOSITS_UNLOCKED";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasBuyerAsTakerWithoutDeposit() {
|
||||||
|
return this.getBuyer() === this.getTaker() && this.buyerAsTakerWithoutDeposit;
|
||||||
|
}
|
||||||
|
|
||||||
static init(ctxP: Partial<TradeContext> | undefined): TradeContext {
|
static init(ctxP: Partial<TradeContext> | undefined): TradeContext {
|
||||||
let ctx = ctxP instanceof TradeContext ? ctxP : new TradeContext(ctxP);
|
let ctx = ctxP instanceof TradeContext ? ctxP : new TradeContext(ctxP);
|
||||||
if (!ctx.offerAmount && ctx.tradeAmount) ctx.offerAmount = ctx.tradeAmount;
|
if (!ctx.offerAmount && ctx.tradeAmount) ctx.offerAmount = ctx.tradeAmount;
|
||||||
@ -487,7 +496,7 @@ interface TxContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// track started haveno processes
|
// track started haveno processes
|
||||||
const HAVENO_PROCESSES: HavenoClient[] = [];
|
const HAVENO_CLIENTS: HavenoClient[] = [];
|
||||||
const HAVENO_PROCESS_PORTS: string[] = [];
|
const HAVENO_PROCESS_PORTS: string[] = [];
|
||||||
const HAVENO_WALLETS: Map<HavenoClient, any> = new Map<HavenoClient, any>();
|
const HAVENO_WALLETS: Map<HavenoClient, any> = new Map<HavenoClient, any>();
|
||||||
|
|
||||||
@ -506,6 +515,10 @@ function isGitHubActions() {
|
|||||||
|
|
||||||
jest.setTimeout(TestConfig.testTimeout);
|
jest.setTimeout(TestConfig.testTimeout);
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
HavenoUtils.log(0, "BEFORE TEST \"" + expect.getState().currentTestName + "\"");
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -532,11 +545,15 @@ beforeAll(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start configured haveno daemons
|
// start configured haveno daemons
|
||||||
|
const startupHavenods: HavenoClient[] = [];
|
||||||
const promises: Promise<HavenoClient>[] = [];
|
const promises: Promise<HavenoClient>[] = [];
|
||||||
let err;
|
let err;
|
||||||
for (const config of TestConfig.startupHavenods) promises.push(initHaveno(config));
|
for (const config of TestConfig.startupHavenods) promises.push(initHaveno(config));
|
||||||
for (const settledPromise of await Promise.allSettled(promises)) {
|
for (const settledPromise of await Promise.allSettled(promises)) {
|
||||||
if (settledPromise.status === "fulfilled") startupHavenods.push((settledPromise as PromiseFulfilledResult<HavenoClient>).value);
|
if (settledPromise.status === "fulfilled") {
|
||||||
|
startupHavenods.push((settledPromise as PromiseFulfilledResult<HavenoClient>).value);
|
||||||
|
startupHavenodUrls.push(startupHavenods[startupHavenods.length - 1].getUrl());
|
||||||
|
}
|
||||||
else if (!err) err = new Error((settledPromise as PromiseRejectedResult).reason);
|
else if (!err) err = new Error((settledPromise as PromiseRejectedResult).reason);
|
||||||
}
|
}
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@ -564,8 +581,27 @@ beforeAll(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
afterEach(async () => {
|
||||||
HavenoUtils.log(0, "BEFORE TEST \"" + expect.getState().currentTestName + "\"");
|
|
||||||
|
// collect unreleased clients
|
||||||
|
const unreleasedClients: HavenoClient[] = [];
|
||||||
|
for (const havenod of HAVENO_CLIENTS.slice()) {
|
||||||
|
if (!moneroTs.GenUtils.arrayContains(startupHavenodUrls, havenod.getUrl())) {
|
||||||
|
unreleasedClients.push(havenod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// release clients
|
||||||
|
if (unreleasedClients.length > 0) {
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
HavenoUtils.log(0, unreleasedClients.length + " Haveno clients were not released after test \"" + expect.getState().currentTestName + "\", releasing...");
|
||||||
|
for (const client of unreleasedClients) {
|
||||||
|
HavenoUtils.log(0, "\tUnreleased Haveno client: " + client.getUrl());
|
||||||
|
promises.push(releaseHavenoClient(client));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
HavenoUtils.log(0, "Done releasing " + unreleasedClients.length + " unreleased Haveno clients");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -574,10 +610,10 @@ afterAll(async () => {
|
|||||||
|
|
||||||
async function shutDown() {
|
async function shutDown() {
|
||||||
|
|
||||||
// release haveno processes
|
// release all clients
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
for (const havenod of startupHavenods) {
|
for (const client of HAVENO_CLIENTS.slice()) {
|
||||||
promises.push(havenod.getProcess() ? releaseHavenoProcess(havenod) : havenod.disconnect());
|
promises.push(releaseHavenoClient(client));
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
@ -1221,7 +1257,7 @@ test("Can register as an arbitrator (CI)", async () => {
|
|||||||
test("Can get offers (CI)", async () => {
|
test("Can get offers (CI)", async () => {
|
||||||
for (const assetCode of TestConfig.assetCodes) {
|
for (const assetCode of TestConfig.assetCodes) {
|
||||||
const offers: OfferInfo[] = await user1.getOffers(assetCode);
|
const offers: OfferInfo[] = await user1.getOffers(assetCode);
|
||||||
for (const offer of offers) testOffer(offer);
|
for (const offer of offers) testOffer(offer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1235,7 +1271,7 @@ test("Can get my offers (CI)", async () => {
|
|||||||
for (const assetCode of TestConfig.assetCodes) {
|
for (const assetCode of TestConfig.assetCodes) {
|
||||||
const offers: OfferInfo[] = await user1.getMyOffers(assetCode);
|
const offers: OfferInfo[] = await user1.getMyOffers(assetCode);
|
||||||
for (const offer of offers) {
|
for (const offer of offers) {
|
||||||
testOffer(offer);
|
testOffer(offer, undefined, true);
|
||||||
expect(assetCode).toEqual(isCrypto(assetCode) ? offer.getBaseCurrencyCode() : offer.getCounterCurrencyCode()); // crypto asset codes are base
|
expect(assetCode).toEqual(isCrypto(assetCode) ? offer.getBaseCurrencyCode() : offer.getCounterCurrencyCode()); // crypto asset codes are base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1659,6 +1695,24 @@ test("Cannot post offer exceeding trade limit (CI, sanity check)", async () => {
|
|||||||
if (err.message.indexOf("amount is larger than") < 0) throw err;
|
if (err.message.indexOf("amount is larger than") < 0) throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test posting sell offer above limit without buyer deposit
|
||||||
|
try {
|
||||||
|
await executeTrade({
|
||||||
|
offerAmount: moneroTs.MoneroUtils.xmrToAtomicUnits(1.1), // limit is 1 xmr without deposit or fee
|
||||||
|
offerMinAmount: moneroTs.MoneroUtils.xmrToAtomicUnits(0.25),
|
||||||
|
direction: OfferDirection.SELL,
|
||||||
|
assetCode: assetCode,
|
||||||
|
makerPaymentAccountId: account.getId(),
|
||||||
|
isPrivateOffer: true,
|
||||||
|
buyerAsTakerWithoutDeposit: true,
|
||||||
|
takeOffer: false,
|
||||||
|
price: 142.23
|
||||||
|
});
|
||||||
|
throw new Error("Should have rejected posting offer above trade limit")
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.message.indexOf("amount is larger than") < 0) throw err;
|
||||||
|
}
|
||||||
|
|
||||||
// test that sell limit is higher than buy limit
|
// test that sell limit is higher than buy limit
|
||||||
let offerId = await executeTrade({
|
let offerId = await executeTrade({
|
||||||
offerAmount: 2100000000000n,
|
offerAmount: 2100000000000n,
|
||||||
@ -1670,7 +1724,7 @@ test("Cannot post offer exceeding trade limit (CI, sanity check)", async () => {
|
|||||||
await user1.removeOffer(offerId);
|
await user1.removeOffer(offerId);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Can complete a trade within a range", async () => {
|
test("Can complete a trade within a range and without a buyer deposit", async () => {
|
||||||
|
|
||||||
// create payment accounts
|
// create payment accounts
|
||||||
let paymentMethodId = "cash_at_atm";
|
let paymentMethodId = "cash_at_atm";
|
||||||
@ -1682,7 +1736,7 @@ test("Can complete a trade within a range", async () => {
|
|||||||
const tradeStatisticsPre = await arbitrator.getTradeStatistics();
|
const tradeStatisticsPre = await arbitrator.getTradeStatistics();
|
||||||
|
|
||||||
// execute trade
|
// execute trade
|
||||||
const offerAmount = HavenoUtils.xmrToAtomicUnits(2);
|
const offerAmount = HavenoUtils.xmrToAtomicUnits(1);
|
||||||
const offerMinAmount = HavenoUtils.xmrToAtomicUnits(.15);
|
const offerMinAmount = HavenoUtils.xmrToAtomicUnits(.15);
|
||||||
const tradeAmount = getRandomBigIntWithinRange(offerMinAmount, offerAmount);
|
const tradeAmount = getRandomBigIntWithinRange(offerMinAmount, offerAmount);
|
||||||
const ctx: Partial<TradeContext> = {
|
const ctx: Partial<TradeContext> = {
|
||||||
@ -1693,9 +1747,11 @@ test("Can complete a trade within a range", async () => {
|
|||||||
testPayoutUnlocked: true, // override to test unlock
|
testPayoutUnlocked: true, // override to test unlock
|
||||||
makerPaymentAccountId: makerPaymentAccount.getId(),
|
makerPaymentAccountId: makerPaymentAccount.getId(),
|
||||||
takerPaymentAccountId: takerPaymentAccount.getId(),
|
takerPaymentAccountId: takerPaymentAccount.getId(),
|
||||||
direction: OfferDirection.SELL,
|
|
||||||
assetCode: assetCode,
|
assetCode: assetCode,
|
||||||
testBalanceChangeEndToEnd: true
|
testBalanceChangeEndToEnd: true,
|
||||||
|
direction: OfferDirection.SELL,
|
||||||
|
isPrivateOffer: true,
|
||||||
|
buyerAsTakerWithoutDeposit: true
|
||||||
}
|
}
|
||||||
await executeTrade(ctx);
|
await executeTrade(ctx);
|
||||||
|
|
||||||
@ -1751,7 +1807,7 @@ test("Can complete all trade combinations (stress)", async () => {
|
|||||||
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)
|
offerAmount: getRandomBigIntWithinPercent(TestConfig.trade.offerAmount!, 0.15)
|
||||||
};
|
};
|
||||||
ctxs.push(Object.assign({}, new TradeContext(TestConfig.trade), ctx));
|
ctxs.push(new TradeContext(Object.assign({}, new TradeContext(TestConfig.trade), ctx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1760,7 +1816,12 @@ test("Can complete all trade combinations (stress)", async () => {
|
|||||||
|
|
||||||
// execute trades
|
// execute trades
|
||||||
const ctxIdx = undefined; // run single index for debugging
|
const ctxIdx = undefined; // run single index for debugging
|
||||||
if (ctxIdx !== undefined) ctxs = ctxs.slice(ctxIdx, ctxIdx + 1);
|
if (ctxIdx !== undefined) {
|
||||||
|
ctxs = ctxs.slice(ctxIdx, ctxIdx + 1);
|
||||||
|
HavenoUtils.log(0, "Executing single trade configuration");
|
||||||
|
console.log(ctxs[0]);
|
||||||
|
console.log(await ctxs[0].toSummary());
|
||||||
|
}
|
||||||
HavenoUtils.log(0, "Executing " + ctxs.length + " trade configurations");
|
HavenoUtils.log(0, "Executing " + ctxs.length + " trade configurations");
|
||||||
await executeTrades(ctxs);
|
await executeTrades(ctxs);
|
||||||
});
|
});
|
||||||
@ -1910,7 +1971,7 @@ test("Can go offline while resolving a dispute (CI)", async () => {
|
|||||||
buyerOfflineAfterTake: true,
|
buyerOfflineAfterTake: true,
|
||||||
sellerOfflineAfterDisputeOpened: true,
|
sellerOfflineAfterDisputeOpened: true,
|
||||||
buyerOfflineAfterDisputeOpened: false,
|
buyerOfflineAfterDisputeOpened: false,
|
||||||
sellerDisputeContext: DisputeContext.OPEN_AFTER_DEPOSITS_UNLOCK,
|
buyerDisputeContext: DisputeContext.OPEN_AFTER_DEPOSITS_UNLOCK,
|
||||||
disputeWinner: DisputeResult.Winner.SELLER,
|
disputeWinner: DisputeResult.Winner.SELLER,
|
||||||
disputeReason: DisputeResult.Reason.NO_REPLY,
|
disputeReason: DisputeResult.Reason.NO_REPLY,
|
||||||
disputeSummary: "Seller wins dispute because buyer has not replied",
|
disputeSummary: "Seller wins dispute because buyer has not replied",
|
||||||
@ -2445,6 +2506,7 @@ async function executeTrade(ctxP: Partial<TradeContext>): Promise<string> {
|
|||||||
ctx.buyerAppName = ctx.getBuyer().havenod!.getAppName();
|
ctx.buyerAppName = ctx.getBuyer().havenod!.getAppName();
|
||||||
if (ctx.buyerOfflineAfterTake) {
|
if (ctx.buyerOfflineAfterTake) {
|
||||||
HavenoUtils.log(0, "Buyer going offline");
|
HavenoUtils.log(0, "Buyer going offline");
|
||||||
|
assertNotStaticClient(ctx.getBuyer().havenod!);
|
||||||
promises.push(releaseHavenoProcess(ctx.getBuyer().havenod!));
|
promises.push(releaseHavenoProcess(ctx.getBuyer().havenod!));
|
||||||
if (ctx.isBuyerMaker()) ctx.maker.havenod = undefined;
|
if (ctx.isBuyerMaker()) ctx.maker.havenod = undefined;
|
||||||
else ctx.taker.havenod = undefined;
|
else ctx.taker.havenod = undefined;
|
||||||
@ -2452,6 +2514,7 @@ async function executeTrade(ctxP: Partial<TradeContext>): Promise<string> {
|
|||||||
ctx.sellerAppName = ctx.getSeller().havenod!.getAppName();
|
ctx.sellerAppName = ctx.getSeller().havenod!.getAppName();
|
||||||
if (ctx.sellerOfflineAfterTake) {
|
if (ctx.sellerOfflineAfterTake) {
|
||||||
HavenoUtils.log(0, "Seller going offline");
|
HavenoUtils.log(0, "Seller going offline");
|
||||||
|
assertNotStaticClient(ctx.getSeller().havenod!);
|
||||||
promises.push(releaseHavenoProcess(ctx.getSeller().havenod!));
|
promises.push(releaseHavenoProcess(ctx.getSeller().havenod!));
|
||||||
if (ctx.isBuyerMaker()) ctx.taker.havenod = undefined;
|
if (ctx.isBuyerMaker()) ctx.taker.havenod = undefined;
|
||||||
else ctx.maker.havenod = undefined;
|
else ctx.maker.havenod = undefined;
|
||||||
@ -2460,7 +2523,8 @@ async function executeTrade(ctxP: Partial<TradeContext>): Promise<string> {
|
|||||||
|
|
||||||
// wait for deposit txs to unlock
|
// wait for deposit txs to unlock
|
||||||
if (ctx.isStopped) return ctx.offerId!;
|
if (ctx.isStopped) return ctx.offerId!;
|
||||||
await waitForUnlockedTxs(trade.getMakerDepositTxId(), trade.getTakerDepositTxId());
|
if (ctx.hasBuyerAsTakerWithoutDeposit()) await waitForUnlockedTxs(trade.getMakerDepositTxId());
|
||||||
|
else await waitForUnlockedTxs(trade.getMakerDepositTxId(), trade.getTakerDepositTxId());
|
||||||
|
|
||||||
// buyer comes online if offline and used
|
// buyer comes online if offline and used
|
||||||
if (ctx.isStopped) return ctx.offerId!;
|
if (ctx.isStopped) return ctx.offerId!;
|
||||||
@ -2554,6 +2618,7 @@ async function executeTrade(ctxP: Partial<TradeContext>): Promise<string> {
|
|||||||
if (ctx.isStopped) return ctx.offerId!;
|
if (ctx.isStopped) return ctx.offerId!;
|
||||||
if (ctx.buyerOfflineAfterPaymentSent) {
|
if (ctx.buyerOfflineAfterPaymentSent) {
|
||||||
HavenoUtils.log(0, "Buyer going offline");
|
HavenoUtils.log(0, "Buyer going offline");
|
||||||
|
assertNotStaticClient(ctx.getBuyer().havenod!);
|
||||||
await releaseHavenoProcess(ctx.getBuyer().havenod!);
|
await releaseHavenoProcess(ctx.getBuyer().havenod!);
|
||||||
if (ctx.isBuyerMaker()) ctx.maker.havenod = undefined;
|
if (ctx.isBuyerMaker()) ctx.maker.havenod = undefined;
|
||||||
else ctx.taker.havenod = undefined;
|
else ctx.taker.havenod = undefined;
|
||||||
@ -2757,8 +2822,10 @@ async function makeOffer(ctxP?: Partial<TradeContext>): Promise<OfferInfo> {
|
|||||||
ctx.priceMargin,
|
ctx.priceMargin,
|
||||||
ctx.triggerPrice,
|
ctx.triggerPrice,
|
||||||
ctx.offerMinAmount,
|
ctx.offerMinAmount,
|
||||||
ctx.reserveExactAmount);
|
ctx.reserveExactAmount,
|
||||||
testOffer(offer, ctx);
|
ctx.isPrivateOffer,
|
||||||
|
ctx.buyerAsTakerWithoutDeposit);
|
||||||
|
testOffer(offer, ctx, true);
|
||||||
|
|
||||||
// offer is included in my offers only
|
// offer is included in my offers only
|
||||||
if (!getOffer(await ctx.maker.havenod!.getMyOffers(ctx.assetCode!, ctx.direction), offer.getId())) {
|
if (!getOffer(await ctx.maker.havenod!.getMyOffers(ctx.assetCode!, ctx.direction), offer.getId())) {
|
||||||
@ -2771,6 +2838,7 @@ async function makeOffer(ctxP?: Partial<TradeContext>): Promise<OfferInfo> {
|
|||||||
// collect context
|
// collect context
|
||||||
ctx.maker.splitOutputTxFee = BigInt(offer.getSplitOutputTxFee());
|
ctx.maker.splitOutputTxFee = BigInt(offer.getSplitOutputTxFee());
|
||||||
ctx.taker.splitOutputTxFee = 0n;
|
ctx.taker.splitOutputTxFee = 0n;
|
||||||
|
ctx.challenge = offer.getChallenge();
|
||||||
|
|
||||||
// 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)
|
||||||
// TODO: adjustments should be based on currency and payment method, not fixed-price
|
// TODO: adjustments should be based on currency and payment method, not fixed-price
|
||||||
@ -2835,12 +2903,12 @@ async function takeOffer(ctxP: Partial<TradeContext>): Promise<TradeInfo> {
|
|||||||
const takerBalancesBefore: XmrBalanceInfo = await ctx.taker.havenod!.getBalances();
|
const takerBalancesBefore: XmrBalanceInfo = await ctx.taker.havenod!.getBalances();
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
HavenoUtils.log(1, "Taking offer " + ctx.offerId);
|
HavenoUtils.log(1, "Taking offer " + ctx.offerId);
|
||||||
const trade = await ctx.taker.havenod!.takeOffer(ctx.offerId, ctx.takerPaymentAccountId!, ctx.tradeAmount);
|
const takerTrade = await ctx.taker.havenod!.takeOffer(ctx.offerId, ctx.takerPaymentAccountId!, ctx.tradeAmount, ctx.challenge);
|
||||||
HavenoUtils.log(1, "Done taking offer " + ctx.offerId + " in " + (Date.now() - startTime) + " ms");
|
HavenoUtils.log(1, "Done taking offer " + ctx.offerId + " in " + (Date.now() - startTime) + " ms");
|
||||||
|
|
||||||
// maker is notified that offer is taken
|
// maker is notified that offer is taken
|
||||||
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, takerTrade.getTradeId());
|
||||||
expect(tradeNotifications.length).toBe(1);
|
expect(tradeNotifications.length).toBe(1);
|
||||||
assert(moneroTs.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");
|
||||||
@ -2852,33 +2920,35 @@ async function takeOffer(ctxP: Partial<TradeContext>): Promise<TradeInfo> {
|
|||||||
// wait to observe deposit txs
|
// wait to observe deposit txs
|
||||||
ctx.arbitrator.trade = await ctx.arbitrator.havenod!.getTrade(ctx.offerId!);
|
ctx.arbitrator.trade = await ctx.arbitrator.havenod!.getTrade(ctx.offerId!);
|
||||||
ctx.maker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getMakerDepositTxId());
|
ctx.maker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getMakerDepositTxId());
|
||||||
ctx.taker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getTakerDepositTxId());
|
if (ctx.hasBuyerAsTakerWithoutDeposit()) assert(!ctx.arbitrator.trade!.getTakerDepositTxId());
|
||||||
if (!ctx.maker.depositTx || !ctx.taker.depositTx) {
|
else ctx.taker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getTakerDepositTxId());
|
||||||
|
if (!ctx.maker.depositTx || (!ctx.taker.depositTx && !ctx.hasBuyerAsTakerWithoutDeposit())) {
|
||||||
if (!ctx.maker.depositTx) HavenoUtils.log(0, "Maker deposit tx not found with id " + ctx.arbitrator.trade!.getMakerDepositTxId() + ", waiting...");
|
if (!ctx.maker.depositTx) HavenoUtils.log(0, "Maker deposit tx not found with id " + ctx.arbitrator.trade!.getMakerDepositTxId() + ", waiting...");
|
||||||
if (!ctx.taker.depositTx) HavenoUtils.log(0, "Taker deposit tx not found with id " + ctx.arbitrator.trade!.getTakerDepositTxId() + ", waiting...");
|
if (!ctx.taker.depositTx && !ctx.hasBuyerAsTakerWithoutDeposit()) HavenoUtils.log(0, "Taker deposit tx not found with id " + ctx.arbitrator.trade!.getTakerDepositTxId() + ", waiting...");
|
||||||
await wait(ctx.walletSyncPeriodMs);
|
await wait(ctx.walletSyncPeriodMs);
|
||||||
ctx.maker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getMakerDepositTxId());
|
ctx.maker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getMakerDepositTxId());
|
||||||
ctx.taker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getTakerDepositTxId());
|
if (!ctx.hasBuyerAsTakerWithoutDeposit()) ctx.taker.depositTx = await monerod.getTx(ctx.arbitrator.trade!.getTakerDepositTxId());
|
||||||
if (!ctx.maker.depositTx) throw new Error("Maker deposit tx not found with id " + ctx.arbitrator.trade!.getMakerDepositTxId());
|
if (!ctx.maker.depositTx) throw new Error("Maker deposit tx not found with id " + ctx.arbitrator.trade!.getMakerDepositTxId());
|
||||||
if (!ctx.taker.depositTx) throw new Error("Taker deposit tx not found with id " + ctx.arbitrator.trade!.getTakerDepositTxId());
|
if (!ctx.taker.depositTx && !ctx.hasBuyerAsTakerWithoutDeposit()) throw new Error("Taker deposit tx not found with id " + ctx.arbitrator.trade!.getTakerDepositTxId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// record context
|
// record context
|
||||||
ctx.tradeAmount = BigInt(trade.getAmount()); // re-assign trade amount which could be adjusted
|
ctx.tradeAmount = BigInt(takerTrade.getAmount()); // reassign trade amount which could be adjusted
|
||||||
ctx.maker.trade = await ctx.maker.havenod!.getTrade(ctx.offerId!);
|
ctx.maker.trade = await ctx.maker.havenod!.getTrade(ctx.offerId!);
|
||||||
ctx.taker.trade = await ctx.taker.havenod!.getTrade(ctx.offerId!);
|
ctx.taker.trade = await ctx.taker.havenod!.getTrade(ctx.offerId!);
|
||||||
ctx.maker.balancesAfterTake = await ctx.maker.havenod!.getBalances();
|
ctx.maker.balancesAfterTake = await ctx.maker.havenod!.getBalances();
|
||||||
ctx.taker.balancesAfterTake = await ctx.taker.havenod!.getBalances();
|
ctx.taker.balancesAfterTake = await ctx.taker.havenod!.getBalances();
|
||||||
ctx.maker.depositTxFee = BigInt(ctx.maker.depositTx!.getFee());
|
ctx.maker.depositTxFee = BigInt(ctx.maker.depositTx!.getFee());
|
||||||
ctx.taker.depositTxFee = BigInt(ctx.taker.depositTx!.getFee());
|
ctx.taker.depositTxFee = BigInt(ctx.hasBuyerAsTakerWithoutDeposit() ? 0 : ctx.taker.depositTx!.getFee());
|
||||||
ctx.maker.tradeFee = BigInt(trade.getMakerFee());
|
ctx.maker.tradeFee = BigInt(takerTrade.getMakerFee());
|
||||||
ctx.taker.tradeFee = BigInt(trade.getTakerFee());
|
ctx.taker.tradeFee = BigInt(takerTrade.getTakerFee());
|
||||||
ctx.getBuyer().securityDepositActual = BigInt(trade.getBuyerSecurityDeposit()!);
|
if (ctx.hasBuyerAsTakerWithoutDeposit()) assert.equal(ctx.taker.depositTxFee, 0n);
|
||||||
ctx.getSeller().securityDepositActual = BigInt(trade.getSellerSecurityDeposit()!);
|
ctx.getBuyer().securityDepositActual = BigInt(takerTrade.getBuyerSecurityDeposit()!);
|
||||||
|
ctx.getSeller().securityDepositActual = BigInt(takerTrade.getSellerSecurityDeposit()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test trade model
|
// test trade model
|
||||||
await testTrade(trade, ctx);
|
await testTrade(takerTrade, ctx);
|
||||||
|
|
||||||
// test buyer and seller balances after offer taken
|
// test buyer and seller balances after offer taken
|
||||||
if (!ctx.concurrentTrades) {
|
if (!ctx.concurrentTrades) {
|
||||||
@ -2886,14 +2956,14 @@ async function takeOffer(ctxP: Partial<TradeContext>): Promise<TradeInfo> {
|
|||||||
|
|
||||||
// test buyer balances after offer taken
|
// test buyer balances after offer taken
|
||||||
const buyerBalanceDiffReservedTrade = BigInt(ctx.getBuyer().balancesAfterTake!.getReservedTradeBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getReservedTradeBalance());
|
const buyerBalanceDiffReservedTrade = BigInt(ctx.getBuyer().balancesAfterTake!.getReservedTradeBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getReservedTradeBalance());
|
||||||
expect(buyerBalanceDiffReservedTrade).toEqual(BigInt(trade.getBuyerSecurityDeposit()!));
|
expect(buyerBalanceDiffReservedTrade).toEqual(BigInt(takerTrade.getBuyerSecurityDeposit()!));
|
||||||
const buyerBalanceDiffReservedOffer = BigInt(ctx.getBuyer().balancesAfterTake!.getReservedOfferBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getReservedOfferBalance());
|
const buyerBalanceDiffReservedOffer = BigInt(ctx.getBuyer().balancesAfterTake!.getReservedOfferBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getReservedOfferBalance());
|
||||||
const buyerBalanceDiff = BigInt(ctx.getBuyer().balancesAfterTake!.getBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getBalance());
|
const buyerBalanceDiff = BigInt(ctx.getBuyer().balancesAfterTake!.getBalance()) - BigInt(ctx.getBuyer().balancesBeforeTake!.getBalance());
|
||||||
expect(buyerBalanceDiff).toEqual(-1n * buyerBalanceDiffReservedOffer - buyerBalanceDiffReservedTrade - ctx.getBuyer().depositTxFee! - ctx.getBuyer().tradeFee!);
|
expect(buyerBalanceDiff).toEqual(-1n * buyerBalanceDiffReservedOffer - buyerBalanceDiffReservedTrade - ctx.getBuyer().depositTxFee! - ctx.getBuyer().tradeFee!);
|
||||||
|
|
||||||
// test seller balances after offer taken
|
// test seller balances after offer taken
|
||||||
const sellerBalanceDiffReservedTrade = BigInt(ctx.getSeller().balancesAfterTake!.getReservedTradeBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getReservedTradeBalance());
|
const sellerBalanceDiffReservedTrade = BigInt(ctx.getSeller().balancesAfterTake!.getReservedTradeBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getReservedTradeBalance());
|
||||||
expect(sellerBalanceDiffReservedTrade).toEqual(BigInt(trade.getAmount()) + BigInt(trade.getSellerSecurityDeposit()!));
|
expect(sellerBalanceDiffReservedTrade).toEqual(BigInt(takerTrade.getAmount()) + BigInt(takerTrade.getSellerSecurityDeposit()!));
|
||||||
const sellerBalanceDiffReservedOffer = BigInt(ctx.getSeller().balancesAfterTake!.getReservedOfferBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getReservedOfferBalance());
|
const sellerBalanceDiffReservedOffer = BigInt(ctx.getSeller().balancesAfterTake!.getReservedOfferBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getReservedOfferBalance());
|
||||||
const sellerBalanceDiff = BigInt(ctx.getSeller().balancesAfterTake!.getBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getBalance());
|
const sellerBalanceDiff = BigInt(ctx.getSeller().balancesAfterTake!.getBalance()) - BigInt(ctx.getSeller().balancesBeforeTake!.getBalance());
|
||||||
expect(sellerBalanceDiff).toEqual(-1n * sellerBalanceDiffReservedOffer - sellerBalanceDiffReservedTrade - ctx.getSeller().depositTxFee! - ctx.getSeller().tradeFee!);
|
expect(sellerBalanceDiff).toEqual(-1n * sellerBalanceDiffReservedOffer - sellerBalanceDiffReservedTrade - ctx.getSeller().depositTxFee! - ctx.getSeller().tradeFee!);
|
||||||
@ -2912,25 +2982,25 @@ async function takeOffer(ctxP: Partial<TradeContext>): Promise<TradeInfo> {
|
|||||||
|
|
||||||
// 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)
|
||||||
// TODO: adjustments are based on payment method, not fixed-price
|
// TODO: adjustments are based on payment method, not fixed-price
|
||||||
if (trade.getOffer()!.getUseMarketBasedPrice()) {
|
if (takerTrade.getOffer()!.getUseMarketBasedPrice()) {
|
||||||
assert.equal(ctx.tradeAmount, BigInt(trade.getAmount()));
|
assert.equal(ctx.tradeAmount, BigInt(takerTrade.getAmount()));
|
||||||
} else {
|
} else {
|
||||||
expect(Math.abs(HavenoUtils.percentageDiff(ctx.tradeAmount!, BigInt(trade.getAmount())))).toBeLessThan(TestConfig.maxAdjustmentPct);
|
expect(Math.abs(HavenoUtils.percentageDiff(ctx.tradeAmount!, BigInt(takerTrade.getAmount())))).toBeLessThan(TestConfig.maxAdjustmentPct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// maker is notified of balance change
|
// maker is notified of balance change
|
||||||
|
|
||||||
// taker is notified of balance change
|
// taker is notified of balance change
|
||||||
|
|
||||||
return trade;
|
return takerTrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testTrade(trade: TradeInfo, ctx: TradeContext, havenod?: HavenoClient): Promise<void> {
|
async function testTrade(trade: TradeInfo, ctx: TradeContext, havenod?: HavenoClient): Promise<void> {
|
||||||
expect(BigInt(trade.getAmount())).toEqual(ctx!.tradeAmount);
|
expect(BigInt(trade.getAmount())).toEqual(ctx!.tradeAmount);
|
||||||
|
|
||||||
// test security deposit = max(.1, trade amount * security deposit pct)
|
// test security deposit = max(0.1, trade amount * security deposit pct)
|
||||||
const expectedSecurityDeposit = HavenoUtils.max(HavenoUtils.xmrToAtomicUnits(.1), HavenoUtils.multiply(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.getBuyerSecurityDeposit())).toEqual(ctx.hasBuyerAsTakerWithoutDeposit() ? 0n : expectedSecurityDeposit - ctx.getBuyer().depositTxFee!);
|
||||||
expect(BigInt(trade.getSellerSecurityDeposit())).toEqual(expectedSecurityDeposit - ctx.getSeller().depositTxFee!);
|
expect(BigInt(trade.getSellerSecurityDeposit())).toEqual(expectedSecurityDeposit - ctx.getSeller().depositTxFee!);
|
||||||
|
|
||||||
// test phase
|
// test phase
|
||||||
@ -3138,8 +3208,6 @@ async function resolveDispute(ctxP: Partial<TradeContext>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// award too little to loser (minority receiver)
|
// award too little to loser (minority receiver)
|
||||||
let makerDepositTx = await monerod.getTx(trade.getMakerDepositTxId());
|
|
||||||
let takerDepositTx = await monerod.getTx(trade.getTakerDepositTxId());
|
|
||||||
customWinnerAmount = tradeAmount + BigInt(trade.getBuyerSecurityDeposit()) + BigInt(trade.getSellerSecurityDeposit()) - 10000n;
|
customWinnerAmount = tradeAmount + BigInt(trade.getBuyerSecurityDeposit()) + BigInt(trade.getSellerSecurityDeposit()) - 10000n;
|
||||||
try {
|
try {
|
||||||
await arbitrator.resolveDispute(ctx.offerId!, ctx.disputeWinner!, ctx.disputeReason!, "Loser gets too little", customWinnerAmount);
|
await arbitrator.resolveDispute(ctx.offerId!, ctx.disputeWinner!, ctx.disputeReason!, "Loser gets too little", customWinnerAmount);
|
||||||
@ -3235,16 +3303,16 @@ async function testAmountsAfterComplete(tradeCtx: TradeContext) {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// get expected payouts for disputed trade
|
// get expected payouts for disputed trade
|
||||||
const winnerGetsAll = tradeCtx.disputeWinnerAmount === tradeCtx.maker.securityDepositActual! + tradeCtx.taker.securityDepositActual! + tradeCtx.tradeAmount!;
|
const winnerGetsAll = tradeCtx.disputeWinnerAmount === tradeCtx.maker.securityDepositActual! + tradeCtx.taker.securityDepositActual! + tradeCtx.tradeAmount! || (tradeCtx.hasBuyerAsTakerWithoutDeposit() && tradeCtx.disputeWinner === DisputeResult.Winner.SELLER && tradeCtx.disputeWinnerAmount === undefined);
|
||||||
if (tradeCtx.disputeWinnerAmount) {
|
if (tradeCtx.disputeWinnerAmount) {
|
||||||
tradeCtx.getDisputeWinner()!.payoutTxFee = winnerGetsAll ? payoutTxFee : 0n;
|
tradeCtx.getDisputeWinner()!.payoutTxFee = winnerGetsAll ? payoutTxFee : 0n;
|
||||||
tradeCtx.getDisputeWinner()!.payoutAmount = tradeCtx.disputeWinnerAmount - tradeCtx.getDisputeWinner()!.payoutTxFee!;
|
tradeCtx.getDisputeWinner()!.payoutAmount = tradeCtx.disputeWinnerAmount - tradeCtx.getDisputeWinner()!.payoutTxFee!;
|
||||||
tradeCtx.getDisputeLoser()!.payoutTxFee = winnerGetsAll ? 0n : payoutTxFee;
|
tradeCtx.getDisputeLoser()!.payoutTxFee = winnerGetsAll ? 0n : payoutTxFee;
|
||||||
tradeCtx.getDisputeLoser()!.payoutAmount = tradeCtx.maker.securityDepositActual! + tradeCtx.taker.securityDepositActual! + tradeCtx.tradeAmount! - tradeCtx.disputeWinnerAmount - tradeCtx.getDisputeLoser()!.payoutTxFee!;
|
tradeCtx.getDisputeLoser()!.payoutAmount = tradeCtx.maker.securityDepositActual! + tradeCtx.taker.securityDepositActual! + tradeCtx.tradeAmount! - tradeCtx.disputeWinnerAmount - tradeCtx.getDisputeLoser()!.payoutTxFee!;
|
||||||
} else {
|
} else {
|
||||||
tradeCtx.getDisputeWinner()!.payoutTxFee = payoutTxFee / 2n;
|
tradeCtx.getDisputeWinner()!.payoutTxFee = winnerGetsAll ? payoutTxFee : payoutTxFee / 2n;
|
||||||
tradeCtx.getDisputeWinner()!.payoutAmount = tradeCtx.tradeAmount! + tradeCtx.getDisputeWinner()!.securityDepositActual! - tradeCtx.getDisputeWinner()!.payoutTxFee!;
|
tradeCtx.getDisputeWinner()!.payoutAmount = tradeCtx.tradeAmount! + tradeCtx.getDisputeWinner()!.securityDepositActual! - tradeCtx.getDisputeWinner()!.payoutTxFee!;
|
||||||
tradeCtx.getDisputeLoser()!.payoutTxFee = payoutTxFee / 2n;
|
tradeCtx.getDisputeLoser()!.payoutTxFee = winnerGetsAll ? 0n : payoutTxFee / 2n;
|
||||||
tradeCtx.getDisputeLoser()!.payoutAmount = tradeCtx.getDisputeLoser()!.securityDepositActual! - tradeCtx.getDisputeLoser()!.payoutTxFee!;
|
tradeCtx.getDisputeLoser()!.payoutAmount = tradeCtx.getDisputeLoser()!.securityDepositActual! - tradeCtx.getDisputeLoser()!.payoutTxFee!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3457,18 +3525,20 @@ async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> {
|
|||||||
"--logLevel", ctx.logLevel!
|
"--logLevel", ctx.logLevel!
|
||||||
];
|
];
|
||||||
havenod = await HavenoClient.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + ctx.port, ctx.logProcessOutput!);
|
havenod = await HavenoClient.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + ctx.port, ctx.logProcessOutput!);
|
||||||
HAVENO_PROCESSES.push(havenod);
|
|
||||||
|
|
||||||
// wait to process network notifications
|
// wait to process network notifications
|
||||||
await wait(3000);
|
await wait(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add to list of clients
|
||||||
|
HAVENO_CLIENTS.push(havenod)
|
||||||
|
|
||||||
// open account if configured
|
// open account if configured
|
||||||
if (ctx.autoLogin) {
|
if (ctx.autoLogin) {
|
||||||
try {
|
try {
|
||||||
await initHavenoAccount(havenod, ctx.accountPassword!);
|
await initHavenoAccount(havenod, ctx.accountPassword!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await releaseHavenoProcess(havenod);
|
await releaseHavenoClient(havenod);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3487,11 +3557,21 @@ async function initHaveno(ctx?: HavenodContext): Promise<HavenoClient> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release a Haveno client by shutting down its process or disconnecting.
|
||||||
|
*/
|
||||||
|
async function releaseHavenoClient(client: HavenoClient, deleteProcessAppDir?: boolean) {
|
||||||
|
if (client.getProcess()) return releaseHavenoProcess(client, deleteProcessAppDir);
|
||||||
|
else await client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
||||||
moneroTs.GenUtils.remove(HAVENO_PROCESSES, havenod);
|
if (!testsOwnProcess(havenod)) throw new Error("Cannot shut down havenod process which is not owned by test");
|
||||||
|
if (!moneroTs.GenUtils.arrayContains(HAVENO_CLIENTS, havenod)) throw new Error("Cannot release Haveno client which is not in list of clients");
|
||||||
|
moneroTs.GenUtils.remove(HAVENO_CLIENTS, havenod);
|
||||||
moneroTs.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();
|
||||||
@ -3501,6 +3581,14 @@ async function releaseHavenoProcess(havenod: HavenoClient, deleteAppDir?: boolea
|
|||||||
if (deleteAppDir) deleteHavenoInstance(havenod);
|
if (deleteAppDir) deleteHavenoInstance(havenod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testsOwnProcess(havenod: HavenoClient) {
|
||||||
|
return havenod.getProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNotStaticClient(client: HavenoClient) {
|
||||||
|
if (client === user1 || client === user2 || client === arbitrator) throw new Error("Tests are not designed to shut down user1, user2, or arbitrator")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a Haveno instance from disk.
|
* Delete a Haveno instance from disk.
|
||||||
*/
|
*/
|
||||||
@ -4007,13 +4095,26 @@ function testCryptoPaymentAccountsEqual(acct1: PaymentAccount, acct2: PaymentAcc
|
|||||||
expect(acct1.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress()).toEqual(acct2.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress());
|
expect(acct1.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress()).toEqual(acct2.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
function testOffer(offer: OfferInfo, ctx?: Partial<TradeContext>) {
|
function testOffer(offer: OfferInfo, ctxP?: Partial<TradeContext>, isMyOffer?: boolean) {
|
||||||
|
let ctx = TradeContext.init(ctxP);
|
||||||
expect(offer.getId().length).toBeGreaterThan(0);
|
expect(offer.getId().length).toBeGreaterThan(0);
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
expect(offer.getBuyerSecurityDepositPct()).toEqual(ctx?.securityDepositPct);
|
expect(offer.getIsPrivateOffer()).toEqual(ctx?.isPrivateOffer ? true : false); // TODO: update tests for security deposit
|
||||||
expect(offer.getSellerSecurityDepositPct()).toEqual(ctx?.securityDepositPct);
|
if (offer.getIsPrivateOffer()) {
|
||||||
|
if (isMyOffer) expect(offer.getChallenge().length).toBeGreaterThan(0);
|
||||||
|
else expect(offer.getChallenge()).toEqual("");
|
||||||
|
if (ctx.isBuyerMaker()) {
|
||||||
|
expect(offer.getBuyerSecurityDepositPct()).toEqual(ctx.securityDepositPct);
|
||||||
|
} else {
|
||||||
|
expect(offer.getBuyerSecurityDepositPct()).toEqual(ctx.buyerAsTakerWithoutDeposit ? 0 : ctx.securityDepositPct);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expect(offer.getBuyerSecurityDepositPct()).toEqual(ctx.securityDepositPct);
|
||||||
|
expect(offer.getChallenge()).toEqual("");
|
||||||
|
}
|
||||||
|
expect(offer.getSellerSecurityDepositPct()).toEqual(ctx.securityDepositPct);
|
||||||
expect(offer.getUseMarketBasedPrice()).toEqual(!ctx?.price);
|
expect(offer.getUseMarketBasedPrice()).toEqual(!ctx?.price);
|
||||||
expect(offer.getMarketPriceMarginPct()).toEqual(ctx?.priceMargin ? ctx?.priceMargin : 0);
|
expect(offer.getMarketPriceMarginPct()).toEqual(ctx?.priceMargin ? ctx.priceMargin : 0);
|
||||||
|
|
||||||
// TODO: test rest of offer
|
// TODO: test rest of offer
|
||||||
}
|
}
|
||||||
|
@ -1046,6 +1046,8 @@ export default class HavenoClient {
|
|||||||
* @param {number} triggerPrice - price to remove offer (optional)
|
* @param {number} triggerPrice - price to remove offer (optional)
|
||||||
* @param {bigint} minAmount - minimum amount to trade (optional, default to fixed amount)
|
* @param {bigint} minAmount - minimum amount to trade (optional, default to fixed amount)
|
||||||
* @param {number} reserveExactAmount - reserve exact amount needed for offer, incurring on-chain transaction and 10 confirmations before the offer goes live (default = false)
|
* @param {number} reserveExactAmount - reserve exact amount needed for offer, incurring on-chain transaction and 10 confirmations before the offer goes live (default = false)
|
||||||
|
* @param {boolean} isPrivateOffer - whether the offer is private (default = false)
|
||||||
|
* @param {boolean} buyerAsTakerWithoutDeposit - waive buyer as taker deposit and fee (default false)
|
||||||
* @return {OfferInfo} the posted offer
|
* @return {OfferInfo} the posted offer
|
||||||
*/
|
*/
|
||||||
async postOffer(direction: OfferDirection,
|
async postOffer(direction: OfferDirection,
|
||||||
@ -1057,7 +1059,9 @@ export default class HavenoClient {
|
|||||||
marketPriceMarginPct?: number,
|
marketPriceMarginPct?: number,
|
||||||
triggerPrice?: number,
|
triggerPrice?: number,
|
||||||
minAmount?: bigint,
|
minAmount?: bigint,
|
||||||
reserveExactAmount?: boolean): Promise<OfferInfo> {
|
reserveExactAmount?: boolean,
|
||||||
|
isPrivateOffer?: boolean,
|
||||||
|
buyerAsTakerWithoutDeposit?: boolean): Promise<OfferInfo> {
|
||||||
console.log("Posting offer with security deposit %: " + securityDepositPct)
|
console.log("Posting offer with security deposit %: " + securityDepositPct)
|
||||||
try {
|
try {
|
||||||
const request = new PostOfferRequest()
|
const request = new PostOfferRequest()
|
||||||
@ -1065,13 +1069,15 @@ export default class HavenoClient {
|
|||||||
.setAmount(amount.toString())
|
.setAmount(amount.toString())
|
||||||
.setCurrencyCode(assetCode)
|
.setCurrencyCode(assetCode)
|
||||||
.setPaymentAccountId(paymentAccountId)
|
.setPaymentAccountId(paymentAccountId)
|
||||||
.setBuyerSecurityDepositPct(securityDepositPct)
|
.setSecurityDepositPct(securityDepositPct)
|
||||||
.setUseMarketBasedPrice(price === undefined)
|
.setUseMarketBasedPrice(price === undefined)
|
||||||
.setMinAmount(minAmount ? minAmount.toString() : amount.toString());
|
.setMinAmount(minAmount ? minAmount.toString() : amount.toString());
|
||||||
if (price) request.setPrice(price.toString());
|
if (price) request.setPrice(price.toString());
|
||||||
if (marketPriceMarginPct) request.setMarketPriceMarginPct(marketPriceMarginPct);
|
if (marketPriceMarginPct) request.setMarketPriceMarginPct(marketPriceMarginPct);
|
||||||
if (triggerPrice) request.setTriggerPrice(triggerPrice.toString());
|
if (triggerPrice) request.setTriggerPrice(triggerPrice.toString());
|
||||||
if (reserveExactAmount) request.setReserveExactAmount(reserveExactAmount);
|
if (reserveExactAmount) request.setReserveExactAmount(true);
|
||||||
|
if (isPrivateOffer) request.setIsPrivateOffer(true);
|
||||||
|
if (buyerAsTakerWithoutDeposit) request.setBuyerAsTakerWithoutDeposit(true);
|
||||||
return (await this._offersClient.postOffer(request, {password: this._password})).getOffer()!;
|
return (await this._offersClient.postOffer(request, {password: this._password})).getOffer()!;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
throw new HavenoError(e.message, e.code);
|
throw new HavenoError(e.message, e.code);
|
||||||
@ -1097,16 +1103,19 @@ export default class HavenoClient {
|
|||||||
* @param {string} offerId - id of the offer to take
|
* @param {string} offerId - id of the offer to take
|
||||||
* @param {string} paymentAccountId - id of the payment account
|
* @param {string} paymentAccountId - id of the payment account
|
||||||
* @param {bigint|undefined} amount - amount the taker chooses to buy or sell within the offer range (default is max offer amount)
|
* @param {bigint|undefined} amount - amount the taker chooses to buy or sell within the offer range (default is max offer amount)
|
||||||
|
* @param {string|undefined} challenge - the challenge to use for the private offer
|
||||||
* @return {TradeInfo} the initialized trade
|
* @return {TradeInfo} the initialized trade
|
||||||
*/
|
*/
|
||||||
async takeOffer(offerId: string,
|
async takeOffer(offerId: string,
|
||||||
paymentAccountId: string,
|
paymentAccountId: string,
|
||||||
amount?: bigint): Promise<TradeInfo> {
|
amount?: bigint,
|
||||||
|
challenge?: string): Promise<TradeInfo> {
|
||||||
try {
|
try {
|
||||||
const request = new TakeOfferRequest()
|
const request = new TakeOfferRequest()
|
||||||
.setOfferId(offerId)
|
.setOfferId(offerId)
|
||||||
.setPaymentAccountId(paymentAccountId);
|
.setPaymentAccountId(paymentAccountId);
|
||||||
if (amount) request.setAmount(amount.toString());
|
if (amount) request.setAmount(amount.toString());
|
||||||
|
if (challenge) request.setChallenge(challenge);
|
||||||
const resp = await this._tradesClient.takeOffer(request, {password: this._password});
|
const resp = await this._tradesClient.takeOffer(request, {password: this._password});
|
||||||
if (resp.getTrade()) return resp.getTrade()!;
|
if (resp.getTrade()) return resp.getTrade()!;
|
||||||
throw new HavenoError(resp.getFailureReason()?.getDescription()!, resp.getFailureReason()?.getAvailabilityResult());
|
throw new HavenoError(resp.getFailureReason()?.getDescription()!, resp.getFailureReason()?.getAvailabilityResult());
|
||||||
@ -1319,7 +1328,7 @@ export default class HavenoClient {
|
|||||||
try {
|
try {
|
||||||
await this.disconnect();
|
await this.disconnect();
|
||||||
await this._shutdownServerClient.stop(new StopRequest(), {password: this._password}); // process receives 'exit' event
|
await this._shutdownServerClient.stop(new StopRequest(), {password: this._password}); // process receives 'exit' event
|
||||||
if (this._process) return HavenoUtils.kill(this._process);
|
if (this._process) await HavenoUtils.kill(this._process);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
throw new HavenoError(e.message, e.code);
|
throw new HavenoError(e.message, e.code);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user