havenod supports crypto account creation and getPrice(currencyCode)

disable account creation with instant trades
This commit is contained in:
woodser 2021-10-22 13:51:57 -04:00
parent 50d8c3b119
commit 967d036216
3 changed files with 103 additions and 39 deletions

View File

@ -30,7 +30,7 @@ Running the [top-level API tests](./src/HavenoDaemon.test.tsx) is a great way to
Example: `docker run --rm -it -v ~/git/haveno-ui-poc/config/envoy.test.yaml:/envoy.test.yaml -p 8080:8080 -p 8081:8081 envoyproxy/envoy-dev:8a2143613d43d17d1eb35a24b4a4a4c432215606 -c /envoy.test.yaml` Example: `docker run --rm -it -v ~/git/haveno-ui-poc/config/envoy.test.yaml:/envoy.test.yaml -p 8080:8080 -p 8081:8081 envoyproxy/envoy-dev:8a2143613d43d17d1eb35a24b4a4a4c432215606 -c /envoy.test.yaml`
5. `npm install` 5. `npm install`
6. Modify test config as needed in [HavenoDaemon.test.tsx](./src/HavenoDaemon.test.tsx). 6. Modify test config as needed in [HavenoDaemon.test.tsx](./src/HavenoDaemon.test.tsx).
7. `npm test` 7. `npm test` to run all tests or `npm run test -- -t 'my test'` to run tests by name
## How to Update the Protobuf Client ## How to Update the Protobuf Client

View File

@ -5,6 +5,7 @@ import {PaymentAccount} from './protobuf/pb_pb';
// import monero-javascript // import monero-javascript
const monerojs = require("monero-javascript"); // TODO (woodser): support typescript and `npm install @types/monero-javascript` in monero-javascript const monerojs = require("monero-javascript"); // TODO (woodser): support typescript and `npm install @types/monero-javascript` in monero-javascript
const GenUtils = monerojs.GenUtils;
const MoneroTxConfig = monerojs.MoneroTxConfig; const MoneroTxConfig = monerojs.MoneroTxConfig;
const TaskLooper = monerojs.TaskLooper; const TaskLooper = monerojs.TaskLooper;
@ -16,7 +17,7 @@ const havenoVersion = "1.6.2";
const aliceDaemonUrl = "http://localhost:8080"; const aliceDaemonUrl = "http://localhost:8080";
const aliceDaemonPassword = "apitest"; const aliceDaemonPassword = "apitest";
const alice: HavenoDaemon = new HavenoDaemon(aliceDaemonUrl, aliceDaemonPassword); const alice: HavenoDaemon = new HavenoDaemon(aliceDaemonUrl, aliceDaemonPassword);
const aliceWalletUrl = "http://127.0.0.1:56627"; // alice's internal haveno wallet for direct testing // TODO (woodser): make configurable rather than randomly generated const aliceWalletUrl = "http://127.0.0.1:51743"; // alice's internal haveno wallet for direct testing // TODO (woodser): make configurable rather than randomly generated
const aliceWalletUsername = "rpc_user"; const aliceWalletUsername = "rpc_user";
const aliceWalletPassword = "abc123"; const aliceWalletPassword = "abc123";
let aliceWallet: any; let aliceWallet: any;
@ -41,6 +42,16 @@ let fundingWallet: any;
// other test config // other test config
const WALLET_SYNC_PERIOD = 5000; const WALLET_SYNC_PERIOD = 5000;
const MAX_TIME_PEER_NOTICE = 3000; const MAX_TIME_PEER_NOTICE = 3000;
const TEST_CRYPTO_ACCOUNTS = [ // TODO (woodser): test other cryptos, fiat
{
currencyCode: "eth",
address: "0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f"
},
{
currencyCode: "btc",
address: "bcrt1q6j90vywv8x7eyevcnn2tn2wrlg3vsjlsvt46qz"
}
];
beforeAll(async () => { beforeAll(async () => {
@ -63,6 +74,22 @@ test("Can get the version", async () => {
expect(version).toEqual(havenoVersion); expect(version).toEqual(havenoVersion);
}); });
test("Can get market prices", async () => {
// test crypto prices
for (let testAccount of TEST_CRYPTO_ACCOUNTS) {
let price = await alice.getPrice(testAccount.currencyCode);
console.log(testAccount.currencyCode + " price: " + price);
expect(price).toBeGreaterThan(0);
await GenUtils.waitFor(1000); // wait before next request // TODO (woodser): disable price throttle?
}
// test fiat price
let price = await alice.getPrice("USD");
console.log("usd price: " + price);
expect(price).toBeGreaterThan(0);
});
test("Can get balances", async () => { test("Can get balances", async () => {
let balances: XmrBalanceInfo = await alice.getBalances(); let balances: XmrBalanceInfo = await alice.getBalances();
expect(balances.getUnlockedBalance()); expect(balances.getUnlockedBalance());
@ -88,30 +115,49 @@ test("Can get my offers", async () => {
test("Can get payment accounts", async () => { test("Can get payment accounts", async () => {
let paymentAccounts: PaymentAccount[] = await alice.getPaymentAccounts(); let paymentAccounts: PaymentAccount[] = await alice.getPaymentAccounts();
for (let paymentAccount of paymentAccounts) { for (let paymentAccount of paymentAccounts) {
testPaymentAccount(paymentAccount); testCryptoPaymentAccount(paymentAccount);
} }
}); });
test("Can create a crypto payment account", async () => { test("Can create crypto payment accounts", async () => {
const ethAddress = "0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f";
let ethPaymentAccount: PaymentAccount = await alice.createCryptoPaymentAccount( // test each stagenet crypto account
"my eth account", for (let testAccount of TEST_CRYPTO_ACCOUNTS) {
"eth",
"0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f", // create payment account
true); let name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID();
expect(ethPaymentAccount.getPaymentAccountPayload()!.getInstantCryptoCurrencyAccountPayload()!.getAddress()).toEqual(ethAddress); let paymentAccount: PaymentAccount = await alice.createCryptoPaymentAccount(
let found = false; name,
for (let paymentAccount of await alice.getPaymentAccounts()) { testAccount.currencyCode,
if (paymentAccount.getId() === ethPaymentAccount.getId()) { testAccount.address);
found = true; testCryptoPaymentAccount(paymentAccount);
ethPaymentAccount = paymentAccount; testCryptoPaymentAccountEquals(paymentAccount, testAccount, name);
// fetch and test payment account
let fetchedAccount: PaymentAccount | undefined;
for (let account of await alice.getPaymentAccounts()) {
if (paymentAccount.getId() === account.getId()) {
fetchedAccount = account;
break; break;
} }
} }
if (!found) throw new Error("Payment account not found after being added"); if (!fetchedAccount) throw new Error("Payment account not found after being added");
expect(ethPaymentAccount.getAccountName()).toEqual("my eth account"); testCryptoPaymentAccount(paymentAccount);
expect(ethPaymentAccount.getPaymentAccountPayload()!.getInstantCryptoCurrencyAccountPayload()!.getAddress()).toEqual("0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f"); testCryptoPaymentAccountEquals(fetchedAccount, testAccount, name);
//expect(ethPaymentAccount.getSelectedTradeCurrency()!.getCode()).toEqual("ETH"); // TODO: selected trade currency missing or interferes with other tests
// wait before creating next account
await GenUtils.waitFor(1000);
// TODO (woodser): test rejecting account with invalid currency code
// TODO (woodser): test rejecting account with invalid address
// TODO (woodser): test rejecting account with duplicate name
}
function testCryptoPaymentAccountEquals(paymentAccount: PaymentAccount, testAccount: any, name: string) {
expect(paymentAccount.getAccountName()).toEqual(name);
expect(paymentAccount.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress()).toEqual(testAccount.address);
expect(paymentAccount.getSelectedTradeCurrency()!.getCode()).toEqual(testAccount.currencyCode.toUpperCase());
}
}); });
test("Can post and remove an offer", async () => { test("Can post and remove an offer", async () => {
@ -197,16 +243,12 @@ test("Can complete a trade", async () => {
let tradeAmount: bigint = BigInt("250000000000"); let tradeAmount: bigint = BigInt("250000000000");
await waitForUnlockedBalance(tradeAmount, alice, bob); await waitForUnlockedBalance(tradeAmount, alice, bob);
// get bob's ethereum payment account // create bob's ethereum payment account
let ethPaymentAccount: PaymentAccount | undefined; let testAccount = TEST_CRYPTO_ACCOUNTS[0];
console.log("Getting bob's payment accounts"); let ethPaymentAccount: PaymentAccount = await bob.createCryptoPaymentAccount(
for (let paymentAccount of await bob.getPaymentAccounts()) { testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
if (paymentAccount.getSelectedTradeCurrency()?.getCode() === "ETH") { testAccount.currencyCode,
ethPaymentAccount = paymentAccount; testAccount.address);
break;
}
}
if (!ethPaymentAccount) throw new Error("Bob must have ethereum payment account to take offer");
// alice posts offer to buy xmr // alice posts offer to buy xmr
console.log("Alice posting offer"); console.log("Alice posting offer");
@ -325,9 +367,15 @@ function getOffer(offers: OfferInfo[], id: string): OfferInfo | undefined {
return offers.find(offer => offer.getId() === id); return offers.find(offer => offer.getId() === id);
} }
function testPaymentAccount(paymentAccount: PaymentAccount) { function testCryptoPaymentAccount(paymentAccount: PaymentAccount) {
expect(paymentAccount.getId()).toHaveLength; expect(paymentAccount.getId()).toHaveLength;
// TODO: test rest of payment account expect(paymentAccount.getAccountName()).toHaveLength;
expect(paymentAccount.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()!.getAddress()).toHaveLength;
expect(paymentAccount.getSelectedTradeCurrency()!.getCode()).toHaveLength;
expect(paymentAccount.getTradeCurrenciesList().length).toEqual(1);
let tradeCurrency = paymentAccount.getTradeCurrenciesList()[0];
expect(tradeCurrency.getName()).toHaveLength;
expect(tradeCurrency.getCode()).toEqual(paymentAccount.getSelectedTradeCurrency()!.getCode());
} }
function testOffer(offer: OfferInfo) { function testOffer(offer: OfferInfo) {

View File

@ -1,6 +1,6 @@
import * as grpcWeb from 'grpc-web'; import * as grpcWeb from 'grpc-web';
import {GetVersionClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient} from './protobuf/GrpcServiceClientPb'; import {GetVersionClient, PriceClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient} from './protobuf/GrpcServiceClientPb';
import {GetVersionRequest, GetVersionReply, GetBalancesRequest, GetBalancesReply, XmrBalanceInfo, GetOffersRequest, GetOffersReply, OfferInfo, GetPaymentAccountsRequest, GetPaymentAccountsReply, CreateCryptoCurrencyPaymentAccountRequest, CreateCryptoCurrencyPaymentAccountReply, CreateOfferRequest, CreateOfferReply, CancelOfferRequest, TakeOfferRequest, TakeOfferReply, TradeInfo, GetTradeRequest, GetTradeReply, GetNewDepositSubaddressRequest, GetNewDepositSubaddressReply, ConfirmPaymentStartedRequest, ConfirmPaymentReceivedRequest} from './protobuf/grpc_pb'; import {GetVersionRequest, GetVersionReply, MarketPriceRequest, MarketPriceReply, GetBalancesRequest, GetBalancesReply, XmrBalanceInfo, GetOffersRequest, GetOffersReply, OfferInfo, GetPaymentAccountsRequest, GetPaymentAccountsReply, CreateCryptoCurrencyPaymentAccountRequest, CreateCryptoCurrencyPaymentAccountReply, CreateOfferRequest, CreateOfferReply, CancelOfferRequest, TakeOfferRequest, TakeOfferReply, TradeInfo, GetTradeRequest, GetTradeReply, GetNewDepositSubaddressRequest, GetNewDepositSubaddressReply, ConfirmPaymentStartedRequest, ConfirmPaymentReceivedRequest} from './protobuf/grpc_pb';
import {PaymentAccount, AvailabilityResult} from './protobuf/pb_pb'; import {PaymentAccount, AvailabilityResult} from './protobuf/pb_pb';
/** /**
@ -12,6 +12,7 @@ class HavenoDaemon {
_url: string; _url: string;
_password: string; _password: string;
_getVersionClient: GetVersionClient; _getVersionClient: GetVersionClient;
_priceClient: PriceClient;
_walletsClient: WalletsClient; _walletsClient: WalletsClient;
_paymentAccountsClient: PaymentAccountsClient; _paymentAccountsClient: PaymentAccountsClient;
_offersClient: OffersClient; _offersClient: OffersClient;
@ -27,6 +28,7 @@ class HavenoDaemon {
this._url = url; this._url = url;
this._password = password; this._password = password;
this._getVersionClient = new GetVersionClient(this._url); this._getVersionClient = new GetVersionClient(this._url);
this._priceClient = new PriceClient(this._url);
this._walletsClient = new WalletsClient(this._url); this._walletsClient = new WalletsClient(this._url);
this._paymentAccountsClient = new PaymentAccountsClient(this._url); this._paymentAccountsClient = new PaymentAccountsClient(this._url);
this._offersClient = new OffersClient(this._url); this._offersClient = new OffersClient(this._url);
@ -48,6 +50,22 @@ class HavenoDaemon {
}); });
} }
/**
* Get the current market price of the given currency code as a ratio, e.g. ETH/XMR.
*
* @param {string} currencyCode - currency code to get the price of
* @return {number} the current market price of the given currency code as a ratio, e.g. XMR/ETH
*/
async getPrice(currencyCode: string) {
let that = this;
return new Promise(function(resolve, reject) {
that._priceClient.getMarketPrice(new MarketPriceRequest().setCurrencyCode(currencyCode), {password: that._password}, function(err: grpcWeb.Error, response: MarketPriceReply) {
if (err) reject(err);
else resolve(response.getPrice());
});
});
}
/** /**
* Get the user's balances. * Get the user's balances.
* *
@ -99,19 +117,17 @@ class HavenoDaemon {
* @param {string} accountName - description of the account * @param {string} accountName - description of the account
* @param {string} currencyCode - currency code of the account * @param {string} currencyCode - currency code of the account
* @param {string} address - payment address of the account * @param {string} address - payment address of the account
* @param {boolean} tradeInstant - whether or not trade can be completed quickly
* @return {PaymentAccount} the created payment account * @return {PaymentAccount} the created payment account
*/ */
async createCryptoPaymentAccount(accountName: string, async createCryptoPaymentAccount(accountName: string,
currencyCode: string, currencyCode: string,
address: string, address: string): Promise<PaymentAccount> {
tradeInstant: boolean): Promise<PaymentAccount> {
let that = this; let that = this;
let request = new CreateCryptoCurrencyPaymentAccountRequest() let request = new CreateCryptoCurrencyPaymentAccountRequest()
.setAccountName(accountName) .setAccountName(accountName)
.setCurrencyCode(currencyCode) .setCurrencyCode(currencyCode)
.setAddress(address) .setAddress(address)
.setTradeInstant(tradeInstant); false; // not using instant trades
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
that._paymentAccountsClient.createCryptoCurrencyPaymentAccount(request, {password: that._password}, function(err: grpcWeb.Error, response: CreateCryptoCurrencyPaymentAccountReply) { that._paymentAccountsClient.createCryptoCurrencyPaymentAccount(request, {password: that._password}, function(err: grpcWeb.Error, response: CreateCryptoCurrencyPaymentAccountReply) {
if (err) reject(err); if (err) reject(err);