From 5ed0efd67eceda6f0c8e2464de30c4f579af1949 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 13 Jun 2022 20:57:27 +0200 Subject: [PATCH] feat: market offers --- Reviewed-by: @schowdhuri --- package.json | 4 +- packages/renderer/assets/check-circle.svg | 3 + packages/renderer/src/Routes.tsx | 2 + .../atoms/Currency/Currency.stories.tsx | 1 + .../components/atoms/Currency/Currency.tsx | 11 +- .../molecules/Table/Table.style.tsx | 39 +++++ .../src/components/molecules/Table/Table.tsx | 15 +- .../components/molecules/Table/TableBody.tsx | 1 + .../molecules/Table/TableHeader.tsx | 1 + .../src/components/molecules/Table/_types.tsx | 9 +- .../WalletTransactions.stories.tsx | 2 +- .../MarketOffersTable.stories.tsx | 58 +++++++ .../MarketOffersTable.test.tsx | 116 ++++++++++++++ .../MarketOffersTable/MarketOffersTable.tsx | 150 ++++++++++++++++++ .../MarketOffersTableCell.tsx | 87 ++++++++++ .../MarketOffersTable.test.tsx.snap | 134 ++++++++++++++++ .../organisms/MarketOffersTable/_types.ts | 28 ++++ .../organisms/MarketOffersTable/index.ts | 18 +++ .../MarketsOffers/MarketsOffers.stories.tsx | 31 ++++ .../organisms/MarketsOffers/MarketsOffers.tsx | 57 +++++++ .../organisms/MarketsOffers/_utils.ts | 35 ++++ .../organisms/MarketsOffers/index.ts | 17 ++ .../components/organisms/Sidebar/_NavLink.tsx | 7 +- .../__snapshots__/Sidebar.test.tsx.snap | 30 ++-- .../organisms/Sidebar/_constants.tsx | 14 +- .../templates/AccountLayout/index.tsx | 4 +- .../templates/NavbarLayout/index.tsx | 19 ++- .../renderer/src/constants/lang/LangKeys.ts | 7 + packages/renderer/src/constants/lang/en.ts | 6 + packages/renderer/src/constants/lang/es.ts | 6 + packages/renderer/src/constants/query-keys.ts | 2 + packages/renderer/src/constants/routes.ts | 1 + .../src/hooks/haveno/useMarketsOffers.ts | 82 ++++++++++ .../renderer/src/hooks/haveno/useMyOffers.ts | 33 ++++ .../haveno/useSetCryptoPaymentAccount.ts | 54 +++++++ .../renderer/src/hooks/haveno/useSetOffer.ts | 67 ++++++++ .../src/pages/Markets/MarketsOffers.tsx | 35 ++++ packages/renderer/src/pages/Markets/index.ts | 17 ++ packages/renderer/src/utils/math.ts | 19 +++ yarn.lock | 8 +- 40 files changed, 1191 insertions(+), 39 deletions(-) create mode 100644 packages/renderer/assets/check-circle.svg create mode 100644 packages/renderer/src/components/molecules/Table/Table.style.tsx create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.stories.tsx create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.test.tsx create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.tsx create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTableCell.tsx create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/__snapshots__/MarketOffersTable.test.tsx.snap create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/_types.ts create mode 100644 packages/renderer/src/components/organisms/MarketOffersTable/index.ts create mode 100644 packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.stories.tsx create mode 100644 packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.tsx create mode 100644 packages/renderer/src/components/organisms/MarketsOffers/_utils.ts create mode 100644 packages/renderer/src/components/organisms/MarketsOffers/index.ts create mode 100644 packages/renderer/src/hooks/haveno/useMarketsOffers.ts create mode 100644 packages/renderer/src/hooks/haveno/useMyOffers.ts create mode 100644 packages/renderer/src/hooks/haveno/useSetCryptoPaymentAccount.ts create mode 100644 packages/renderer/src/hooks/haveno/useSetOffer.ts create mode 100644 packages/renderer/src/pages/Markets/MarketsOffers.tsx create mode 100644 packages/renderer/src/pages/Markets/index.ts create mode 100644 packages/renderer/src/utils/math.ts diff --git a/package.json b/package.json index 87b2575..b9ff314 100644 --- a/package.json +++ b/package.json @@ -54,9 +54,9 @@ "@testing-library/user-event": "^14.2.0", "@types/jsonwebtoken": "^8.5.8", "@types/lodash": "^4.14.182", + "@types/qrcode": "^1.4.2", "@types/react": "<18.0.0", "@types/react-dom": "<18.0.0", - "@types/qrcode": "^1.4.2", "@typescript-eslint/eslint-plugin": "5.12.1", "@typescript-eslint/parser": "^5.19.0", "@vitejs/plugin-react": "^1.3.0", @@ -95,7 +95,7 @@ "dayjs": "^1.11.0", "electron-store": "^8.0.1", "electron-updater": "4.6.5", - "haveno-ts": "0.0.5", + "haveno-ts": "0.0.6", "joi": "^17.6.0", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", diff --git a/packages/renderer/assets/check-circle.svg b/packages/renderer/assets/check-circle.svg new file mode 100644 index 0000000..1387c91 --- /dev/null +++ b/packages/renderer/assets/check-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/renderer/src/Routes.tsx b/packages/renderer/src/Routes.tsx index d3cd127..2e94f5c 100644 --- a/packages/renderer/src/Routes.tsx +++ b/packages/renderer/src/Routes.tsx @@ -30,6 +30,7 @@ import { PaymentMethods, } from "@pages/Account"; import { MyWallet } from "@pages/MyWallet"; +import { MarketsOffersPage } from "@pages/Markets"; export function AppRoutes() { return ( @@ -38,6 +39,7 @@ export function AppRoutes() { } /> } /> } /> + } /> ({ + primary: { + "thead tr": { + backgroundColor: theme.colors.gray[0], + + th: { + borderBottomColor: theme.colors.gray[2], + color: theme.colors.gray[5], + fontSize: 10, + fontWeight: 700, + letterSpacing: "0.05em", + textTransform: "uppercase", + }, + }, + "tbody tr": { + td: { + borderBottomColor: theme.colors.gray[2], + }, + }, + }, +})); diff --git a/packages/renderer/src/components/molecules/Table/Table.tsx b/packages/renderer/src/components/molecules/Table/Table.tsx index d6bd8f5..50b54d2 100644 --- a/packages/renderer/src/components/molecules/Table/Table.tsx +++ b/packages/renderer/src/components/molecules/Table/Table.tsx @@ -20,23 +20,32 @@ import { getExpandedRowModel, } from "@tanstack/react-table"; import { Table as MTable } from "@mantine/core"; +import type { TableProps } from "./_types"; +import { TableVariant } from "./_types"; import { TableProvider } from "./use-table-context"; import { TableHeader } from "./TableHeader"; import { TableBody } from "./TableBody"; -import type { TableProps } from "./_types"; +import { useStyles } from "./Table.style"; export function Table(props: TableProps) { - const { table, columns, data, tableWrap } = props; + const { classes, cx } = useStyles(); + const { table, columns, data, tableWrap, variant, state } = props; const tableInstance = useTableInstance(table, { data, columns, + state, getCoreRowModel: getCoreRowModel(), getExpandedRowModel: getExpandedRowModel(), }); return ( - + diff --git a/packages/renderer/src/components/molecules/Table/TableBody.tsx b/packages/renderer/src/components/molecules/Table/TableBody.tsx index 456530a..620a36c 100644 --- a/packages/renderer/src/components/molecules/Table/TableBody.tsx +++ b/packages/renderer/src/components/molecules/Table/TableBody.tsx @@ -38,6 +38,7 @@ export function TableBody() { key={cell.id} style={{ width: cell.column.getSize(), + textAlign: cell.column.columnDef?.meta?.textAlign, }} > {cell.renderCell()} diff --git a/packages/renderer/src/components/molecules/Table/TableHeader.tsx b/packages/renderer/src/components/molecules/Table/TableHeader.tsx index 43a8a44..57dfad1 100644 --- a/packages/renderer/src/components/molecules/Table/TableHeader.tsx +++ b/packages/renderer/src/components/molecules/Table/TableHeader.tsx @@ -36,6 +36,7 @@ export function TableHeader() { colSpan={header.colSpan} style={{ width: header.getSize(), + textAlign: header.column.columnDef?.meta?.textAlign, }} > {header.isPlaceholder ? null : header.renderHeader()} diff --git a/packages/renderer/src/components/molecules/Table/_types.tsx b/packages/renderer/src/components/molecules/Table/_types.tsx index 3347b04..cfff245 100644 --- a/packages/renderer/src/components/molecules/Table/_types.tsx +++ b/packages/renderer/src/components/molecules/Table/_types.tsx @@ -15,13 +15,14 @@ // ============================================================================= /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { ColumnDef, Row } from "@tanstack/react-table"; +import type { ColumnDef, Row, TableState } from "@tanstack/react-table"; import type { TableProps as MTableProps } from "@mantine/core"; export interface TableProps { columns: Array>; table: any; data: Array; + state?: Partial; showHeader?: boolean; showFooter?: boolean; @@ -29,4 +30,10 @@ export interface TableProps { rowSubComponent?: ({ row }: { row: Row }) => React.ReactNode; tableWrap?: MTableProps; + variant?: TableVariant; +} + +export enum TableVariant { + Default = "Default", + Primary = "Primary", } diff --git a/packages/renderer/src/components/molecules/WalletTransactions/WalletTransactions.stories.tsx b/packages/renderer/src/components/molecules/WalletTransactions/WalletTransactions.stories.tsx index 8cd1e0a..52b1cc6 100644 --- a/packages/renderer/src/components/molecules/WalletTransactions/WalletTransactions.stories.tsx +++ b/packages/renderer/src/components/molecules/WalletTransactions/WalletTransactions.stories.tsx @@ -87,4 +87,4 @@ const data = [ height: 2482937, fee: 0.000005096, }, -] as TWalletTransaction[]; +] as Array; diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.stories.tsx b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.stories.tsx new file mode 100644 index 0000000..313f7ea --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.stories.tsx @@ -0,0 +1,58 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import type { ComponentStory, ComponentMeta } from "@storybook/react"; +import { MarketOffersTable } from "./MarketOffersTable"; + +export default { + title: "molecules/MarketOffersTable", + component: MarketOffersTable, +} as ComponentMeta; + +const Template: ComponentStory = () => { + return ; +}; + +export const Default = Template.bind({}); + +Default.args = {}; + +const data = [ + { + price: 123, + priceCurrency: "EUR", + priceComparison: 0.12, + amount: 123123, + amountCurrency: "EUR", + cost: 123, + costCurrency: "USD", + paymentMethod: "Bitcoin", + accountAge: 12, + accountTrades: 1212, + }, + { + price: 123, + priceCurrency: "EUR", + priceComparison: 0.12, + amount: 123123, + amountCurrency: "EUR", + cost: 123, + costCurrency: "USD", + paymentMethod: "Altcoins", + accountAge: 12, + accountTrades: 1212, + }, +]; diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.test.tsx b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.test.tsx new file mode 100644 index 0000000..007a4b4 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.test.tsx @@ -0,0 +1,116 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { describe, expect, it } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { MarketOffersTable } from "./MarketOffersTable"; +import { AppProviders } from "@atoms/AppProviders"; + +describe("molecules::MarketOffersTable", () => { + it("renders without exploding.", () => { + const { asFragment, unmount } = render( + + + + ); + expect(asFragment()).toMatchSnapshot(); + unmount(); + }); + + it("renders all columns.", () => { + const { unmount } = render( + + + + ); + expect(screen.queryByText("Price")).toBeInTheDocument(); + expect(screen.queryByText("Amount")).toBeInTheDocument(); + expect(screen.queryByText("Costs")).toBeInTheDocument(); + expect(screen.queryByText("Payment Method")).toBeInTheDocument(); + unmount(); + }); + + it("renders formatted price value with currency sign.", () => { + const { unmount } = render( + + + + ); + expect(screen.queryByText("€5,000.96")).toBeInTheDocument(); + expect(screen.queryByText("€9,637.41")).toBeInTheDocument(); + unmount(); + }); + + it("renders formatted amount value with currency code.", () => { + const { unmount } = render( + + + + ); + expect(screen.queryByText("XMR 7,564.94")).toBeInTheDocument(); + expect(screen.queryByText("XMR 6,483.23")).toBeInTheDocument(); + unmount(); + }); + + it("renders offer formatted payment method.", () => { + const { unmount } = render( + + + + ); + expect(screen.queryByText("Bitcoin")).toBeInTheDocument(); + expect(screen.queryByText("Altcoins")).toBeInTheDocument(); + unmount(); + }); + + it("renders formatted price percentage.", () => { + const { unmount } = render( + + + + ); + expect(screen.queryByText("12%")).toBeInTheDocument(); + expect(screen.queryByText("15%")).toBeInTheDocument(); + unmount(); + }); +}); + +const data = [ + { + price: 5000.956, + priceComparison: 0.12, + priceCurrency: "EUR", + amount: 7564.94, + amountCurrency: "XMR", + cost: 532.34, + costCurrency: "USD", + paymentMethod: "Bitcoin", + accountAge: 12, + accountTrades: 1212, + }, + { + price: 9637.41, + priceComparison: 0.15, + priceCurrency: "EUR", + amount: 6483.23, + amountCurrency: "XMR", + cost: 983.32, + costCurrency: "USD", + paymentMethod: "Altcoins", + accountAge: 12, + accountTrades: 3412, + }, +]; diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.tsx b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.tsx new file mode 100644 index 0000000..c9ce370 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTable.tsx @@ -0,0 +1,150 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { createStyles } from "@mantine/core"; +import { createTable } from "@tanstack/react-table"; +import { useIntl } from "react-intl"; +import type { MarketOffer } from "./_types"; +import { + MarketOffersPriceCell, + MarketOffersAmountCell, + MarketOffersAccountTradesCell, + MarketOffersCostsCell, + MarketOffersAccountAgeCell, + MarketOffersPaymentCell, +} from "./MarketOffersTableCell"; +import { Table } from "@molecules/Table"; +import { LangKeys } from "@constants/lang"; +import { TableVariant } from "@molecules/Table/_types"; + +const table = createTable().setRowType(); + +interface MarketOffersTableProps { + data: Array; +} + +export function MarketOffersTable({ data }: MarketOffersTableProps) { + const { classes } = useStyles(); + const columns = useMarketOffersColumns(); + + return ( + + ); +} + +const useStyles = createStyles((theme) => ({ + root: { + "thead tr th, tbody tr td": { + "&:first-of-type": { + paddingLeft: 30, + }, + "&:last-of-type": { + paddingRight: 30, + }, + }, + "tbody tr": { + td: { + background: theme.white, + paddingTop: 22, + paddingBottom: 22, + }, + "&:last-of-type": { + td: { + borderBottom: `1px solid ${theme.colors.gray[2]}`, + }, + }, + }, + }, +})); + +const useMarketOffersColumns = () => { + const { formatMessage } = useIntl(); + + return [ + table.createDataColumn("price", { + id: "price", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnPrice, + defaultMessage: "Price", + }), + cell: ({ row }) => , + size: 400, + }), + table.createDataColumn("amount", { + id: "amount", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnAmount, + defaultMessage: "Amount", + }), + cell: ({ row }) => , + size: 400, + }), + table.createDataColumn("cost", { + id: "costs", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnCost, + defaultMessage: "Costs", + }), + cell: ({ row }) => , + size: 400, + }), + table.createDataColumn("paymentMethod", { + id: "paymentMethod", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnPaymentMethod, + defaultMessage: "Payment Method", + }), + cell: ({ row }) => , + size: 400, + }), + table.createDataColumn("accountAge", { + id: "accountAge", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnAccountAge, + defaultMessage: "Account Age", + }), + cell: ({ row }) => , + size: 400, + }), + table.createDataColumn("accountTrades", { + id: "accountTrades", + header: formatMessage({ + id: LangKeys.MarketsOffersColumnAccountTrades, + defaultMessage: "Account Trades", + }), + cell: ({ row }) => , + size: 400, + meta: { + textAlign: "right", + }, + }), + ]; +}; diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTableCell.tsx b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTableCell.tsx new file mode 100644 index 0000000..6733406 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/MarketOffersTableCell.tsx @@ -0,0 +1,87 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { Group, useMantineTheme } from "@mantine/core"; +import type { MarketOffer } from "./_types"; +import { Currency } from "@atoms/Currency"; +import { BodyText } from "@atoms/Typography"; +import { fractionToPercent } from "@src/utils/math"; +import { ReactComponent as CheckCircle } from "@assets/check-circle.svg"; + +export function MarketOffersAccountAgeCell({ row }: { row?: MarketOffer }) { + const theme = useMantineTheme(); + + return ( + + + {row?.accountAge} Days + + ); +} + +export function MarketOffersPriceCell({ row }: { row?: MarketOffer }) { + return ( + + + + + + + + % + + + ); +} + +export function MarketOffersAmountCell({ row }: { row?: MarketOffer }) { + return ( + + + + ); +} + +export function MarketOffersCostsCell({ row }: { row?: MarketOffer }) { + return ( + + + + ); +} + +export function MarketOffersAccountTradesCell({ row }: { row?: MarketOffer }) { + return ( + + + + ); +} + +export function MarketOffersPaymentCell({ row }: { row?: MarketOffer }) { + return {row?.paymentMethod}; +} diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/__snapshots__/MarketOffersTable.test.tsx.snap b/packages/renderer/src/components/organisms/MarketOffersTable/__snapshots__/MarketOffersTable.test.tsx.snap new file mode 100644 index 0000000..eae98a3 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/__snapshots__/MarketOffersTable.test.tsx.snap @@ -0,0 +1,134 @@ +// Vitest Snapshot v1 + +exports[`molecules::MarketOffersTable > renders without exploding. 1`] = ` + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ Price + + Amount + + Costs + + Payment Method +
+
+
+ €5,000.96 +
+
+ 12% +
+
+
+
+ XMR 7,564.94 +
+
+
+ $532.34 +
+
+
+ Bitcoin +
+
+
+
+ €9,637.41 +
+
+ 15% +
+
+
+
+ XMR 6,483.23 +
+
+
+ $983.32 +
+
+
+ Altcoins +
+
+ +`; diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/_types.ts b/packages/renderer/src/components/organisms/MarketOffersTable/_types.ts new file mode 100644 index 0000000..d9c3462 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/_types.ts @@ -0,0 +1,28 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +export interface MarketOffer { + price: number; + priceComparison: number; + priceCurrency: string; + amount: number; + amountCurrency: string; + cost: number; + costCurrency: string; + paymentMethod: string; + accountAge: number; + accountTrades: number; +} diff --git a/packages/renderer/src/components/organisms/MarketOffersTable/index.ts b/packages/renderer/src/components/organisms/MarketOffersTable/index.ts new file mode 100644 index 0000000..83942ee --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketOffersTable/index.ts @@ -0,0 +1,18 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +export * from "./MarketOffersTable"; +export * from "./_types"; diff --git a/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.stories.tsx b/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.stories.tsx new file mode 100644 index 0000000..1f6123a --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.stories.tsx @@ -0,0 +1,31 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import type { ComponentStory, ComponentMeta } from "@storybook/react"; +import { MarketsOffers } from "./MarketsOffers"; + +export default { + title: "organisms/MarketsOffers", + component: MarketsOffers, +} as ComponentMeta; + +const Template: ComponentStory = () => { + return ; +}; + +export const Default = Template.bind({}); + +Default.args = {}; diff --git a/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.tsx b/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.tsx new file mode 100644 index 0000000..abbb9e8 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketsOffers/MarketsOffers.tsx @@ -0,0 +1,57 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import type { FC } from "react"; +import { useMemo } from "react"; +import { Group, Loader } from "@mantine/core"; +import { transformToMarketsOffers } from "./_utils"; +import { useMarketsOffers } from "@hooks/haveno/useMarketsOffers"; +import { MarketOffersTable } from "@organisms/MarketOffersTable"; + +export function MarketsOffersLoaded() { + const { data } = useMarketsOffers({ + assetCode: "ETH", + direction: "sell", + }); + const tableData = useMemo(() => transformToMarketsOffers(data || []), [data]); + + if (!data) { + return null; + } + return ; +} + +const MarketsOffersBoot: FC = ({ children }) => { + const { isLoading: isOffersLoading } = useMarketsOffers({ + assetCode: "ETH", + direction: "buy", + }); + return isOffersLoading ? ( + + + + ) : ( + <>{children} + ); +}; + +export function MarketsOffers() { + return ( + + + + ); +} diff --git a/packages/renderer/src/components/organisms/MarketsOffers/_utils.ts b/packages/renderer/src/components/organisms/MarketsOffers/_utils.ts new file mode 100644 index 0000000..b62c5e3 --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketsOffers/_utils.ts @@ -0,0 +1,35 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import type { MarketOfferData } from "@hooks/haveno/useMarketsOffers"; +import type { MarketOffer } from "@organisms/MarketOffersTable"; + +export const transformToMarketsOffers = ( + offers: Array +): Array => { + return offers.map((offer) => ({ + price: offer.price, + priceCurrency: offer.counterCurrencyCode, + amount: offer.amount, + amountCurrency: offer.baseCurrencyCode, + costCurrency: offer.baseCurrencyCode, + paymentMethod: offer.paymentMethodShortName, + cost: offer.txFee, + priceComparison: 0.1, + accountAge: 1, + accountTrades: 1, + })); +}; diff --git a/packages/renderer/src/components/organisms/MarketsOffers/index.ts b/packages/renderer/src/components/organisms/MarketsOffers/index.ts new file mode 100644 index 0000000..e0382fb --- /dev/null +++ b/packages/renderer/src/components/organisms/MarketsOffers/index.ts @@ -0,0 +1,17 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +export * from "./MarketsOffers"; diff --git a/packages/renderer/src/components/organisms/Sidebar/_NavLink.tsx b/packages/renderer/src/components/organisms/Sidebar/_NavLink.tsx index c3f1b1f..120a932 100644 --- a/packages/renderer/src/components/organisms/Sidebar/_NavLink.tsx +++ b/packages/renderer/src/components/organisms/Sidebar/_NavLink.tsx @@ -16,17 +16,20 @@ import { UnstyledButton, Group, Text, createStyles } from "@mantine/core"; import type { ReactNode } from "react"; +import { Link } from "react-router-dom"; interface NavLinkProps { icon: ReactNode; isActive?: boolean; label: string; + link: string; } -export function NavLink({ icon, isActive = false, label }: NavLinkProps) { +export function NavLink({ icon, isActive = false, label, link }: NavLinkProps) { const { classes } = useStyles({ isActive }); + return ( - + {icon} renders without exploding 1`] = `
-
- +
-
- +
-
- +
-
- +
-
- +
, label: "Markets", + link: "/markets", }, { icon: , label: "My Offers", + link: "/", }, { icon: , label: "My Trades", + link: "/", }, { icon: , label: "Notifications", + link: "/", }, { icon: , label: "Account", + link: "/", }, -]; +] as Array; diff --git a/packages/renderer/src/components/templates/AccountLayout/index.tsx b/packages/renderer/src/components/templates/AccountLayout/index.tsx index 5e09d6d..0a2c916 100644 --- a/packages/renderer/src/components/templates/AccountLayout/index.tsx +++ b/packages/renderer/src/components/templates/AccountLayout/index.tsx @@ -19,7 +19,7 @@ import { NavbarLayout } from "@templates/NavbarLayout"; import { AccountSidebar } from "@organisms/AccountSidebar"; interface AccountContentProps { - children: JSX.Element | JSX.Element[]; + children: JSX.Element | Array; } function AccountContent({ children }: AccountContentProps) { @@ -34,7 +34,7 @@ function AccountContent({ children }: AccountContentProps) { } interface AccountLayoutProps { - children: JSX.Element | JSX.Element[]; + children: JSX.Element | Array; } export function AccountLayout({ children }: AccountLayoutProps) { diff --git a/packages/renderer/src/components/templates/NavbarLayout/index.tsx b/packages/renderer/src/components/templates/NavbarLayout/index.tsx index 03cef3a..ee5eb8b 100644 --- a/packages/renderer/src/components/templates/NavbarLayout/index.tsx +++ b/packages/renderer/src/components/templates/NavbarLayout/index.tsx @@ -14,15 +14,24 @@ // limitations under the License. // ============================================================================= -import { Box, createStyles, Group } from "@mantine/core"; import type { FC } from "react"; +import type { DefaultProps } from "@mantine/core"; +import { Box, createStyles, Group } from "@mantine/core"; import { Sidebar } from "@organisms/Sidebar"; -export const NavbarLayout: FC = (props) => { - const { children } = props; - const { classes } = useStyles(); +export const NavbarLayout: FC = ({ + children, + classNames, + className, + ...rest +}) => { + const { classes, cx } = useStyles(undefined, { + name: "NavbarLayout", + classNames, + }); + return ( - + {children} diff --git a/packages/renderer/src/constants/lang/LangKeys.ts b/packages/renderer/src/constants/lang/LangKeys.ts index 1c49a78..6570b3c 100644 --- a/packages/renderer/src/constants/lang/LangKeys.ts +++ b/packages/renderer/src/constants/lang/LangKeys.ts @@ -99,4 +99,11 @@ export enum LangKeys { AccountBackupRestoreBtn = "account.backup.restore.btn", AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification", AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification", + + MarketsOffersColumnPrice = "marketsOffers.columnPrice", + MarketsOffersColumnAmount = "marketsOffers.columnAmount", + MarketsOffersColumnCost = "marketsOffers.columnCost", + MarketsOffersColumnPaymentMethod = "marketsOffers.columnPaymentMethod", + MarketsOffersColumnAccountAge = "marketsOffers.columnAccountAge", + MarketsOffersColumnAccountTrades = "marketsOffers.columnAccountTypes", } diff --git a/packages/renderer/src/constants/lang/en.ts b/packages/renderer/src/constants/lang/en.ts index 433afcd..873bc32 100644 --- a/packages/renderer/src/constants/lang/en.ts +++ b/packages/renderer/src/constants/lang/en.ts @@ -116,6 +116,12 @@ const LangPackEN: { [key in LangKeys]: string } = { "The backup has been downloaded successfully.", [LangKeys.AccountBackupRestoreSuccessNotif]: "The backup has been restored successfully.", + [LangKeys.MarketsOffersColumnPrice]: "Price", + [LangKeys.MarketsOffersColumnAmount]: "Amount", + [LangKeys.MarketsOffersColumnCost]: "Costs", + [LangKeys.MarketsOffersColumnAccountAge]: "Account Age", + [LangKeys.MarketsOffersColumnAccountTrades]: "Account Trades", + [LangKeys.MarketsOffersColumnPaymentMethod]: "Payment Method", }; export default LangPackEN; diff --git a/packages/renderer/src/constants/lang/es.ts b/packages/renderer/src/constants/lang/es.ts index 2901623..e31bb1f 100644 --- a/packages/renderer/src/constants/lang/es.ts +++ b/packages/renderer/src/constants/lang/es.ts @@ -119,6 +119,12 @@ const LangPackES: { [key in LangKeys]: string } = { "La copia de seguridad se ha descargado correctamente.", [LangKeys.AccountBackupRestoreSuccessNotif]: "La copia de seguridad se ha restaurado correctamente.", + [LangKeys.MarketsOffersColumnPrice]: "Precio", + [LangKeys.MarketsOffersColumnAmount]: "Monto", + [LangKeys.MarketsOffersColumnCost]: "Costos", + [LangKeys.MarketsOffersColumnAccountAge]: "Edad de la cuenta", + [LangKeys.MarketsOffersColumnAccountTrades]: "Operaciones de cuenta", + [LangKeys.MarketsOffersColumnPaymentMethod]: "Método de pago", }; export default LangPackES; diff --git a/packages/renderer/src/constants/query-keys.ts b/packages/renderer/src/constants/query-keys.ts index 668f43f..29ee9ce 100644 --- a/packages/renderer/src/constants/query-keys.ts +++ b/packages/renderer/src/constants/query-keys.ts @@ -29,6 +29,8 @@ export enum QueryKeys { XmrSeed = "Haveno.XmrSeed", XmrPrimaryAddress = "Haveno.XmrPrimaryAddress", XmrTxs = "Haveno.XmrTransactions", + MarketsOffers = "Haveno.MarketsOffers", + MyOffers = "Haveno.MyOffers", // Storage StorageAccountInfo = "Storage.AccountInfo", diff --git a/packages/renderer/src/constants/routes.ts b/packages/renderer/src/constants/routes.ts index 95d46fe..90f00f9 100644 --- a/packages/renderer/src/constants/routes.ts +++ b/packages/renderer/src/constants/routes.ts @@ -22,6 +22,7 @@ export const ROUTES = { CreateAccount: "/onboarding/create-account", RestoreBackup: "/onboarding/restore-backup", MyWallet: "/my-wallet", + Markets: "/markets", // Account routes PaymentAccounts: "/account/payment-accounts", diff --git a/packages/renderer/src/hooks/haveno/useMarketsOffers.ts b/packages/renderer/src/hooks/haveno/useMarketsOffers.ts new file mode 100644 index 0000000..58d2618 --- /dev/null +++ b/packages/renderer/src/hooks/haveno/useMarketsOffers.ts @@ -0,0 +1,82 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { useQuery } from "react-query"; +import type { OfferInfo } from "haveno-ts"; +import { useHavenoClient } from "./useHavenoClient"; +import { QueryKeys } from "@constants/query-keys"; + +interface MarketsOfferesQuery { + assetCode: string; + direction?: "buy" | "sell"; +} + +export function useMarketsOffers(query: MarketsOfferesQuery) { + const client = useHavenoClient(); + + return useQuery, Error>( + [QueryKeys.MarketsOffers, query], + async () => { + const offers = await client.getMyOffers(query.assetCode); + return transformData(offers); + } + ); +} + +const transformData = (offers: Array) => { + return offers.map((offerObj: OfferInfo): MarketOfferData => { + const offer = offerObj.toObject(); + + return { + ...offer, + price: parseFloat(offer.price), + volume: parseFloat(offer.volume), + minVolume: parseFloat(offer.minVolume), + triggerPrice: parseFloat(offer.triggerPrice), + }; + }); +}; + +export interface MarketOfferData { + id: string; + direction: string; + price: number; + useMarketBasedPrice: boolean; + marketPriceMarginPct: number; + amount: number; + minAmount: number; + volume: number; + minVolume: number; + buyerSecurityDeposit: number; + triggerPrice: number; + paymentAccountId: string; + paymentMethodId: string; + paymentMethodShortName: string; + baseCurrencyCode: string; + counterCurrencyCode: string; + date: number; + state: string; + sellerSecurityDeposit: number; + offerFeePaymentTxId: string; + txFee: number; + makerFee: number; + isActivated: boolean; + isMyOffer: boolean; + ownerNodeAddress: string; + pubKeyRing: string; + versionNr: string; + protocolVersion: number; +} diff --git a/packages/renderer/src/hooks/haveno/useMyOffers.ts b/packages/renderer/src/hooks/haveno/useMyOffers.ts new file mode 100644 index 0000000..b09c701 --- /dev/null +++ b/packages/renderer/src/hooks/haveno/useMyOffers.ts @@ -0,0 +1,33 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { useQuery } from "react-query"; +import type { OfferInfo } from "haveno-ts"; +import { useHavenoClient } from "./useHavenoClient"; +import { QueryKeys } from "@constants/query-keys"; + +interface MyOfferesQuery { + assetCode: string; + direction?: "buy" | "sell"; +} + +export function useMarketsOffers(query: MyOfferesQuery) { + const client = useHavenoClient(); + + return useQuery, Error>([QueryKeys.MyOffers, query], () => + client.getMyOffers(query.assetCode, query.direction) + ); +} diff --git a/packages/renderer/src/hooks/haveno/useSetCryptoPaymentAccount.ts b/packages/renderer/src/hooks/haveno/useSetCryptoPaymentAccount.ts new file mode 100644 index 0000000..7c394d0 --- /dev/null +++ b/packages/renderer/src/hooks/haveno/useSetCryptoPaymentAccount.ts @@ -0,0 +1,54 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { useMutation, useQueryClient } from "react-query"; +import { showNotification } from "@mantine/notifications"; +import { useHavenoClient } from "./useHavenoClient"; +import { QueryKeys } from "@constants/query-keys"; + +interface SetCryptoPaymentAccount { + accountName: string; + assetCode: string; + address: string; +} + +export function useSetCryptoPaymentAccount() { + const client = useHavenoClient(); + const queryClient = useQueryClient(); + + return useMutation( + async (variables: SetCryptoPaymentAccount) => { + return client.createCryptoPaymentAccount( + variables.accountName, + variables.assetCode, + variables.address + ); + }, + { + onSuccess: () => { + queryClient.invalidateQueries(QueryKeys.PaymentAccounts); + }, + onError: (err: Error) => { + console.dir(err); + showNotification({ + color: "red", + message: err.message || "", + title: "Something went wrong", + }); + }, + } + ); +} diff --git a/packages/renderer/src/hooks/haveno/useSetOffer.ts b/packages/renderer/src/hooks/haveno/useSetOffer.ts new file mode 100644 index 0000000..de567a8 --- /dev/null +++ b/packages/renderer/src/hooks/haveno/useSetOffer.ts @@ -0,0 +1,67 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { useMutation, useQueryClient } from "react-query"; +import { showNotification } from "@mantine/notifications"; +import { useHavenoClient } from "./useHavenoClient"; +import { QueryKeys } from "@constants/query-keys"; + +interface SetOfferVariables { + direction: string; + amount: bigint; + assetCode: string; + paymentAccountId: string; + buyerSecurityDeposit: number; + price?: number; + marketPriceMargin?: number; + triggerPrice?: number; + minAmount?: bigint; +} + +export function useSetOffer() { + const client = useHavenoClient(); + const queryClient = useQueryClient(); + + return useMutation( + async (variables: SetOfferVariables) => { + return client.postOffer( + variables.direction, + variables.amount, + variables.assetCode, + variables.paymentAccountId, + variables.buyerSecurityDeposit, + variables.price, + variables.marketPriceMargin, + variables.triggerPrice, + variables.minAmount + ); + }, + { + onSuccess: () => { + queryClient.invalidateQueries(QueryKeys.MarketsOffers); + queryClient.invalidateQueries(QueryKeys.MyOffers); + }, + onError: (err: Error) => { + console.dir(err); + showNotification({ + color: "red", + message: err.message || "", + title: "Something went wrong", + }); + }, + } + ); +} diff --git a/packages/renderer/src/pages/Markets/MarketsOffers.tsx b/packages/renderer/src/pages/Markets/MarketsOffers.tsx new file mode 100644 index 0000000..d84b3cb --- /dev/null +++ b/packages/renderer/src/pages/Markets/MarketsOffers.tsx @@ -0,0 +1,35 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +import { createStyles } from "@mantine/core"; +import { NavbarLayout } from "@templates/NavbarLayout"; +import { MarketsOffers } from "@organisms/MarketsOffers"; + +export function MarketsOffersPage() { + const { classes } = useStyles(); + + return ( + + + + ); +} + +const useStyles = createStyles(() => ({ + contentArea: { + padding: 0, + }, +})); diff --git a/packages/renderer/src/pages/Markets/index.ts b/packages/renderer/src/pages/Markets/index.ts new file mode 100644 index 0000000..e0382fb --- /dev/null +++ b/packages/renderer/src/pages/Markets/index.ts @@ -0,0 +1,17 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +export * from "./MarketsOffers"; diff --git a/packages/renderer/src/utils/math.ts b/packages/renderer/src/utils/math.ts new file mode 100644 index 0000000..30e1c48 --- /dev/null +++ b/packages/renderer/src/utils/math.ts @@ -0,0 +1,19 @@ +// ============================================================================= +// Copyright 2022 Haveno +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +export const fractionToPercent = (value: number) => { + return value * 100; +}; diff --git a/yarn.lock b/yarn.lock index d5cbeab..742c48c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8187,10 +8187,10 @@ hastscript@^6.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -haveno-ts@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/haveno-ts/-/haveno-ts-0.0.5.tgz#996e290b4dd1659e58f86aff4e0cc0d3c7516ad3" - integrity sha512-HzBuvMmsQLUybk99NqOe50lAcYmnwv2uv0/5IMAIuUpqFmJq2xvPW6vvZASXkRBVYPdRYjLLnUV5ygy8Rq7Yog== +haveno-ts@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/haveno-ts/-/haveno-ts-0.0.6.tgz#ba83986fb741744d3a4441ee323992deff838d0d" + integrity sha512-9OYfr2tGD8nTSSBzgltVeoZdntddwt6WrYj4SLN+9BArqSEdZ9jF+6O2xtUGaVaJDNVli2Eg6pINEAxsUN5MhA== dependencies: "@types/node" "^17.0.30" console "^0.7.2"