mirror of
https://github.com/haveno-dex/haveno-ts.git
synced 2025-02-02 17:44:40 -05:00
update payment methods
This commit is contained in:
parent
6fa1db708f
commit
1f5ee145ad
@ -30,7 +30,7 @@ import console from "console"; // import console because jest swallows messages
|
||||
// ------------------------------ TEST CONFIG ---------------------------------
|
||||
|
||||
const TestConfig = {
|
||||
logLevel: 0,
|
||||
logLevel: 1,
|
||||
moneroBinsDir: "../haveno/.localnet",
|
||||
testDataDir: "./testdata",
|
||||
networkType: monerojs.MoneroNetworkType.STAGENET,
|
||||
@ -308,7 +308,7 @@ test("Can manage an account", async () => {
|
||||
assert(size > 0);
|
||||
|
||||
// delete account which shuts down server
|
||||
await charlie.deleteAccount();
|
||||
await charlie.deleteAccount(); // TODO: support deleting and restoring account without shutting down server, #310
|
||||
assert(!await charlie.isConnectedToDaemon());
|
||||
await releaseHavenoProcess(charlie);
|
||||
|
||||
@ -709,7 +709,7 @@ test("Can get market prices", async () => {
|
||||
const price = await alice.getPrice(assetCode);
|
||||
expect(price).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
|
||||
// test that prices are reasonable
|
||||
const usd = await alice.getPrice("USD");
|
||||
expect(usd).toBeGreaterThan(50);
|
||||
@ -720,7 +720,7 @@ test("Can get market prices", async () => {
|
||||
const btc = await alice.getPrice("BTC");
|
||||
expect(btc).toBeGreaterThan(0.0004)
|
||||
expect(btc).toBeLessThan(0.4);
|
||||
|
||||
|
||||
// test invalid currency
|
||||
await expect(async () => { await alice.getPrice("INVALID_CURRENCY") })
|
||||
.rejects
|
||||
@ -769,20 +769,19 @@ test("Can get market depth", async () => {
|
||||
expect(marketDepth.getSellPricesList().length).toEqual(marketDepth.getSellDepthList().length);
|
||||
|
||||
// test buy prices and depths
|
||||
const priceDivisor = 100000000; // TODO: offer price = price * 100000000
|
||||
const buyOffers = (await alice.getOffers(assetCode, "buy")).concat(await alice.getMyOffers(assetCode, "buy")).sort(function(a, b) { return a.getPrice() - b.getPrice() });
|
||||
expect(marketDepth.getBuyPricesList()[0]).toEqual(1 / (buyOffers[0].getPrice() / priceDivisor)); // TODO: price when posting offer is reversed. this assumes crypto counter currency
|
||||
expect(marketDepth.getBuyPricesList()[1]).toEqual(1 / (buyOffers[1].getPrice() / priceDivisor));
|
||||
expect(marketDepth.getBuyPricesList()[2]).toEqual(1 / (buyOffers[2].getPrice() / priceDivisor));
|
||||
const buyOffers = (await alice.getOffers(assetCode, "buy")).concat(await alice.getMyOffers(assetCode, "buy")).sort(function(a, b) { return parseFloat(a.getPrice()) - parseFloat(b.getPrice()) });
|
||||
expect(marketDepth.getBuyPricesList()[0]).toEqual(1 / parseFloat(buyOffers[0].getPrice())); // TODO: price when posting offer is reversed. this assumes crypto counter currency
|
||||
expect(marketDepth.getBuyPricesList()[1]).toEqual(1 / parseFloat(buyOffers[1].getPrice()));
|
||||
expect(marketDepth.getBuyPricesList()[2]).toEqual(1 / parseFloat(buyOffers[2].getPrice()));
|
||||
expect(marketDepth.getBuyDepthList()[0]).toEqual(0.15);
|
||||
expect(marketDepth.getBuyDepthList()[1]).toEqual(0.30);
|
||||
expect(marketDepth.getBuyDepthList()[2]).toEqual(0.65);
|
||||
|
||||
// test sell prices and depths
|
||||
const sellOffers = (await alice.getOffers(assetCode, "sell")).concat(await alice.getMyOffers(assetCode, "sell")).sort(function(a, b) { return b.getPrice() - a.getPrice() });
|
||||
expect(marketDepth.getSellPricesList()[0]).toEqual(1 / (sellOffers[0].getPrice() / priceDivisor));
|
||||
expect(marketDepth.getSellPricesList()[1]).toEqual(1 / (sellOffers[1].getPrice() / priceDivisor));
|
||||
expect(marketDepth.getSellPricesList()[2]).toEqual(1 / (sellOffers[2].getPrice() / priceDivisor));
|
||||
const sellOffers = (await alice.getOffers(assetCode, "sell")).concat(await alice.getMyOffers(assetCode, "sell")).sort(function(a, b) { return parseFloat(b.getPrice()) - parseFloat(a.getPrice()) });
|
||||
expect(marketDepth.getSellPricesList()[0]).toEqual(1 / parseFloat(sellOffers[0].getPrice()));
|
||||
expect(marketDepth.getSellPricesList()[1]).toEqual(1 / parseFloat(sellOffers[1].getPrice()));
|
||||
expect(marketDepth.getSellPricesList()[2]).toEqual(1 / parseFloat(sellOffers[2].getPrice()));
|
||||
expect(marketDepth.getSellDepthList()[0]).toEqual(0.3);
|
||||
expect(marketDepth.getSellDepthList()[1]).toEqual(0.6);
|
||||
expect(marketDepth.getSellDepthList()[2]).toEqual(1);
|
||||
@ -860,6 +859,8 @@ test("Can create fiat payment accounts", async () => {
|
||||
const accountForm = await alice.getPaymentAccountForm(paymentMethodId);
|
||||
|
||||
// edit form
|
||||
accountForm.tradeCurrencies ="gbp,eur,usd";
|
||||
accountForm.selectedTradeCurrency = "usd";
|
||||
accountForm.accountName = "Revolut account " + GenUtils.getUUID();
|
||||
accountForm.userName = "user123";
|
||||
|
||||
@ -927,28 +928,50 @@ test("Can create crypto payment accounts", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("Can post and remove offers", async () => {
|
||||
test("Can prepare for trading", async () => {
|
||||
|
||||
// wait for alice to have at least 5 outputs of 0.5 XMR
|
||||
await fundOutputs([aliceWallet], BigInt("500000000000"), 5);
|
||||
// create payment accounts
|
||||
if (!await hasPaymentAccount(alice, "eth")) await createPaymentAccount(alice, "eth");
|
||||
if (!await hasPaymentAccount(alice, "bch")) await createPaymentAccount(alice, "bch");
|
||||
if (!await hasPaymentAccount(alice, "usd")) await createPaymentAccount(alice, "usd");
|
||||
if (!await hasPaymentAccount(bob, "eth")) await createPaymentAccount(bob, "eth");
|
||||
if (!await hasPaymentAccount(bob, "bch")) await createPaymentAccount(bob, "bch");
|
||||
if (!await hasPaymentAccount(bob, "usd")) await createPaymentAccount(bob, "usd");
|
||||
|
||||
// fund wallets
|
||||
const tradeAmount = BigInt("250000000000");
|
||||
await fundOutputs([aliceWallet, bobWallet], tradeAmount * BigInt("6"), 4);
|
||||
|
||||
// wait for havenod to observe funds
|
||||
await wait(TestConfig.walletSyncPeriodMs);
|
||||
});
|
||||
|
||||
test("Can post and remove offers", async () => {
|
||||
|
||||
// wait for alice to have unlocked balance to post offer
|
||||
await waitForUnlockedBalance(BigInt("250000000000") * BigInt("2"), alice);
|
||||
|
||||
// get unlocked balance before reserving funds for offer
|
||||
const unlockedBalanceBefore = BigInt((await alice.getBalances()).getUnlockedBalance());
|
||||
|
||||
// post crypto offer
|
||||
let assetCode = "ETH";
|
||||
let assetCode = "BCH";
|
||||
let price = 1 / 17;
|
||||
price = 1 / price; // TODO: price in crypto offer is inverted
|
||||
let offer: OfferInfo = await postOffer(alice, {assetCode: assetCode, price: price});
|
||||
assert.equal(offer.getState(), "AVAILABLE");
|
||||
assert.equal(offer.getBaseCurrencyCode(), assetCode); // TODO: base and counter currencies inverted in crypto offer
|
||||
assert.equal(offer.getCounterCurrencyCode(), "XMR");
|
||||
assert.equal(offer.getPrice(), price * 100000000); // TODO: price when posting crypto offer is inverted and * 100000000.
|
||||
assert.equal(parseFloat(offer.getPrice()), price);
|
||||
|
||||
// has offer
|
||||
offer = await alice.getMyOffer(offer.getId());
|
||||
assert.equal(offer.getState(), "AVAILABLE");
|
||||
|
||||
// peer sees offer
|
||||
await wait(TestConfig.maxTimePeerNoticeMs);
|
||||
if (!getOffer(await bob.getOffers(assetCode, TestConfig.postOffer.direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posted");
|
||||
|
||||
// cancel offer
|
||||
await alice.removeOffer(offer.getId());
|
||||
|
||||
@ -965,7 +988,7 @@ test("Can post and remove offers", async () => {
|
||||
assert.equal(offer.getState(), "AVAILABLE");
|
||||
assert.equal(offer.getBaseCurrencyCode(), "XMR");
|
||||
assert.equal(offer.getCounterCurrencyCode(), "USD");
|
||||
assert.equal(offer.getPrice(), price * 10000); // TODO: price = price * 10000
|
||||
assert.equal(parseFloat(offer.getPrice()), price);
|
||||
|
||||
// has offer
|
||||
offer = await alice.getMyOffer(offer.getId());
|
||||
@ -997,7 +1020,7 @@ test("Can schedule offers with locked funds", async () => {
|
||||
await fundOutputs([charlieWallet], outputAmt, 2, false);
|
||||
|
||||
// schedule offer
|
||||
const assetCode = "ETH";
|
||||
const assetCode = "BCH";
|
||||
const direction = "BUY";
|
||||
let offer: OfferInfo = await postOffer(charlie, {assetCode: assetCode, direction: direction, awaitUnlockedBalance: false});
|
||||
assert.equal(offer.getState(), "SCHEDULED");
|
||||
@ -1202,7 +1225,7 @@ test("Can resolve disputes", async () => {
|
||||
|
||||
// wait for alice and bob to have unlocked balance for trade
|
||||
const tradeAmount = BigInt("250000000000");
|
||||
await fundOutputs([aliceWallet, bobWallet], tradeAmount * BigInt("6"), 4, true);
|
||||
await fundOutputs([aliceWallet, bobWallet], tradeAmount * BigInt("6"), 4);
|
||||
|
||||
// register to receive notifications
|
||||
const aliceNotifications: NotificationMessage[] = [];
|
||||
@ -2071,25 +2094,34 @@ function testDestination(destination: XmrDestination) {
|
||||
}
|
||||
|
||||
function getRandomAssetCode() {
|
||||
return TestConfig.assetCodes[GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)];
|
||||
return TestConfig.assetCodes[GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)];
|
||||
}
|
||||
|
||||
async function hasPaymentAccount(trader: HavenoClient, assetCode: string): Promise<boolean> {
|
||||
for (const paymentAccount of await trader.getPaymentAccounts()) {
|
||||
if (paymentAccount.getSelectedTradeCurrency()!.getCode() === assetCode.toUpperCase()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function createPaymentAccount(trader: HavenoClient, assetCode: string): Promise<PaymentAccount> {
|
||||
return isCrypto(assetCode) ? createCryptoPaymentAccount(trader, assetCode) : createRevolutPaymentAccount(trader);
|
||||
return isCrypto(assetCode) ? createCryptoPaymentAccount(trader, assetCode) : createRevolutPaymentAccount(trader);
|
||||
}
|
||||
|
||||
function isCrypto(assetCode: string) {
|
||||
return getCryptoAddress(assetCode) !== undefined;
|
||||
return getCryptoAddress(assetCode) !== undefined;
|
||||
}
|
||||
|
||||
function getCryptoAddress(currencyCode: string): string | undefined {
|
||||
for (const cryptoAddress of TestConfig.cryptoAddresses) {
|
||||
if (cryptoAddress.currencyCode === currencyCode.toUpperCase()) return cryptoAddress.address;
|
||||
}
|
||||
for (const cryptoAddress of TestConfig.cryptoAddresses) {
|
||||
if (cryptoAddress.currencyCode === currencyCode.toUpperCase()) return cryptoAddress.address;
|
||||
}
|
||||
}
|
||||
|
||||
async function createRevolutPaymentAccount(trader: HavenoClient): Promise<PaymentAccount> {
|
||||
const accountForm = await trader.getPaymentAccountForm('REVOLUT');
|
||||
accountForm.tradeCurrencies ="gbp,eur,usd";
|
||||
accountForm.selectedTradeCurrency = "usd";
|
||||
accountForm.accountName = "Revolut account " + GenUtils.getUUID();
|
||||
accountForm.userName = "user123";
|
||||
return trader.createPaymentAccount(accountForm);
|
||||
|
@ -781,12 +781,16 @@ export default class HavenoClient {
|
||||
* @return {number} the price of the asset per 1 XMR
|
||||
*/
|
||||
async getPrice(assetCode: string): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._priceClient.getMarketPrice(new MarketPriceRequest().setCurrencyCode(assetCode), {password: this._password}, function(err: grpcWeb.RpcError, response: MarketPriceReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPrice());
|
||||
try { // TODO (woodser): try...catch is necessary to preserve stack trace. use throughout client
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._priceClient.getMarketPrice(new MarketPriceRequest().setCurrencyCode(assetCode), {password: this._password}, function(err: grpcWeb.RpcError, response: MarketPriceReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPrice());
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e: any) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -891,12 +895,16 @@ export default class HavenoClient {
|
||||
* @return {PaymentAccount} the created payment account
|
||||
*/
|
||||
async createPaymentAccount(paymentAccountForm: any): Promise<PaymentAccount> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._paymentAccountsClient.createPaymentAccount(new CreatePaymentAccountRequest().setPaymentAccountForm(JSON.stringify(paymentAccountForm)), {password: this._password}, function(err: grpcWeb.RpcError, response: CreatePaymentAccountReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPaymentAccount()!);
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._paymentAccountsClient.createPaymentAccount(new CreatePaymentAccountRequest().setPaymentAccountForm(JSON.stringify(paymentAccountForm)), {password: this._password}, function(err: grpcWeb.RpcError, response: CreatePaymentAccountReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPaymentAccount()!);
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e: any) {
|
||||
throw new Error(e.message); // re-catch error to preserve stack trace TODO: repeat this pattern throughout this class
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -913,12 +921,16 @@ export default class HavenoClient {
|
||||
.setCurrencyCode(assetCode)
|
||||
.setAddress(address)
|
||||
.setTradeInstant(false); // not using instant trades
|
||||
return new Promise((resolve, reject) => {
|
||||
this._paymentAccountsClient.createCryptoCurrencyPaymentAccount(request, {password: this._password}, function(err: grpcWeb.RpcError, response: CreateCryptoCurrencyPaymentAccountReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPaymentAccount()!);
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._paymentAccountsClient.createCryptoCurrencyPaymentAccount(request, {password: this._password}, function(err: grpcWeb.RpcError, response: CreateCryptoCurrencyPaymentAccountReply) {
|
||||
if (err) reject(err);
|
||||
else resolve(response.getPaymentAccount()!);
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e: any) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -977,9 +989,9 @@ export default class HavenoClient {
|
||||
* @param {bigint} amount - amount of XMR to trade
|
||||
* @param {string} assetCode - asset code to trade for XMR
|
||||
* @param {string} paymentAccountId - payment account id
|
||||
* @param {number} buyerSecurityDeposit - buyer security deposit as % of trade amount
|
||||
* @param {number} buyerSecurityDepositPct - buyer security deposit as % of trade amount
|
||||
* @param {number} price - trade price (optional, default to market price)
|
||||
* @param {number} marketPriceMargin - if using market price, % from market price to accept (optional, default 0%)
|
||||
* @param {number} marketPriceMarginPct - if using market price, % from market price to accept (optional, default 0%)
|
||||
* @param {bigint} minAmount - minimum amount to trade (optional, default to fixed amount)
|
||||
* @param {number} triggerPrice - price to remove offer (optional)
|
||||
* @return {OfferInfo} the posted offer
|
||||
@ -988,9 +1000,9 @@ export default class HavenoClient {
|
||||
amount: bigint,
|
||||
assetCode: string,
|
||||
paymentAccountId: string,
|
||||
buyerSecurityDeposit: number,
|
||||
buyerSecurityDepositPct: number,
|
||||
price?: number,
|
||||
marketPriceMargin?: number,
|
||||
marketPriceMarginPct?: number,
|
||||
triggerPrice?: number,
|
||||
minAmount?: bigint): Promise<OfferInfo> {
|
||||
const request = new CreateOfferRequest()
|
||||
@ -998,11 +1010,11 @@ export default class HavenoClient {
|
||||
.setAmount(amount.toString())
|
||||
.setCurrencyCode(assetCode)
|
||||
.setPaymentAccountId(paymentAccountId)
|
||||
.setBuyerSecurityDeposit(buyerSecurityDeposit)
|
||||
.setBuyerSecurityDepositPct(buyerSecurityDepositPct)
|
||||
.setPrice(price ? price.toString() : "1.0") // TOOD (woodser): positive price required even if using market price?
|
||||
.setUseMarketBasedPrice(price === undefined) // TODO (woodser): this field is redundant; remove from api
|
||||
.setMinAmount(minAmount ? minAmount.toString() : amount.toString());
|
||||
if (marketPriceMargin) request.setMarketPriceMargin(marketPriceMargin);
|
||||
if (marketPriceMarginPct) request.setMarketPriceMarginPct(marketPriceMarginPct);
|
||||
if (triggerPrice) request.setTriggerPrice(triggerPrice.toString());
|
||||
return new Promise((resolve, reject) => {
|
||||
this._offersClient.createOffer(request, {password: this._password}, function(err: grpcWeb.RpcError, response: CreateOfferReply) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user