support strict null checks and ignore .vscode

This commit is contained in:
woodser 2022-10-14 16:09:10 -04:00
parent 8fb88e8703
commit a382b18a6d
2 changed files with 164 additions and 161 deletions

1
.gitignore vendored
View File

@ -19,6 +19,7 @@ backup.zip
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.vscode
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*

View File

@ -153,7 +153,7 @@ const TestConfig = {
makeOffer: true, makeOffer: true,
takeOffer: true, takeOffer: true,
awaitFundsToMakeOffer: true, awaitFundsToMakeOffer: true,
maker: undefined, // assigned to user1 before all tests maker: {} as HavenoClient, // assigned to user1 before all tests
direction: "buy", // buy or sell xmr direction: "buy", // buy or sell xmr
amount: BigInt("200000000000"), // amount of xmr to trade amount: BigInt("200000000000"), // amount of xmr to trade
minAmount: undefined, minAmount: undefined,
@ -164,12 +164,12 @@ const TestConfig = {
priceMargin: 0.0, priceMargin: 0.0,
triggerPrice: undefined, triggerPrice: undefined,
awaitFundsToTakeOffer: true, awaitFundsToTakeOffer: true,
taker: undefined, // assigned to user2 before all tests taker: {} as HavenoClient, // assigned to user2 before all tests
offerId: undefined, offerId: undefined,
takerPaymentAccountId: undefined, takerPaymentAccountId: undefined,
buyerSendsPayment: true, buyerSendsPayment: true,
sellerReceivesPayment: true, sellerReceivesPayment: true,
arbitrator: undefined, // test arbitrator state (does not choose arbitrator). assigned to default arbitrator before all tests arbitrator: {} as HavenoClient, // test arbitrator state (does not choose arbitrator). assigned to default arbitrator before all tests
resolveDispute: true, // resolve dispute after opening, resolveDispute: true, // resolve dispute after opening,
disputeWinner: DisputeResult.Winner.SELLER, disputeWinner: DisputeResult.Winner.SELLER,
disputeReason: DisputeResult.Reason.PEER_WAS_LATE, disputeReason: DisputeResult.Reason.PEER_WAS_LATE,
@ -257,7 +257,7 @@ beforeAll(async () => {
HavenoUtils.log(0, "Funding wallet new subaddress: " + subaddress.getAddress()); HavenoUtils.log(0, "Funding wallet new subaddress: " + subaddress.getAddress());
// start configured haveno daemons // start configured haveno daemons
const promises = []; const promises: Promise<HavenoClient>[] = [];
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") throw new Error((settledPromise as PromiseRejectedResult).reason); if (settledPromise.status !== "fulfilled") throw new Error((settledPromise as PromiseRejectedResult).reason);
@ -291,7 +291,7 @@ beforeEach(async () => {
afterAll(async () => { afterAll(async () => {
// release haveno processes // release haveno processes
const promises = []; const promises: Promise<void>[] = [];
for (const havenod of startupHavenods) { for (const havenod of startupHavenods) {
promises.push(havenod.getProcess() ? releaseHavenoProcess(havenod) : havenod.disconnect()); promises.push(havenod.getProcess() ? releaseHavenoProcess(havenod) : havenod.disconnect());
} }
@ -309,7 +309,7 @@ test("Can get the version", async () => {
}); });
test("Can manage an account", async () => { test("Can manage an account", async () => {
let user3: HavenoClient | undefined; let user3: HavenoClient|undefined;
let err: any; let err: any;
try { try {
@ -427,7 +427,7 @@ test("Can manage an account", async () => {
test("Can manage Monero daemon connections", async () => { test("Can manage Monero daemon connections", async () => {
let monerod2: any; let monerod2: any;
let user3: HavenoClient | undefined; let user3: HavenoClient|undefined;
let err: any; let err: any;
try { try {
@ -440,7 +440,7 @@ test("Can manage Monero daemon connections", async () => {
testConnection(getConnection(connections, monerodUrl1)!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); testConnection(getConnection(connections, monerodUrl1)!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
// test default connection // test default connection
let connection: UrlConnection | undefined = await user3.getMoneroConnection(); let connection: UrlConnection|undefined = await user3.getMoneroConnection();
assert(await user3.isConnectedToMonero()); assert(await user3.isConnectedToMonero());
testConnection(connection!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // TODO: should be no authentication? testConnection(connection!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // TODO: should be no authentication?
@ -979,7 +979,7 @@ test("Can validate payment account forms", async () => {
assert(found, "Payment account not found after adding"); assert(found, "Payment account not found after adding");
// test payment account // test payment account
expect(fiatAccount.getPaymentMethod().getId()).toEqual(paymentMethod.getId()); expect(fiatAccount.getPaymentMethod()!.getId()).toEqual(paymentMethod.getId());
testFiatAccount(fiatAccount, accountForm); testFiatAccount(fiatAccount, accountForm);
} }
}); });
@ -998,7 +998,7 @@ test("Can create fiat payment accounts", async () => {
// create payment account // create payment account
const fiatAccount = await user1.createPaymentAccount(accountForm); const fiatAccount = await user1.createPaymentAccount(accountForm);
expect(fiatAccount.getAccountName()).toEqual(HavenoUtils.getFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, accountForm)); expect(fiatAccount.getAccountName()).toEqual(HavenoUtils.getFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, accountForm));
expect(fiatAccount.getSelectedTradeCurrency().getCode()).toEqual("USD"); expect(fiatAccount.getSelectedTradeCurrency()!.getCode()).toEqual("USD");
expect(fiatAccount.getTradeCurrenciesList().length).toBeGreaterThan(0); expect(fiatAccount.getTradeCurrenciesList().length).toBeGreaterThan(0);
expect(fiatAccount.getPaymentAccountPayload()!.getPaymentMethodId()).toEqual(paymentMethodId); expect(fiatAccount.getPaymentAccountPayload()!.getPaymentMethodId()).toEqual(paymentMethodId);
expect(fiatAccount.getPaymentAccountPayload()!.getRevolutAccountPayload()!.getAccountId()).toEqual(HavenoUtils.getFormValue(PaymentAccountFormField.FieldId.USER_NAME, accountForm)); // TODO: payment payload account id is username? expect(fiatAccount.getPaymentAccountPayload()!.getRevolutAccountPayload()!.getAccountId()).toEqual(HavenoUtils.getFormValue(PaymentAccountFormField.FieldId.USER_NAME, accountForm)); // TODO: payment payload account id is username?
@ -1027,7 +1027,7 @@ test("Can create crypto payment accounts", async () => {
testCryptoPaymentAccountEquals(paymentAccount, testAccount, name); testCryptoPaymentAccountEquals(paymentAccount, testAccount, name);
// fetch and test payment account // fetch and test payment account
let fetchedAccount: PaymentAccount | undefined; let fetchedAccount: PaymentAccount|undefined;
for (const account of await user1.getPaymentAccounts()) { for (const account of await user1.getPaymentAccounts()) {
if (paymentAccount.getId() === account.getId()) { if (paymentAccount.getId() === account.getId()) {
fetchedAccount = account; fetchedAccount = account;
@ -1125,7 +1125,7 @@ test("Can make offers", async () => {
// TODO: support splitting outputs // TODO: support splitting outputs
// TODO: provide number of confirmations in offer status // TODO: provide number of confirmations in offer status
test("Can schedule offers with locked funds", async () => { test("Can schedule offers with locked funds", async () => {
let user3: HavenoClient | undefined; let user3: HavenoClient|undefined;
let err: any; let err: any;
try { try {
@ -1222,7 +1222,7 @@ test("Can complete trades at the same time", async () => {
test("Can go offline while completing a trade", async () => { test("Can go offline while completing a trade", async () => {
let traders: HavenoClient[] = []; let traders: HavenoClient[] = [];
let config: TradeConfig = undefined; let config: TradeConfig = {};
let err: any; let err: any;
try { try {
@ -1263,6 +1263,7 @@ test("Can resolve disputes", async () => {
const tradeIds = await executeTrades(configs); const tradeIds = await executeTrades(configs);
// open disputes at same time but do not resolve // open disputes at same time but do not resolve
HavenoUtils.log(1, "Opening disputes");
const trade2 = await user1.getTrade(tradeIds[2]); const trade2 = await user1.getTrade(tradeIds[2]);
const trade3 = await user1.getTrade(tradeIds[3]); const trade3 = await user1.getTrade(tradeIds[3]);
const disputeConfigs: TradeConfig[] = [{ const disputeConfigs: TradeConfig[] = [{
@ -1289,7 +1290,7 @@ test("Can resolve disputes", async () => {
disputeWinner: DisputeResult.Winner.BUYER, disputeWinner: DisputeResult.Winner.BUYER,
disputeReason: DisputeResult.Reason.WRONG_SENDER_ACCOUNT, disputeReason: DisputeResult.Reason.WRONG_SENDER_ACCOUNT,
disputeSummary: "Split trade amount", disputeSummary: "Split trade amount",
disputeWinnerAmount: BigInt(trade2.getAmountAsLong()) / BigInt(2) + HavenoUtils.centinerosToAtomicUnits(trade2.getOffer().getBuyerSecurityDeposit()) disputeWinnerAmount: BigInt(trade2.getAmountAsLong()) / BigInt(2) + HavenoUtils.centinerosToAtomicUnits(trade2.getOffer()!.getBuyerSecurityDeposit())
}, { }, {
offerId: tradeIds[3], offerId: tradeIds[3],
takeOffer: false, takeOffer: false,
@ -1298,17 +1299,18 @@ test("Can resolve disputes", async () => {
disputeWinner: DisputeResult.Winner.SELLER, disputeWinner: DisputeResult.Winner.SELLER,
disputeReason: DisputeResult.Reason.TRADE_ALREADY_SETTLED, disputeReason: DisputeResult.Reason.TRADE_ALREADY_SETTLED,
disputeSummary: "Seller gets everything", disputeSummary: "Seller gets everything",
disputeWinnerAmount: BigInt(trade3.getAmountAsLong()) + HavenoUtils.centinerosToAtomicUnits(trade3.getOffer().getBuyerSecurityDeposit() + trade3.getOffer().getSellerSecurityDeposit()) disputeWinnerAmount: BigInt(trade3.getAmountAsLong()) + HavenoUtils.centinerosToAtomicUnits(trade3.getOffer()!.getBuyerSecurityDeposit() + trade3.getOffer()!.getSellerSecurityDeposit())
}]; }];
await executeTrades(disputeConfigs); await executeTrades(disputeConfigs);
// resolve disputes in sequence // resolve disputes
HavenoUtils.log(1, "Resolving disputes");
for (const config of disputeConfigs) config.resolveDispute = true; for (const config of disputeConfigs) config.resolveDispute = true;
await executeTrades(disputeConfigs, false); await executeTrades(disputeConfigs, false); // resolve in sequence to test balances before and after
}); });
test("Cannot make or take offer with insufficient unlocked funds", async () => { test("Cannot make or take offer with insufficient unlocked funds", async () => {
let user3: HavenoClient | undefined; let user3: HavenoClient|undefined;
let err: any; let err: any;
try { try {
@ -1376,7 +1378,7 @@ test("Invalidates offers when reserved funds are spent", async () => {
await waitForAvailableBalance(tradeAmount * BigInt("2"), user1); await waitForAvailableBalance(tradeAmount * BigInt("2"), user1);
// get frozen key images before posting offer // get frozen key images before posting offer
const frozenKeyImagesBefore = []; const frozenKeyImagesBefore: any[] = [];
for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesBefore.push(frozenOutput.getKeyImage().getHex()); for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesBefore.push(frozenOutput.getKeyImage().getHex());
// post offer // post offer
@ -1385,8 +1387,8 @@ test("Invalidates offers when reserved funds are spent", async () => {
const offer: OfferInfo = await makeOffer({maker: user1, assetCode: assetCode, amount: tradeAmount}); const offer: OfferInfo = await makeOffer({maker: user1, assetCode: assetCode, amount: tradeAmount});
// get key images reserved by offer // get key images reserved by offer
const reservedKeyImages = []; const reservedKeyImages: any[] = [];
const frozenKeyImagesAfter = []; const frozenKeyImagesAfter: any[] = [];
for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesAfter.push(frozenOutput.getKeyImage().getHex()); for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesAfter.push(frozenOutput.getKeyImage().getHex());
for (const frozenKeyImageAfter of frozenKeyImagesAfter) { for (const frozenKeyImageAfter of frozenKeyImagesAfter) {
if (!frozenKeyImagesBefore.includes(frozenKeyImageAfter)) reservedKeyImages.push(frozenKeyImageAfter); if (!frozenKeyImagesBefore.includes(frozenKeyImageAfter)) reservedKeyImages.push(frozenKeyImageAfter);
@ -1538,8 +1540,8 @@ test("Selects arbitrators which are online, registered, and least used", async (
await wait(TestConfig.walletSyncPeriodMs * 2); await wait(TestConfig.walletSyncPeriodMs * 2);
// get internal api addresses // get internal api addresses
const arbitratorApiUrl = "localhost:" + TestConfig.proxyPorts.get(getPort(arbitrator.getUrl()))[1]; // TODO: havenod.getApiUrl()? const arbitratorApiUrl = "localhost:" + TestConfig.proxyPorts.get(getPort(arbitrator.getUrl()))![1]; // TODO: havenod.getApiUrl()?
const arbitrator2ApiUrl = "localhost:" + TestConfig.proxyPorts.get(getPort(arbitrator2.getUrl()))[1]; const arbitrator2ApiUrl = "localhost:" + TestConfig.proxyPorts.get(getPort(arbitrator2.getUrl()))![1];
let err = undefined; let err = undefined;
try { try {
@ -1625,7 +1627,7 @@ test("Selects arbitrators which are online, registered, and least used", async (
// ---------------------------- TRADE HELPERS --------------------------------- // ---------------------------- TRADE HELPERS ---------------------------------
function getTradeConfigs(numConfigs: number): TradeConfig[] { function getTradeConfigs(numConfigs: number): TradeConfig[] {
const configs = []; const configs: TradeConfig[] = [];
for (let i = 0; i < numConfigs; i++) configs.push({}); for (let i = 0; i < numConfigs; i++) configs.push({});
return configs; return configs;
} }
@ -1646,14 +1648,14 @@ async function executeTrades(configs: TradeConfig[], concurrentTrades?: boolean)
for (let i = 0; i < configs.length; i++) Object.assign(configs[i], TestConfig.trade, Object.assign({}, configs[i])); for (let i = 0; i < configs.length; i++) Object.assign(configs[i], TestConfig.trade, Object.assign({}, configs[i]));
// wait for traders to have unlocked balance for trades // wait for traders to have unlocked balance for trades
const wallets = []; const wallets: any[] = [];
for (const config of configs) { for (const config of configs) {
if (config.awaitFundsToMakeOffer && config.makeOffer && !config.offerId) wallets.push(await getWallet(config.maker)); // TODO: this can add duplicate wallets and over-fund if (config.awaitFundsToMakeOffer && config.makeOffer && !config.offerId) wallets.push(await getWallet(config.maker!)); // TODO: this can add duplicate wallets and over-fund
if (config.awaitFundsToTakeOffer && config.takeOffer) wallets.push(await getWallet(config.taker)); if (config.awaitFundsToTakeOffer && config.takeOffer) wallets.push(await getWallet(config.taker!));
} }
let tradeAmount: bigint = undefined; let tradeAmount: bigint|undefined = undefined;
for (const config of configs) if (!tradeAmount || tradeAmount < config.amount) tradeAmount = config.amount; // use max amount for (const config of configs) if (!tradeAmount || tradeAmount < config.amount!) tradeAmount = config.amount; // use max amount
await fundOutputs(wallets, tradeAmount * BigInt("2"), configs.length); await fundOutputs(wallets, tradeAmount! * BigInt("2"), configs.length);
await wait(TestConfig.walletSyncPeriodMs); await wait(TestConfig.walletSyncPeriodMs);
// execute trades // execute trades
@ -1689,31 +1691,31 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
Object.assign(config, TestConfig.trade, Object.assign({}, config)); Object.assign(config, TestConfig.trade, Object.assign({}, config));
// fund maker and taker // fund maker and taker
const clientsToFund = []; const clientsToFund: HavenoClient[] = [];
const makingOffer = config.makeOffer && !config.offerId; const makingOffer = config.makeOffer && !config.offerId;
if (config.awaitFundsToMakeOffer && makingOffer) clientsToFund.push(config.maker); if (config.awaitFundsToMakeOffer && makingOffer) clientsToFund.push(config.maker!);
if (config.awaitFundsToTakeOffer && config.takeOffer) clientsToFund.push(config.taker); if (config.awaitFundsToTakeOffer && config.takeOffer) clientsToFund.push(config.taker!);
await waitForAvailableBalance(config.amount * BigInt("2"), ...clientsToFund); await waitForAvailableBalance(config.amount! * BigInt("2"), ...clientsToFund);
// determine buyer and seller // determine buyer and seller
let offer: OfferInfo = undefined; let offer: OfferInfo|undefined = undefined;
let isBuyerMaker = undefined; let isBuyerMaker = false;
if (makingOffer) { if (makingOffer) {
isBuyerMaker = "buy" === config.direction.toLowerCase() ? config.maker : config.taker; isBuyerMaker = "buy" === config.direction!.toLowerCase();
} else { } else {
offer = getOffer(await config.maker.getMyOffers(config.assetCode, config.direction), config.offerId); offer = getOffer(await config.maker!.getMyOffers(config.assetCode!, config.direction), config.offerId!);
if (!offer) { if (!offer) {
const trade = await config.maker.getTrade(config.offerId); const trade = await config.maker!.getTrade(config.offerId!);
offer = trade.getOffer(); offer = trade.getOffer();
} }
isBuyerMaker = "buy" === offer.getDirection().toLowerCase() ? config.maker : config.taker; isBuyerMaker = "buy" === offer!.getDirection().toLowerCase();
} }
config.buyer = isBuyerMaker ? config.maker : config.taker; config.buyer = isBuyerMaker ? config.maker : config.taker;
config.seller = isBuyerMaker ? config.taker : config.maker; config.seller = isBuyerMaker ? config.taker : config.maker;
// get info before trade // get info before trade
const buyerBalancesBefore = await config.buyer.getBalances(); const buyerBalancesBefore = await config.buyer!.getBalances();
const sellerBalancesBefore = await config.seller.getBalances(); const sellerBalancesBefore = await config.seller!.getBalances();
// make offer if configured // make offer if configured
if (makingOffer) { if (makingOffer) {
@ -1726,25 +1728,25 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
// TODO (woodser): test error message taking offer before posted // TODO (woodser): test error message taking offer before posted
// take offer or get existing trade // take offer or get existing trade
let trade = undefined; let trade: TradeInfo|undefined = undefined;
if (config.takeOffer) trade = await takeOffer(config); if (config.takeOffer) trade = await takeOffer(config);
else trade = await config.taker.getTrade(config.offerId); else trade = await config.taker!.getTrade(config.offerId!);
// test trader chat // test trader chat
if (config.testTraderChat) await testTradeChat(trade.getTradeId(), config.maker, config.taker); if (config.testTraderChat) await testTradeChat(trade.getTradeId(), config.maker!, config.taker!);
// shut down buyer and seller if configured // shut down buyer and seller if configured
const promises = []; const promises: Promise<void>[] = [];
const buyerAppName = config.buyer.getAppName(); const buyerAppName = config.buyer!.getAppName();
if (config.buyerOfflineAfterTake) { if (config.buyerOfflineAfterTake) {
promises.push(releaseHavenoProcess(config.buyer)); promises.push(releaseHavenoProcess(config.buyer!));
config.buyer = undefined; // TODO: don't track them separately? config.buyer = undefined; // TODO: don't track them separately?
if (isBuyerMaker) config.maker = undefined; if (isBuyerMaker) config.maker = undefined;
else config.taker = undefined; else config.taker = undefined;
} }
const sellerAppName = config.seller.getAppName(); const sellerAppName = config.seller!.getAppName();
if (config.sellerOfflineAfterTake) { if (config.sellerOfflineAfterTake) {
promises.push(releaseHavenoProcess(config.seller)); promises.push(releaseHavenoProcess(config.seller!));
config.seller = undefined; config.seller = undefined;
if (isBuyerMaker) config.taker = undefined; if (isBuyerMaker) config.taker = undefined;
else config.maker = undefined; else config.maker = undefined;
@ -1759,16 +1761,16 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
config.buyer = await initHaveno({appName: buyerAppName}); config.buyer = await initHaveno({appName: buyerAppName});
if (isBuyerMaker) config.maker = config.buyer; if (isBuyerMaker) config.maker = config.buyer;
else config.taker = config.buyer; else config.taker = config.buyer;
expect((await config.buyer.getTrade(offer.getId())).getPhase()).toEqual("DEPOSITS_UNLOCKED"); expect((await config.buyer.getTrade(offer!.getId())).getPhase()).toEqual("DEPOSITS_UNLOCKED");
} }
// test trade states // test trade states
await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2); // TODO: only wait here if buyer comes online? await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2); // TODO: only wait here if buyer comes online?
let fetchedTrade = await config.buyer.getTrade(config.offerId); let fetchedTrade = await config.buyer!.getTrade(config.offerId!);
expect(fetchedTrade.getIsDepositUnlocked()).toBe(true); expect(fetchedTrade.getIsDepositUnlocked()).toBe(true);
expect(fetchedTrade.getPhase()).toEqual("DEPOSITS_UNLOCKED"); expect(fetchedTrade.getPhase()).toEqual("DEPOSITS_UNLOCKED");
if (!config.sellerOfflineAfterTake) { if (!config.sellerOfflineAfterTake) {
fetchedTrade = await config.seller.getTrade(trade.getTradeId()); fetchedTrade = await config.seller!.getTrade(trade.getTradeId());
expect(fetchedTrade.getIsDepositUnlocked()).toBe(true); expect(fetchedTrade.getIsDepositUnlocked()).toBe(true);
expect(fetchedTrade.getPhase()).toEqual("DEPOSITS_UNLOCKED"); expect(fetchedTrade.getPhase()).toEqual("DEPOSITS_UNLOCKED");
} }
@ -1778,11 +1780,11 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
// open dispute(s) if configured // open dispute(s) if configured
if (!config.disputeOpener) { if (!config.disputeOpener) {
if (config.buyerOpensDisputeAfterDepositsUnlock) { if (config.buyerOpensDisputeAfterDepositsUnlock) {
await config.buyer.openDispute(config.offerId); await config.buyer!.openDispute(config.offerId!);
config.disputeOpener = config.buyer; config.disputeOpener = config.buyer;
} }
if (config.sellerOpensDisputeAfterDepositsUnlock) { if (config.sellerOpensDisputeAfterDepositsUnlock) {
await config.seller.openDispute(config.offerId); await config.seller!.openDispute(config.offerId!);
if (!config.disputeOpener) config.disputeOpener = config.seller; if (!config.disputeOpener) config.disputeOpener = config.seller;
} }
config.disputePeer = config.disputeOpener === config.buyer ? config.seller : config.buyer; config.disputePeer = config.disputeOpener === config.buyer ? config.seller : config.buyer;
@ -1792,19 +1794,19 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
// if dispute opened, resolve dispute if configured and return // if dispute opened, resolve dispute if configured and return
if (config.disputeOpener) { if (config.disputeOpener) {
if (config.resolveDispute) await resolveDispute(config); if (config.resolveDispute) await resolveDispute(config);
return config.offerId; return config.offerId!;
} }
// buyer confirms payment is sent // buyer confirms payment is sent
if (!config.buyerSendsPayment) return offer.getId(); if (!config.buyerSendsPayment) return offer!.getId();
HavenoUtils.log(1, "Buyer confirming payment sent"); HavenoUtils.log(1, "Buyer confirming payment sent");
await config.buyer.confirmPaymentStarted(trade.getTradeId()); await config.buyer!.confirmPaymentStarted(trade.getTradeId());
fetchedTrade = await config.buyer.getTrade(trade.getTradeId()); fetchedTrade = await config.buyer!.getTrade(trade.getTradeId());
expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT"); expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT");
// buyer goes offline if configured // buyer goes offline if configured
if (config.buyerOfflineAfterPaymentSent) { if (config.buyerOfflineAfterPaymentSent) {
await releaseHavenoProcess(config.buyer); await releaseHavenoProcess(config.buyer!);
config.buyer = undefined; config.buyer = undefined;
if (isBuyerMaker) config.maker = undefined; if (isBuyerMaker) config.maker = undefined;
else config.taker = undefined; else config.taker = undefined;
@ -1824,7 +1826,7 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT"); expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT");
// seller confirms payment is received // seller confirms payment is received
if (!config.sellerReceivesPayment) return offer.getId(); if (!config.sellerReceivesPayment) return offer!.getId();
HavenoUtils.log(1, "Seller confirming payment received"); HavenoUtils.log(1, "Seller confirming payment received");
await config.seller.confirmPaymentReceived(trade.getTradeId()); await config.seller.confirmPaymentReceived(trade.getTradeId());
fetchedTrade = await config.seller.getTrade(trade.getTradeId()); fetchedTrade = await config.seller.getTrade(trade.getTradeId());
@ -1840,29 +1842,29 @@ async function executeTrade(config?: TradeConfig): Promise<string> {
// test notifications // test notifications
await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2); await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2);
fetchedTrade = await config.buyer.getTrade(trade.getTradeId()); fetchedTrade = await config.buyer!.getTrade(trade.getTradeId());
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED"); // TODO: this should be WITHDRAW_COMPLETED? expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED"); // TODO: this should be WITHDRAW_COMPLETED?
fetchedTrade = await config.seller.getTrade(trade.getTradeId()); fetchedTrade = await config.seller.getTrade(trade.getTradeId());
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED"); expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
const arbitratorTrade = await config.arbitrator.getTrade(trade.getTradeId()); const arbitratorTrade = await config.arbitrator!.getTrade(trade.getTradeId());
expect(arbitratorTrade.getState()).toEqual("WITHDRAW_COMPLETED"); expect(arbitratorTrade.getState()).toEqual("WITHDRAW_COMPLETED");
// TODO: traders mark trades as complete // TODO: traders mark trades as complete
// test balances after payout tx unless other trades can interfere // test balances after payout tx unless other trades can interfere
if (!config.concurrentTrades) { if (!config.concurrentTrades) {
const buyerBalancesAfter = await config.buyer.getBalances(); const buyerBalancesAfter = await config.buyer!.getBalances();
const sellerBalancesAfter = await config.seller.getBalances(); const sellerBalancesAfter = await config.seller.getBalances();
// TODO: getBalance() = available + pending + reserved offers? would simplify this equation // TODO: getBalance() = available + pending + reserved offers? would simplify this equation
const buyerFee = BigInt(buyerBalancesBefore.getBalance()) + BigInt(buyerBalancesBefore.getReservedOfferBalance()) + HavenoUtils.centinerosToAtomicUnits(offer.getAmount()) - (BigInt(buyerBalancesAfter.getBalance()) + BigInt(buyerBalancesAfter.getReservedOfferBalance())); // buyer fee = total balance before + offer amount - total balance after const buyerFee = BigInt(buyerBalancesBefore.getBalance()) + BigInt(buyerBalancesBefore.getReservedOfferBalance()) + HavenoUtils.centinerosToAtomicUnits(offer!.getAmount()) - (BigInt(buyerBalancesAfter.getBalance()) + BigInt(buyerBalancesAfter.getReservedOfferBalance())); // buyer fee = total balance before + offer amount - total balance after
const sellerFee = BigInt(sellerBalancesBefore.getBalance()) + BigInt(sellerBalancesBefore.getReservedOfferBalance()) - HavenoUtils.centinerosToAtomicUnits(offer.getAmount()) - (BigInt(sellerBalancesAfter.getBalance()) + BigInt(sellerBalancesAfter.getReservedOfferBalance())); // seller fee = total balance before - offer amount - total balance after const sellerFee = BigInt(sellerBalancesBefore.getBalance()) + BigInt(sellerBalancesBefore.getReservedOfferBalance()) - HavenoUtils.centinerosToAtomicUnits(offer!.getAmount()) - (BigInt(sellerBalancesAfter.getBalance()) + BigInt(sellerBalancesAfter.getReservedOfferBalance())); // seller fee = total balance before - offer amount - total balance after
expect(buyerFee).toBeLessThanOrEqual(TestConfig.maxFee); expect(buyerFee).toBeLessThanOrEqual(TestConfig.maxFee);
expect(buyerFee).toBeGreaterThan(BigInt("0")); expect(buyerFee).toBeGreaterThan(BigInt("0"));
expect(sellerFee).toBeLessThanOrEqual(TestConfig.maxFee); expect(sellerFee).toBeLessThanOrEqual(TestConfig.maxFee);
expect(sellerFee).toBeGreaterThan(BigInt("0")); expect(sellerFee).toBeGreaterThan(BigInt("0"));
} }
return offer.getId(); return offer!.getId();
} }
async function makeOffer(config?: TradeConfig): Promise<OfferInfo> { async function makeOffer(config?: TradeConfig): Promise<OfferInfo> {
@ -1875,13 +1877,13 @@ async function makeOffer(config?: TradeConfig): Promise<OfferInfo> {
if (config.awaitFundsToMakeOffer) await waitForAvailableBalance(config.amount! * BigInt("2"), config.maker); if (config.awaitFundsToMakeOffer) await waitForAvailableBalance(config.amount! * BigInt("2"), config.maker);
// create payment account if not given // TODO: re-use existing payment account // create payment account if not given // TODO: re-use existing payment account
if (!config.makerPaymentAccountId) config.makerPaymentAccountId = (await createPaymentAccount(config.maker, config.assetCode!)).getId(); if (!config.makerPaymentAccountId) config.makerPaymentAccountId = (await createPaymentAccount(config.maker!, config.assetCode!)).getId();
// get unlocked balance before reserving offer // get unlocked balance before reserving offer
const unlockedBalanceBefore = BigInt((await config.maker.getBalances()).getAvailableBalance()); const unlockedBalanceBefore = BigInt((await config.maker!.getBalances()).getAvailableBalance());
// post offer // post offer
const offer: OfferInfo = await config.maker.postOffer( const offer: OfferInfo = await config.maker!.postOffer(
config.direction!, config.direction!,
config.amount!, config.amount!,
config.assetCode!, config.assetCode!,
@ -1894,15 +1896,15 @@ async function makeOffer(config?: TradeConfig): Promise<OfferInfo> {
testOffer(offer, config); testOffer(offer, config);
// offer is included in my offers only // offer is included in my offers only
if (!getOffer(await config.maker.getMyOffers(config.assetCode!, config.direction), offer.getId())) { if (!getOffer(await config.maker!.getMyOffers(config.assetCode!, config.direction), offer.getId())) {
console.warn("Offer is not included in my offers after posting, waiting up to 10 seconds"); console.warn("Offer is not included in my offers after posting, waiting up to 10 seconds");
await wait(10000); // TODO: remove this wait time await wait(10000); // TODO: remove this wait time
if (!getOffer(await config.maker.getMyOffers(config.assetCode!, config.direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in my offers"); if (!getOffer(await config.maker!.getMyOffers(config.assetCode!, config.direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in my offers");
} }
if (getOffer(await config.maker.getOffers(config.assetCode!, config.direction), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers"); if (getOffer(await config.maker!.getOffers(config.assetCode!, config.direction), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers");
// unlocked balance has decreased // unlocked balance has decreased
const unlockedBalanceAfter = BigInt((await config.maker.getBalances()).getAvailableBalance()); const unlockedBalanceAfter = BigInt((await config.maker!.getBalances()).getAvailableBalance());
if (offer.getState() === "SCHEDULED") { if (offer.getState() === "SCHEDULED") {
if (unlockedBalanceAfter !== unlockedBalanceBefore) throw new Error("Unlocked balance should not change for scheduled offer"); if (unlockedBalanceAfter !== unlockedBalanceBefore) throw new Error("Unlocked balance should not change for scheduled offer");
} else if (offer.getState() === "AVAILABLE") { } else if (offer.getState() === "AVAILABLE") {
@ -1922,7 +1924,7 @@ async function takeOffer(config: TradeConfig): Promise<TradeInfo> {
// taker sees offer // taker sees offer
if (!config.offerId) throw new Error("Must provide offer id"); if (!config.offerId) throw new Error("Must provide offer id");
const takerOffer = getOffer(await config.taker.getOffers(config.assetCode, config.direction), config.offerId); const takerOffer = getOffer(await config.taker!.getOffers(config.assetCode!, config.direction), config.offerId);
if (!takerOffer) throw new Error("Offer " + config.offerId + " was not found in taker's offers after posting"); if (!takerOffer) throw new Error("Offer " + config.offerId + " was not found in taker's offers after posting");
expect(takerOffer.getState()).toEqual("UNKNOWN"); // TODO: offer state should be known expect(takerOffer.getState()).toEqual("UNKNOWN"); // TODO: offer state should be known
@ -1930,23 +1932,23 @@ async function takeOffer(config: TradeConfig): Promise<TradeInfo> {
if (config.awaitFundsToTakeOffer) await waitForAvailableBalance(config.amount! * BigInt("2"), config.taker); if (config.awaitFundsToTakeOffer) await waitForAvailableBalance(config.amount! * BigInt("2"), config.taker);
// create payment account if not given // TODO: re-use existing payment account // create payment account if not given // TODO: re-use existing payment account
if (!config.takerPaymentAccountId) config.takerPaymentAccountId = (await createPaymentAccount(config.taker, config.assetCode!)).getId(); if (!config.takerPaymentAccountId) config.takerPaymentAccountId = (await createPaymentAccount(config.taker!, config.assetCode!)).getId();
// register to receive notifications // register to receive notifications
const makerNotifications: NotificationMessage[] = []; const makerNotifications: NotificationMessage[] = [];
const takerNotifications: NotificationMessage[] = []; const takerNotifications: NotificationMessage[] = [];
await config.maker.addNotificationListener(notification => { makerNotifications.push(notification); }); await config.maker!.addNotificationListener(notification => { makerNotifications.push(notification); });
await config.taker.addNotificationListener(notification => { takerNotifications.push(notification); }); await config.taker!.addNotificationListener(notification => { takerNotifications.push(notification); });
// take offer // take offer
const takerBalancesBefore: XmrBalanceInfo = await config.taker.getBalances(); const takerBalancesBefore: XmrBalanceInfo = await config.taker!.getBalances();
const startTime = Date.now(); const startTime = Date.now();
HavenoUtils.log(1, "Taking offer " + config.offerId); HavenoUtils.log(1, "Taking offer " + config.offerId);
const trade = await config.taker.takeOffer(config.offerId, config.takerPaymentAccountId!); const trade = await config.taker!.takeOffer(config.offerId, config.takerPaymentAccountId!);
HavenoUtils.log(1, "Done taking offer in " + (Date.now() - startTime) + " ms"); HavenoUtils.log(1, "Done taking offer in " + (Date.now() - startTime) + " ms");
// test taker's balances after taking trade // test taker's balances after taking trade
const takerBalancesAfter: XmrBalanceInfo = await config.taker.getBalances(); const takerBalancesAfter: XmrBalanceInfo = await config.taker!.getBalances();
expect(BigInt(takerBalancesAfter.getAvailableBalance())).toBeLessThan(BigInt(takerBalancesBefore.getAvailableBalance())); expect(BigInt(takerBalancesAfter.getAvailableBalance())).toBeLessThan(BigInt(takerBalancesBefore.getAvailableBalance()));
expect(BigInt(takerBalancesAfter.getReservedOfferBalance()) + BigInt(takerBalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(takerBalancesBefore.getReservedOfferBalance()) + BigInt(takerBalancesBefore.getReservedTradeBalance())); expect(BigInt(takerBalancesAfter.getReservedOfferBalance()) + BigInt(takerBalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(takerBalancesBefore.getReservedOfferBalance()) + BigInt(takerBalancesBefore.getReservedTradeBalance()));
@ -1961,14 +1963,14 @@ async function takeOffer(config: TradeConfig): Promise<TradeInfo> {
// maker is notified of balance change // maker is notified of balance change
// taker can get trade // taker can get trade
let fetchedTrade: TradeInfo = await config.taker.getTrade(trade.getTradeId()); let fetchedTrade: TradeInfo = await config.taker!.getTrade(trade.getTradeId());
assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED"], fetchedTrade.getPhase())); assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED"], fetchedTrade.getPhase()));
// TODO: test fetched trade // TODO: test fetched trade
// taker is notified of balance change // taker is notified of balance change
// maker can get trade // maker can get trade
fetchedTrade = await config.maker.getTrade(trade.getTradeId()); fetchedTrade = await config.maker!.getTrade(trade.getTradeId());
assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED"], fetchedTrade.getPhase())); assert(GenUtils.arrayContains(["DEPOSITS_PUBLISHED", "DEPOSITS_CONFIRMED"], fetchedTrade.getPhase()));
return trade; return trade;
} }
@ -1976,14 +1978,14 @@ async function takeOffer(config: TradeConfig): Promise<TradeInfo> {
async function testOpenDispute(config: TradeConfig) { async function testOpenDispute(config: TradeConfig) {
// test dispute state // test dispute state
const openerDispute = await config.disputeOpener.getDispute(config.offerId); const openerDispute = await config.disputeOpener!.getDispute(config.offerId!);
expect(openerDispute.getTradeId()).toEqual(config.offerId); expect(openerDispute.getTradeId()).toEqual(config.offerId);
expect(openerDispute.getIsOpener()).toBe(true); expect(openerDispute.getIsOpener()).toBe(true);
expect(openerDispute.getDisputeOpenerIsBuyer()).toBe(config.disputeOpener === config.buyer); expect(openerDispute.getDisputeOpenerIsBuyer()).toBe(config.disputeOpener === config.buyer);
// get non-existing dispute should fail // get non-existing dispute should fail
try { try {
await config.disputeOpener.getDispute("invalid"); await config.disputeOpener!.getDispute("invalid");
throw new Error("get dispute with invalid id should fail"); throw new Error("get dispute with invalid id should fail");
} catch (err: any) { } catch (err: any) {
assert.equal(err.message, "dispute for trade id 'invalid' not found"); assert.equal(err.message, "dispute for trade id 'invalid' not found");
@ -1991,12 +1993,12 @@ async function testOpenDispute(config: TradeConfig) {
// peer sees the dispute // peer sees the dispute
await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs); await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs);
const peerDispute = await config.disputePeer.getDispute(config.offerId); const peerDispute = await config.disputePeer!.getDispute(config.offerId!);
expect(peerDispute.getTradeId()).toEqual(config.offerId); expect(peerDispute.getTradeId()).toEqual(config.offerId);
expect(peerDispute.getIsOpener()).toBe(false); expect(peerDispute.getIsOpener()).toBe(false);
// arbitrator sees both disputes // arbitrator sees both disputes
const disputes = await config.arbitrator.getDisputes(); const disputes = await config.arbitrator!.getDisputes();
expect(disputes.length).toBeGreaterThanOrEqual(2); expect(disputes.length).toBeGreaterThanOrEqual(2);
const arbDisputePeer = disputes.find(d => d.getId() === peerDispute.getId()); const arbDisputePeer = disputes.find(d => d.getId() === peerDispute.getId());
assert(arbDisputePeer); assert(arbDisputePeer);
@ -2007,14 +2009,14 @@ async function testOpenDispute(config: TradeConfig) {
const disputeOpenerNotifications: NotificationMessage[] = []; const disputeOpenerNotifications: NotificationMessage[] = [];
const disputePeerNotifications: NotificationMessage[] = []; const disputePeerNotifications: NotificationMessage[] = [];
const arbitratorNotifications: NotificationMessage[] = []; const arbitratorNotifications: NotificationMessage[] = [];
await config.disputeOpener.addNotificationListener(notification => { HavenoUtils.log(3, "Dispute opener received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); disputeOpenerNotifications.push(notification); }); await config.disputeOpener!.addNotificationListener(notification => { HavenoUtils.log(3, "Dispute opener received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); disputeOpenerNotifications.push(notification); });
await config.disputePeer.addNotificationListener(notification => { HavenoUtils.log(3, "Dispute peer received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); disputePeerNotifications.push(notification); }); await config.disputePeer!.addNotificationListener(notification => { HavenoUtils.log(3, "Dispute peer received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); disputePeerNotifications.push(notification); });
await arbitrator.addNotificationListener(notification => { HavenoUtils.log(3, "Arbitrator received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); arbitratorNotifications.push(notification); }); await arbitrator.addNotificationListener(notification => { HavenoUtils.log(3, "Arbitrator received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); arbitratorNotifications.push(notification); });
// arbitrator sends chat messages to traders // arbitrator sends chat messages to traders
HavenoUtils.log(1, "Testing chat messages"); HavenoUtils.log(1, "Testing chat messages");
await config.arbitrator.sendDisputeChatMessage(arbDisputeOpener!.getId(), "Arbitrator chat message to dispute opener", []); await config.arbitrator!.sendDisputeChatMessage(arbDisputeOpener!.getId(), "Arbitrator chat message to dispute opener", []);
await config.arbitrator.sendDisputeChatMessage(arbDisputePeer!.getId(), "Arbitrator chat message to dispute peer", []); await config.arbitrator!.sendDisputeChatMessage(arbDisputePeer!.getId(), "Arbitrator chat message to dispute peer", []);
// traders reply to arbitrator chat messages // traders reply to arbitrator chat messages
await wait(TestConfig.maxTimePeerNoticeMs); // wait for arbitrator's message to arrive await wait(TestConfig.maxTimePeerNoticeMs); // wait for arbitrator's message to arrive
@ -2027,14 +2029,14 @@ async function testOpenDispute(config: TradeConfig) {
attachment2.setBytes(bytes2); attachment2.setBytes(bytes2);
attachment2.setFileName("proof.png"); attachment2.setFileName("proof.png");
HavenoUtils.log(2, "Dispute opener sending chat message to arbitrator. tradeId=" + config.offerId + ", disputeId=" + openerDispute.getId()); HavenoUtils.log(2, "Dispute opener sending chat message to arbitrator. tradeId=" + config.offerId + ", disputeId=" + openerDispute.getId());
await config.disputeOpener.sendDisputeChatMessage(openerDispute.getId(), "Dispute opener chat message", [attachment, attachment2]); await config.disputeOpener!.sendDisputeChatMessage(openerDispute.getId(), "Dispute opener chat message", [attachment, attachment2]);
await wait(TestConfig.maxTimePeerNoticeMs); // wait for user2's message to arrive await wait(TestConfig.maxTimePeerNoticeMs); // wait for user2's message to arrive
HavenoUtils.log(2, "Dispute peer sending chat message to arbitrator. tradeId=" + config.offerId + ", disputeId=" + peerDispute.getId()); HavenoUtils.log(2, "Dispute peer sending chat message to arbitrator. tradeId=" + config.offerId + ", disputeId=" + peerDispute.getId());
await config.disputePeer.sendDisputeChatMessage(peerDispute.getId(), "Dispute peer chat message", []); await config.disputePeer!.sendDisputeChatMessage(peerDispute.getId(), "Dispute peer chat message", []);
// test trader chat messages // test trader chat messages
await wait(TestConfig.maxTimePeerNoticeMs); await wait(TestConfig.maxTimePeerNoticeMs);
let dispute = await config.disputeOpener.getDispute(config.offerId); let dispute = await config.disputeOpener!.getDispute(config.offerId!);
let messages = dispute.getChatMessageList(); let messages = dispute.getChatMessageList();
expect(messages.length).toEqual(3); // 1st message is the system message expect(messages.length).toEqual(3); // 1st message is the system message
expect(messages[1].getMessage()).toEqual("Arbitrator chat message to dispute opener"); expect(messages[1].getMessage()).toEqual("Arbitrator chat message to dispute opener");
@ -2045,7 +2047,7 @@ async function testOpenDispute(config: TradeConfig) {
expect(attachments[0].getBytes()).toEqual(bytes); expect(attachments[0].getBytes()).toEqual(bytes);
expect(attachments[1].getFileName()).toEqual("proof.png"); expect(attachments[1].getFileName()).toEqual("proof.png");
expect(attachments[1].getBytes()).toEqual(bytes2); expect(attachments[1].getBytes()).toEqual(bytes2);
dispute = await config.disputePeer.getDispute(config.offerId); dispute = await config.disputePeer!.getDispute(config.offerId!);
messages = dispute.getChatMessageList(); messages = dispute.getChatMessageList();
expect(messages.length).toEqual(3); expect(messages.length).toEqual(3);
expect(messages[1].getMessage()).toEqual("Arbitrator chat message to dispute peer"); expect(messages[1].getMessage()).toEqual("Arbitrator chat message to dispute peer");
@ -2075,11 +2077,11 @@ async function testOpenDispute(config: TradeConfig) {
async function resolveDispute(config: TradeConfig) { async function resolveDispute(config: TradeConfig) {
// award too little to loser // award too little to loser
const offer = (await config.maker.getTrade(config.offerId)).getOffer(); const offer = (await config.maker!.getTrade(config.offerId!)).getOffer();
const tradeAmount: bigint = HavenoUtils.centinerosToAtomicUnits(offer.getAmount()); const tradeAmount: bigint = HavenoUtils.centinerosToAtomicUnits(offer!.getAmount());
const customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offer.getBuyerSecurityDeposit() + offer.getSellerSecurityDeposit()) - BigInt("10000"); const customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offer!.getBuyerSecurityDeposit() + offer!.getSellerSecurityDeposit()) - BigInt("10000");
try { try {
await arbitrator.resolveDispute(config.offerId, config.disputeWinner, config.disputeReason, "Loser gets too little", customWinnerAmount); await arbitrator.resolveDispute(config.offerId!, config.disputeWinner!, config.disputeReason!, "Loser gets too little", customWinnerAmount);
throw new Error("Should have failed resolving dispute with insufficient loser payout"); throw new Error("Should have failed resolving dispute with insufficient loser payout");
} catch (err: any) { } catch (err: any) {
assert.equal(err.message, "Loser payout is too small to cover the mining fee"); assert.equal(err.message, "Loser payout is too small to cover the mining fee");
@ -2088,29 +2090,29 @@ async function resolveDispute(config: TradeConfig) {
// resolve dispute according to configuration // resolve dispute according to configuration
const winner = config.disputeWinner === DisputeResult.Winner.BUYER ? config.buyer : config.seller; const winner = config.disputeWinner === DisputeResult.Winner.BUYER ? config.buyer : config.seller;
const loser = config.disputeWinner === DisputeResult.Winner.BUYER ? config.seller : config.buyer; const loser = config.disputeWinner === DisputeResult.Winner.BUYER ? config.seller : config.buyer;
const winnerBalancesBefore = await winner.getBalances(); const winnerBalancesBefore = await winner!.getBalances();
const loserBalancesBefore = await loser.getBalances(); const loserBalancesBefore = await loser!.getBalances();
HavenoUtils.log(1, "Resolving dispute for trade " + config.offerId); HavenoUtils.log(1, "Resolving dispute for trade " + config.offerId);
const startTime = Date.now(); const startTime = Date.now();
await arbitrator.resolveDispute(config.offerId, config.disputeWinner, config.disputeReason, config.disputeSummary, config.disputeWinnerAmount); await arbitrator.resolveDispute(config.offerId!, config.disputeWinner!, config.disputeReason!, config.disputeSummary!, config.disputeWinnerAmount);
HavenoUtils.log(1, "Done resolving dispute (" + (Date.now() - startTime) + ")"); HavenoUtils.log(1, "Done resolving dispute (" + (Date.now() - startTime) + ")");
// test resolved dispute // test resolved dispute
await wait(TestConfig.maxWalletStartupMs); await wait(TestConfig.maxWalletStartupMs);
let dispute = await config.disputeOpener.getDispute(config.offerId); let dispute = await config.disputeOpener!.getDispute(config.offerId!);
expect(dispute.getIsClosed()).toBe(true); expect(dispute.getIsClosed()).toBe(true);
dispute = await config.disputePeer.getDispute(config.offerId); dispute = await config.disputePeer!.getDispute(config.offerId!);
expect(dispute.getIsClosed()).toBe(true); expect(dispute.getIsClosed()).toBe(true);
// check balances after payout tx unless concurrent trades // check balances after payout tx unless concurrent trades
if (config.concurrentTrades) return; if (config.concurrentTrades) return;
await wait(TestConfig.walletSyncPeriodMs * 2); await wait(TestConfig.walletSyncPeriodMs * 2);
const winnerBalancesAfter = await winner.getBalances(); const winnerBalancesAfter = await winner!.getBalances();
const loserBalancesAfter = await loser.getBalances(); const loserBalancesAfter = await loser!.getBalances();
const winnerDifference = BigInt(winnerBalancesAfter.getBalance()) - BigInt(winnerBalancesBefore.getBalance()); const winnerDifference = BigInt(winnerBalancesAfter.getBalance()) - BigInt(winnerBalancesBefore.getBalance());
const loserDifference = BigInt(loserBalancesAfter.getBalance()) - BigInt(loserBalancesBefore.getBalance()); const loserDifference = BigInt(loserBalancesAfter.getBalance()) - BigInt(loserBalancesBefore.getBalance());
const winnerSecurityDeposit = HavenoUtils.centinerosToAtomicUnits(config.disputeWinner === DisputeResult.Winner.BUYER ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit()) const winnerSecurityDeposit = HavenoUtils.centinerosToAtomicUnits(config.disputeWinner === DisputeResult.Winner.BUYER ? offer!.getBuyerSecurityDeposit() : offer!.getSellerSecurityDeposit())
const loserSecurityDeposit = HavenoUtils.centinerosToAtomicUnits(config.disputeWinner === DisputeResult.Winner.BUYER ? offer.getSellerSecurityDeposit() : offer.getBuyerSecurityDeposit()); const loserSecurityDeposit = HavenoUtils.centinerosToAtomicUnits(config.disputeWinner === DisputeResult.Winner.BUYER ? offer!.getSellerSecurityDeposit() : offer!.getBuyerSecurityDeposit());
const winnerPayout = config.disputeWinnerAmount ? config.disputeWinnerAmount : tradeAmount + winnerSecurityDeposit; // TODO: this assumes security deposit is returned to winner, but won't be the case if payment sent const winnerPayout = config.disputeWinnerAmount ? config.disputeWinnerAmount : tradeAmount + winnerSecurityDeposit; // TODO: this assumes security deposit is returned to winner, but won't be the case if payment sent
const loserPayout = loserSecurityDeposit; const loserPayout = loserSecurityDeposit;
expect(winnerDifference).toEqual(winnerPayout); expect(winnerDifference).toEqual(winnerPayout);
@ -2385,7 +2387,7 @@ async function prepareForTrading(numTrades: number, ...havenods: HavenoClient[])
// fund wallets // fund wallets
const tradeAmount = BigInt("250000000000"); const tradeAmount = BigInt("250000000000");
const wallets = []; const wallets: Promise<any>[] = [];
for (const havenod of havenods) wallets.push(await getWallet(havenod)); for (const havenod of havenods) wallets.push(await getWallet(havenod));
await fundOutputs(wallets, tradeAmount * BigInt("2"), numTrades); await fundOutputs(wallets, tradeAmount * BigInt("2"), numTrades);
@ -2550,7 +2552,7 @@ async function fundOutputs(wallets: any[], amt: bigint, numOutputs?: number, wai
if (waitForUnlock === undefined) waitForUnlock = true; if (waitForUnlock === undefined) waitForUnlock = true;
// collect destinations // collect destinations
const destinations = []; const destinations: any[] = [];
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++) {
@ -2615,8 +2617,8 @@ function getNotifications(notifications: NotificationMessage[], notificationType
if (notification.getType() !== notificationType) continue; if (notification.getType() !== notificationType) continue;
if (tradeId) { if (tradeId) {
let found = false; let found = false;
if (notification.getTrade() && notification.getTrade().getTradeId() === tradeId) found = true; if (notification.getTrade() && notification.getTrade()!.getTradeId() === tradeId) found = true;
if (notification.getChatMessage() && notification.getChatMessage().getTradeId() === tradeId) found = true; if (notification.getChatMessage() && notification.getChatMessage()!.getTradeId() === tradeId) found = true;
if (!found) continue; if (!found) continue;
} }
filteredNotifications.push(notification); filteredNotifications.push(notification);
@ -2624,7 +2626,7 @@ function getNotifications(notifications: NotificationMessage[], notificationType
return filteredNotifications; return filteredNotifications;
} }
function getConnection(connections: UrlConnection[], url: string): UrlConnection | undefined { function getConnection(connections: UrlConnection[], url: string): UrlConnection|undefined {
for (const connection of connections) if (connection.getUrl() === url) return connection; for (const connection of connections) if (connection.getUrl() === url) return connection;
return undefined; return undefined;
} }
@ -2713,7 +2715,7 @@ function isCrypto(assetCode: string) {
return getCryptoAddress(assetCode) !== undefined; return getCryptoAddress(assetCode) !== undefined;
} }
function getCryptoAddress(currencyCode: string): string | undefined { function getCryptoAddress(currencyCode: string): string|undefined {
for (const cryptoAddress of TestConfig.cryptoAddresses) { for (const cryptoAddress of TestConfig.cryptoAddresses) {
if (cryptoAddress.currencyCode === currencyCode.toUpperCase()) return cryptoAddress.address; if (cryptoAddress.currencyCode === currencyCode.toUpperCase()) return cryptoAddress.address;
} }
@ -2738,7 +2740,7 @@ async function createCryptoPaymentAccount(trader: HavenoClient, currencyCode = "
throw new Error("No test config for crypto: " + currencyCode); throw new Error("No test config for crypto: " + currencyCode);
} }
function getOffer(offers: OfferInfo[], id: string): OfferInfo | undefined { function getOffer(offers: OfferInfo[], id: string): OfferInfo|undefined {
return offers.find(offer => offer.getId() === id); return offers.find(offer => offer.getId() === id);
} }
@ -2842,7 +2844,7 @@ function getValidFormInput(fieldId: PaymentAccountFormField.FieldId, form: Payme
case PaymentAccountFormField.FieldId.COUNTRY: case PaymentAccountFormField.FieldId.COUNTRY:
case PaymentAccountFormField.FieldId.BANK_COUNTRY_CODE: case PaymentAccountFormField.FieldId.BANK_COUNTRY_CODE:
case PaymentAccountFormField.FieldId.INTERMEDIARY_COUNTRY_CODE: case PaymentAccountFormField.FieldId.INTERMEDIARY_COUNTRY_CODE:
return field.getSupportedCountriesList().length ? field.getSupportedCountriesList().at(0).getCode() : "FR"; return field.getSupportedCountriesList().length ? field.getSupportedCountriesList().at(0)!.getCode() : "FR";
case PaymentAccountFormField.FieldId.EMAIL: case PaymentAccountFormField.FieldId.EMAIL:
return "jdoe@no.com"; return "jdoe@no.com";
case PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR: case PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR:
@ -3035,77 +3037,77 @@ function getInvalidFormInput(form: PaymentAccountForm, fieldId: PaymentAccountFo
function testFiatAccount(account: PaymentAccount, form: PaymentAccountForm) { function testFiatAccount(account: PaymentAccount, form: PaymentAccountForm) {
expect(account.getAccountName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_NAME).getValue()); // TODO: using number as payment method, account payload's account name = user name expect(account.getAccountName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_NAME).getValue()); // TODO: using number as payment method, account payload's account name = user name
const isCountryBased = account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload() !== undefined; const isCountryBased = account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload() !== undefined;
if (isCountryBased) expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.COUNTRY).getValue()); if (isCountryBased) expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.COUNTRY).getValue());
switch (form.getId()) { switch (form.getId()) {
case PaymentAccountForm.FormId.REVOLUT: case PaymentAccountForm.FormId.REVOLUT:
expect(account.getPaymentAccountPayload().getRevolutAccountPayload().getUserName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.USER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getRevolutAccountPayload()!.getUserName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.USER_NAME).getValue());
expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue()); expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue());
break; break;
case PaymentAccountForm.FormId.SEPA: case PaymentAccountForm.FormId.SEPA:
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
//expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload().getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue()); // TODO: if this is deprecated, remove from sepa model //expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload().getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue()); // TODO: if this is deprecated, remove from sepa model
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload().getIban()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.IBAN).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload()!.getIban()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.IBAN).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload().getBic()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BIC).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaAccountPayload()!.getBic()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BIC).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getAcceptedCountryCodesList().join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCEPTED_COUNTRY_CODES).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getAcceptedCountryCodesList().join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCEPTED_COUNTRY_CODES).getValue());
break; break;
case PaymentAccountForm.FormId.SEPA_INSTANT: case PaymentAccountForm.FormId.SEPA_INSTANT:
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload().getIban()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.IBAN).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload()!.getIban()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.IBAN).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload().getBic()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BIC).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getSepaInstantAccountPayload()!.getBic()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BIC).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getAcceptedCountryCodesList().join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCEPTED_COUNTRY_CODES).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getAcceptedCountryCodesList().join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCEPTED_COUNTRY_CODES).getValue());
break; break;
case PaymentAccountForm.FormId.TRANSFERWISE: case PaymentAccountForm.FormId.TRANSFERWISE:
expect(account.getPaymentAccountPayload().getTransferwiseAccountPayload().getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue()); expect(account.getPaymentAccountPayload()!.getTransferwiseAccountPayload()!.getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue());
break; break;
case PaymentAccountForm.FormId.CLEAR_X_CHANGE: case PaymentAccountForm.FormId.CLEAR_X_CHANGE:
expect(account.getPaymentAccountPayload().getClearXchangeAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getClearXchangeAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
expect(account.getPaymentAccountPayload().getClearXchangeAccountPayload().getEmailOrMobileNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR).getValue()); expect(account.getPaymentAccountPayload()!.getClearXchangeAccountPayload()!.getEmailOrMobileNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL_OR_MOBILE_NR).getValue());
break; break;
case PaymentAccountForm.FormId.SWIFT: case PaymentAccountForm.FormId.SWIFT:
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBankSwiftCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_SWIFT_CODE).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBankSwiftCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_SWIFT_CODE).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBankCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_COUNTRY_CODE).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBankCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_COUNTRY_CODE).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBankName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBankName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_NAME).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBankBranch()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_BRANCH).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBankBranch()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_BRANCH).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBankAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_ADDRESS).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBankAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BANK_ADDRESS).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getIntermediarySwiftCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_SWIFT_CODE).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getIntermediarySwiftCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_SWIFT_CODE).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getIntermediaryCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_COUNTRY_CODE).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getIntermediaryCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_COUNTRY_CODE).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getIntermediaryName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getIntermediaryName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_NAME).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getIntermediaryBranch()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_BRANCH).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getIntermediaryBranch()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_BRANCH).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getIntermediaryAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_ADDRESS).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getIntermediaryAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.INTERMEDIARY_ADDRESS).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBeneficiaryName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBeneficiaryName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_NAME).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBeneficiaryAccountNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_ACCOUNT_NR).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBeneficiaryAccountNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_ACCOUNT_NR).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBeneficiaryAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_ADDRESS).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBeneficiaryAddress()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_ADDRESS).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBeneficiaryCity()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_CITY).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBeneficiaryCity()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_CITY).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getBeneficiaryPhone()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_PHONE).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getBeneficiaryPhone()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.BENEFICIARY_PHONE).getValue());
expect(account.getPaymentAccountPayload().getSwiftAccountPayload().getSpecialInstructions()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.SPECIAL_INSTRUCTIONS).getValue()); expect(account.getPaymentAccountPayload()!.getSwiftAccountPayload()!.getSpecialInstructions()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.SPECIAL_INSTRUCTIONS).getValue());
break; break;
case PaymentAccountForm.FormId.F2F: case PaymentAccountForm.FormId.F2F:
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload().getCity()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.CITY).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload()!.getCity()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.CITY).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload().getContact()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.CONTACT).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload()!.getContact()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.CONTACT).getValue());
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload().getExtraInfo()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EXTRA_INFO).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getF2fAccountPayload()!.getExtraInfo()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EXTRA_INFO).getValue());
break; break;
case PaymentAccountForm.FormId.STRIKE: case PaymentAccountForm.FormId.STRIKE:
expect(account.getPaymentAccountPayload().getCountryBasedPaymentAccountPayload()!.getStrikeAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getCountryBasedPaymentAccountPayload()!.getStrikeAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
break; break;
case PaymentAccountForm.FormId.MONEY_GRAM: case PaymentAccountForm.FormId.MONEY_GRAM:
expect(account.getPaymentAccountPayload().getMoneyGramAccountPayload().getCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.COUNTRY).getValue()); // TODO: ok to not be CountryBasedPaymentAccountPayload? expect(account.getPaymentAccountPayload()!.getMoneyGramAccountPayload()!.getCountryCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.COUNTRY).getValue()); // TODO: ok to not be CountryBasedPaymentAccountPayload?
expect(account.getPaymentAccountPayload().getMoneyGramAccountPayload().getState()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.STATE).getValue()); expect(account.getPaymentAccountPayload()!.getMoneyGramAccountPayload()!.getState()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.STATE).getValue());
expect(account.getPaymentAccountPayload().getMoneyGramAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getMoneyGramAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
expect(account.getPaymentAccountPayload().getMoneyGramAccountPayload().getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue()); expect(account.getPaymentAccountPayload()!.getMoneyGramAccountPayload()!.getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue());
expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue()); expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue());
break; break;
case PaymentAccountForm.FormId.FASTER_PAYMENTS: case PaymentAccountForm.FormId.FASTER_PAYMENTS:
expect(account.getPaymentAccountPayload().getFasterPaymentsAccountPayload().getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue()); expect(account.getPaymentAccountPayload()!.getFasterPaymentsAccountPayload()!.getHolderName()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.HOLDER_NAME).getValue());
expect(account.getPaymentAccountPayload().getFasterPaymentsAccountPayload().getSortCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.SORT_CODE).getValue()); expect(account.getPaymentAccountPayload()!.getFasterPaymentsAccountPayload()!.getSortCode()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.SORT_CODE).getValue());
expect(account.getPaymentAccountPayload().getFasterPaymentsAccountPayload().getAccountNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_NR).getValue()); expect(account.getPaymentAccountPayload()!.getFasterPaymentsAccountPayload()!.getAccountNr()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_NR).getValue());
break; break;
case PaymentAccountForm.FormId.UPHOLD: case PaymentAccountForm.FormId.UPHOLD:
expect(account.getPaymentAccountPayload().getUpholdAccountPayload().getAccountOwner()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_OWNER).getValue()); expect(account.getPaymentAccountPayload()!.getUpholdAccountPayload()!.getAccountOwner()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_OWNER).getValue());
expect(account.getPaymentAccountPayload().getUpholdAccountPayload().getAccountId()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_ID).getValue()); expect(account.getPaymentAccountPayload()!.getUpholdAccountPayload()!.getAccountId()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.ACCOUNT_ID).getValue());
break; break;
case PaymentAccountForm.FormId.PAXUM: case PaymentAccountForm.FormId.PAXUM:
expect(account.getPaymentAccountPayload().getPaxumAccountPayload().getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue()); expect(account.getPaymentAccountPayload()!.getPaxumAccountPayload()!.getEmail()).toEqual(getFormField(form, PaymentAccountFormField.FieldId.EMAIL).getValue());
expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue()); expect(account.getTradeCurrenciesList().map(currency => currency.getCode()).join(",")).toEqual(getFormField(form, PaymentAccountFormField.FieldId.TRADE_CURRENCIES).getValue());
break; break;
default: default: