From bda613f89b3c99ae6d4ba608bb4024753b90aab9 Mon Sep 17 00:00:00 2001 From: PLANKTON <68655454+kieled@users.noreply.github.com> Date: Sun, 18 Aug 2024 03:06:26 +0300 Subject: [PATCH] 0.2.6 | Custom HD path (#100) * fix send for many utxos * fix , refactoring, redesign * styles * fix bug with loading trasactions, better styles, clear transactions on create new wallet * update version --- package.json | 2 +- .../controllers/provider/controller.ts | 1 + src/shared/constant/index.ts | 14 +- src/shared/locales/languages/en.json | 14 +- src/shared/utils/transactions.ts | 78 ++---- .../inscription-card/components.tsx | 4 +- src/ui/components/layout/component.tsx | 224 +++++++++--------- src/ui/components/select-hint/component.tsx | 18 +- src/ui/components/select/component.tsx | 34 ++- .../settings-layout/styles.module.scss | 2 +- .../switch-address-type/component.tsx | 17 +- .../switch-address-type/styles.module.scss | 2 +- src/ui/components/switch/component.tsx | 26 +- src/ui/components/switch/styles.module.scss | 9 + src/ui/components/tile/component.tsx | 40 +++- src/ui/components/tile/styles.module.scss | 36 ++- src/ui/hooks/transactions.ts | 16 +- src/ui/hooks/wallet.ts | 6 +- src/ui/index.global.scss | 6 +- .../inscription-details/components.tsx | 28 ++- src/ui/pages/main/new-wallet/component.tsx | 5 - .../new-wallet/new-mnemonic/component.tsx | 6 +- .../restore-mnemonic-ordinals/component.tsx | 113 --------- .../styles.module.scss | 27 --- .../new-wallet/restore-mnemonic/component.tsx | 95 ++++++-- .../restore-mnemonic/styles.module.scss | 6 +- .../new-wallet/restore-priv-key/component.tsx | 1 - .../pages/main/new-wallet/styles.module.scss | 4 +- .../main/settings/change-addr-type/index.ts | 1 - src/ui/pages/main/settings/component.tsx | 10 +- .../main/settings/language/component.tsx | 34 +-- .../main/settings/language/styles.module.scss | 10 +- .../change-addr-type/component.tsx | 3 +- .../wallet/change-addr-type}/index.ts | 0 .../change-addr-type/styles.module.scss | 0 .../pages/main/settings/wallet/component.tsx | 12 +- .../settings/wallet/network/component.tsx | 18 +- .../wallet/network/styles.module.scss | 2 +- src/ui/pages/main/switch-wallet/component.tsx | 9 +- .../pages/main/transaction-info/component.tsx | 53 ++--- .../main/transaction-info/styles.module.scss | 15 +- src/ui/pages/main/wallet/styles.module.scss | 2 +- .../wallet/transactions-list/component.tsx | 26 +- src/ui/pages/router.tsx | 12 +- src/ui/utils/index.ts | 30 +-- src/ui/utils/tx-ctx.tsx | 18 +- tailwind.config.js | 2 +- 47 files changed, 510 insertions(+), 581 deletions(-) create mode 100644 src/ui/components/switch/styles.module.scss delete mode 100644 src/ui/pages/main/new-wallet/restore-mnemonic-ordinals/component.tsx delete mode 100644 src/ui/pages/main/new-wallet/restore-mnemonic-ordinals/styles.module.scss delete mode 100644 src/ui/pages/main/settings/change-addr-type/index.ts rename src/ui/pages/main/settings/{ => wallet}/change-addr-type/component.tsx (93%) rename src/ui/pages/main/{new-wallet/restore-mnemonic-ordinals => settings/wallet/change-addr-type}/index.ts (100%) rename src/ui/pages/main/settings/{ => wallet}/change-addr-type/styles.module.scss (100%) diff --git a/package.json b/package.json index 4ac0430b..8c7db40b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nintondo-extension", - "version": "0.2.5.5", + "version": "0.2.6", "private": true, "scripts": { "dev": "bun build.ts --watch", diff --git a/src/background/controllers/provider/controller.ts b/src/background/controllers/provider/controller.ts index 003233c2..406afb0d 100644 --- a/src/background/controllers/provider/controller.ts +++ b/src/background/controllers/provider/controller.ts @@ -22,6 +22,7 @@ type Payload

= { approvalRes?: any; }; +// @ts-ignore class ProviderController implements IProviderController { connect = async () => { if ( diff --git a/src/shared/constant/index.ts b/src/shared/constant/index.ts index a588515d..381bd952 100644 --- a/src/shared/constant/index.ts +++ b/src/shared/constant/index.ts @@ -24,18 +24,18 @@ export const ADDRESS_TYPES: { name: string; hdPath: string; }[] = [ - { - value: AddressType.P2PKH, - label: "P2PKH", - name: "Legacy (P2PKH)", - hdPath: "m/44'/0'/0'/0", - }, { value: AddressType.P2WPKH, label: "P2WPKH", name: "Native Segwit (P2WPKH)", hdPath: "m/84'/0'/0'/0", }, + { + value: AddressType.P2PKH, + label: "P2PKH", + name: "Legacy (P2PKH)", + hdPath: "m/44'/0'/0'/0", + }, { value: AddressType.P2TR, label: "P2TR", @@ -95,3 +95,5 @@ export const DEFAULT_FEES = { }; export const DEFAULT_SERVICE_FEE = 1_000_000; + +export const DEFAULT_HD_PATH = "m/44'/0'/0'/0"; diff --git a/src/shared/locales/languages/en.json b/src/shared/locales/languages/en.json index 3cdc7a72..dad96143 100644 --- a/src/shared/locales/languages/en.json +++ b/src/shared/locales/languages/en.json @@ -115,7 +115,6 @@ "new_wallet": { "new_mnemonic_label": "New mnemonic", "restore_mnemonic_label": "Restore mnemonic", - "restore_ordinals_label": "Restore ordinals mnemonic", "restore_from_private_key_label": "Restore from private key", "restore_from_label": "Restore from", "step_1": "Step 1", @@ -136,7 +135,8 @@ "private_key": "Private key", "recover": "Recover", "invalid_private_key_error": "Invalid private key" - } + }, + "hd_path": "Hd path" }, "receive": { "click_to_copy": "Click to copy", @@ -211,20 +211,22 @@ }, "transaction_info": { "txid": "TxId", - "confirmations_label": "Confirmations:", + "confirmations_label": "Confirmations", "fee_label": "Fee", - "value_label": "Value:", + "value_label": "Value", "open_in_explorer": "Open in explorer", "copied": "Copied", "inputs": "Inputs", "outputs": "Outputs", - "details": "Details" + "details": "Details", + "your_address": "You" }, "hooks": { "transaction": { "insufficient_balance_0": "Insufficient balance. Non-Inscription balance", "insufficient_balance_1": "BEL) is lower than", - "insufficient_balance_2": "BEL" + "insufficient_balance_2": "BEL", + "too_many_utxos": "Too many UTXOs. You must consolidate them in order to send" }, "wallet": { "last_wallet_error": "You cannot delete your last wallet" diff --git a/src/shared/utils/transactions.ts b/src/shared/utils/transactions.ts index 69f7276f..e21d6b1b 100644 --- a/src/shared/utils/transactions.ts +++ b/src/shared/utils/transactions.ts @@ -3,80 +3,38 @@ import { payments } from "belcoinjs-lib"; import Big from "big.js"; import { AddressType } from "bellhdw/src/hd/types"; -export enum TxDirection { - out = 0, - in = 1, -} - -export const getTxDirection = ( - transaction: ITransaction, - targetAddress: string -): TxDirection => { - const includesIn = transaction.vin - .map((i) => i.prevout?.scriptpubkey_address) - .includes(targetAddress); - - if (includesIn) { - return TxDirection.out; - } - return TxDirection.in; -}; - export const getTransactionValue = ( transaction: ITransaction, targetAddress: string, fixed: number = 2 ) => { - const direction = getTxDirection(transaction, targetAddress); - let value: number; - switch (direction) { - case TxDirection.in: - value = - transaction.vout.reduce( - (acc, cur) => - cur.scriptpubkey_address === targetAddress ? acc + cur.value : acc, - 0 - ) / - 10 ** 8; - break; - case TxDirection.out: - value = - (transaction.vin.reduce( - (acc, cur) => - cur.prevout?.scriptpubkey_address === targetAddress - ? acc - cur.prevout?.value - : acc, - 0 - ) + - transaction.vout.reduce( - (acc, cur) => - cur.scriptpubkey_address === targetAddress - ? cur.value + acc - : acc, - 0 - )) / - 10 ** 8; - break; - } + const outputsSum = transaction.vout + .filter((i) => i.scriptpubkey_address === targetAddress) + .reduce((acc, cur) => acc + cur.value, 0); + const inputsSum = transaction.vin + .filter((i) => i.prevout?.scriptpubkey_address === targetAddress) + .reduce((acc, cur) => acc + cur.prevout!.value, 0); - // return value; + const value = Math.abs(outputsSum - inputsSum) / 10 ** 8; - if (typeof fixed !== "undefined") { - if (value < 100) { - return Math.abs(value).toFixed(fixed + 1); - } - return Math.abs(value).toFixed(fixed); + if (value < 1) return parseFloat(value.toFixed(5)).toString(); + if (value < 100) { + return value.toFixed(fixed + 1); } - - return Math.abs(value); + return value.toFixed(fixed); }; export const isIncomeTx = ( transaction: ITransaction, targetAddress: string ) => { - const direction = getTxDirection(transaction, targetAddress); - return direction === TxDirection.in; + const outputsSum = transaction.vout + .filter((i) => i.scriptpubkey_address === targetAddress) + .reduce((acc, cur) => acc + cur.value, 0); + const inputsSum = transaction.vin + .filter((i) => i.prevout?.scriptpubkey_address === targetAddress) + .reduce((acc, cur) => acc + cur.prevout!.value, 0); + return outputsSum - inputsSum > 0; }; export const getScriptForAddress = ( diff --git a/src/ui/components/inscription-card/components.tsx b/src/ui/components/inscription-card/components.tsx index bf07e4c3..4975f86b 100644 --- a/src/ui/components/inscription-card/components.tsx +++ b/src/ui/components/inscription-card/components.tsx @@ -40,7 +40,9 @@ const InscriptionCard: FC = ({ inscriptionId }) => {

{ - navigate("/pages/inscription-details", { state: inscriptionId }); + navigate("/pages/inscription-details", { + state: { inscription_id: inscriptionId }, + }); }} >
diff --git a/src/ui/components/layout/component.tsx b/src/ui/components/layout/component.tsx index da01e34b..6634e525 100644 --- a/src/ui/components/layout/component.tsx +++ b/src/ui/components/layout/component.tsx @@ -4,7 +4,7 @@ import cn from "classnames"; import { ChevronLeftIcon, PlusCircleIcon, - ArrowsUpDownIcon + ArrowsUpDownIcon, } from "@heroicons/react/24/outline"; import { useMemo } from "react"; import { useGetCurrentAccount, useWalletState } from "@/ui/states/walletState"; @@ -36,106 +36,103 @@ export default function PagesLayout() { const currentAccount = useGetCurrentAccount(); const defaultTitles = useMemo( - () => [ - { - route: "/pages/change-addr-type", - title: t("components.layout.change_address_type"), - }, - { - route: "/pages/create-new-account", - title: t("components.layout.create_new_account"), - }, - { - route: "/pages/change-password", - title: t("components.layout.change_password"), - }, - { - backAction: () => { - navigate("/home"); + () => + [ + { + route: "/pages/change-addr-type", + title: t("components.layout.change_address_type"), }, - route: "/pages/finalle-send/@", - title: t("components.layout.send"), - }, - { - route: "/pages/security", - title: t("components.layout.security"), - }, - { - route: "/pages/receive", - title: currentAccount?.name ?? "Account", - }, - { - route: "/pages/switch-wallet", - title: t("components.layout.switch_wallet"), - action: { - icon: , - link: "/pages/create-new-wallet", + { + route: "/pages/create-new-account", + title: t("components.layout.create_new_account"), }, - }, - { - route: "/pages/restore-mnemonic", - title: t("components.layout.restore_from_mnemonic"), - }, - { - route: "/pages/restore-ordinals", - title: t("components.layout.restore_from_mnemonic"), - }, - { - route: "/pages/restore-priv-key", - title: t("components.layout.restore_from_private_key"), - }, - { - route: "/pages/send", - title: t("components.layout.send"), - }, - { - route: "/pages/transaction-info/@", - title: t("components.layout.transaction_info"), - }, - { - route: "/pages/settings", - title: t("components.layout.settings"), - }, - { - route: "/pages/advanced", - title: t("components.layout.advanced"), - }, - { - route: "/pages/show-mnemonic/@", - title: t("components.layout.show_mnemonic"), - }, - { - route: "/pages/show-pk/@", - title: t("components.layout.show_private_key"), - }, - { - route: "/pages/discover", - title: t("components.layout.discover"), - }, - { - route: "/pages/connected-sites", - title: t("components.layout.connected_sites"), - }, - { - route: "/pages/language", - title: t("components.layout.change_language"), - }, - { - route: "/pages/wallet-settings", - title: t("components.layout.wallet_settings"), - }, - { - route: "/pages/network-settings", - title: t("components.layout.network_settings"), - }, - { - route: "/pages/create-send", - title: t("components.layout.send"), - backAction: () => { - navigate("/home"); + { + route: "/pages/change-password", + title: t("components.layout.change_password"), + }, + { + backAction: () => { + navigate("/home"); + }, + route: "/pages/finalle-send/@", + title: t("components.layout.send"), + }, + { + route: "/pages/security", + title: t("components.layout.security"), + }, + { + route: "/pages/receive", + title: currentAccount?.name ?? "Account", + }, + { + route: "/pages/switch-wallet", + title: t("components.layout.switch_wallet"), + action: { + icon: , + link: "/pages/create-new-wallet", + }, }, - }, - ] as IRouteTitle[], + { + route: "/pages/restore-mnemonic", + title: t("components.layout.restore_from_mnemonic"), + }, + { + route: "/pages/restore-priv-key", + title: t("components.layout.restore_from_private_key"), + }, + { + route: "/pages/send", + title: t("components.layout.send"), + }, + { + route: "/pages/transaction-info/@", + title: t("components.layout.transaction_info"), + }, + { + route: "/pages/settings", + title: t("components.layout.settings"), + }, + { + route: "/pages/advanced", + title: t("components.layout.advanced"), + }, + { + route: "/pages/show-mnemonic/@", + title: t("components.layout.show_mnemonic"), + }, + { + route: "/pages/show-pk/@", + title: t("components.layout.show_private_key"), + }, + { + route: "/pages/discover", + title: t("components.layout.discover"), + }, + { + route: "/pages/connected-sites", + title: t("components.layout.connected_sites"), + }, + { + route: "/pages/language", + title: t("components.layout.change_language"), + }, + { + route: "/pages/wallet-settings", + title: t("components.layout.wallet_settings"), + }, + { + route: "/pages/network-settings", + title: t("components.layout.network_settings"), + }, + { + route: "/pages/create-send", + title: t("components.layout.send"), + backAction: () => { + navigate("/home"); + }, + }, + ] as IRouteTitle[], [currentAccount?.name, navigate] ); @@ -171,7 +168,7 @@ export default function PagesLayout() { route: "/pages/inscription-details", title: t("inscription_details.title") + - ` #${currentRoute.state?.inscription_number}`, + ` #${currentRoute.state?.inscription_number ?? ""}`, }, { route: "/pages/switch-account", @@ -191,16 +188,25 @@ export default function PagesLayout() { }, { route: /\/pages\/(inscriptions|bel-20)/, - title:
{ - if (currentRoute.pathname === "/pages/inscriptions") { - navigate("/pages/bel-20"); - } else { - navigate("/pages/inscriptions"); - } - }}> - {currentRoute.pathname === "/pages/inscriptions" ? "Inscriptions" : "BEL-20"} - -
, + title: ( +
{ + if (currentRoute.pathname === "/pages/inscriptions") { + navigate("/pages/bel-20"); + } else { + navigate("/pages/inscriptions"); + } + }} + > + + {currentRoute.pathname === "/pages/inscriptions" + ? "Inscriptions" + : "BEL-20"} + + +
+ ), action: { icon: , }, diff --git a/src/ui/components/select-hint/component.tsx b/src/ui/components/select-hint/component.tsx index e700a19f..be3d5d9f 100644 --- a/src/ui/components/select-hint/component.tsx +++ b/src/ui/components/select-hint/component.tsx @@ -1,4 +1,10 @@ -import { Combobox, Transition } from "@headlessui/react"; +import { + Combobox, + ComboboxInput, + ComboboxOption, + ComboboxOptions, + Transition, +} from "@headlessui/react"; import { FC, Fragment, useEffect, useState } from "react"; import englishWords from "nintondo-bip39/src/wordlists/english.json"; import cn from "classnames"; @@ -54,7 +60,7 @@ const SelectWithHint: FC = ({ selected, setSelected }) => {
- = ({ selected, setSelected }) => { leaveFrom="opacity-100" leaveTo="opacity-0" > - + {filtered.length === 0 && query !== "" ? ( <> ) : ( filtered.map((word) => ( - cn(s.options, { [s.optionsActive]: active }) @@ -94,10 +100,10 @@ const SelectWithHint: FC = ({ selected, setSelected }) => { )} - + )) )} - +
diff --git a/src/ui/components/select/component.tsx b/src/ui/components/select/component.tsx index 8ad31b3e..5e6caa04 100644 --- a/src/ui/components/select/component.tsx +++ b/src/ui/components/select/component.tsx @@ -1,4 +1,10 @@ -import { Listbox, Transition } from "@headlessui/react"; +import { + Listbox, + ListboxButton, + ListboxOption, + ListboxOptions, + Transition, +} from "@headlessui/react"; import { ChevronUpDownIcon, CheckIcon } from "@heroicons/react/24/outline"; import { Fragment } from "react"; @@ -9,6 +15,7 @@ interface Props { label?: string; displayCheckIcon?: boolean; className?: string; + anchor?: "bottom" | "top"; } const Select = ({ @@ -18,15 +25,16 @@ const Select = ({ label, displayCheckIcon = true, className, + anchor, }: Props) => { return ( -
+
{label !== undefined ? ( ) : undefined}
- + {selected.name} ({ aria-hidden="true" /> - + - + {values.map((value, valueIdx) => ( - `relative cursor-default select-none py-2 ${ active ? "bg-input-bg text-text" : "text-text" - } ${ - displayCheckIcon ? "pl-10 pr-4" : "flex justify-center" - }` + } ${displayCheckIcon ? "pl-4 pr-4" : "flex justify-center"}` } value={value} > @@ -70,9 +82,9 @@ const Select = ({ ) : null} )} - + ))} - +
diff --git a/src/ui/components/settings-layout/styles.module.scss b/src/ui/components/settings-layout/styles.module.scss index 295674ae..ca93c55a 100644 --- a/src/ui/components/settings-layout/styles.module.scss +++ b/src/ui/components/settings-layout/styles.module.scss @@ -1,5 +1,5 @@ .settings { - @apply w-full grid grid-cols-2 grid-rows-2; + @apply w-full flex flex-col gap-3; } .wrapper { diff --git a/src/ui/components/switch-address-type/component.tsx b/src/ui/components/switch-address-type/component.tsx index 9a47d6dc..5f49ed38 100644 --- a/src/ui/components/switch-address-type/component.tsx +++ b/src/ui/components/switch-address-type/component.tsx @@ -1,8 +1,8 @@ import s from "./styles.module.scss"; import { ADDRESS_TYPES } from "@/shared/constant"; import { FC } from "react"; -import cn from "classnames"; import { AddressType } from "bellhdw"; +import Tile from "../tile"; interface Props { handler: (type: AddressType) => void; @@ -13,18 +13,13 @@ const SwitchAddressType: FC = ({ handler, selectedType }) => { return (
{ADDRESS_TYPES.map((i) => ( -
handler(i.value)} - > -
-

{i.name.replace(/ \(.*\)$/, "")}

-

{i.label}

-
-
+ selected={selectedType === i.value} + /> ))}
); diff --git a/src/ui/components/switch-address-type/styles.module.scss b/src/ui/components/switch-address-type/styles.module.scss index d09ca07b..4b2436b6 100644 --- a/src/ui/components/switch-address-type/styles.module.scss +++ b/src/ui/components/switch-address-type/styles.module.scss @@ -1,5 +1,5 @@ .allTypes { - @apply w-full h-full grid grid-rows-2 grid-cols-2 gap-3 pt-8 max-h-60; + @apply w-full flex flex-col gap-3 pt-8; } .addressType { diff --git a/src/ui/components/switch/component.tsx b/src/ui/components/switch/component.tsx index 0b93c57e..92535058 100644 --- a/src/ui/components/switch/component.tsx +++ b/src/ui/components/switch/component.tsx @@ -1,6 +1,7 @@ -import { Switch } from "@headlessui/react"; +import { Field, Label, Switch } from "@headlessui/react"; import { FC } from "react"; import cn from "classnames"; +import s from "./styles.module.scss"; interface Props { locked?: boolean; @@ -8,6 +9,7 @@ interface Props { onChange: (value: boolean) => void; label: string; className?: string; + disabled?: boolean; } const SwitchComponent: FC = ({ @@ -16,34 +18,40 @@ const SwitchComponent: FC = ({ value, label, className, + disabled, }) => { return ( - +
{ if (locked) return; onChange(v); }} - className={`${value ? "bg-orange-600" : "bg-gray-500"} - relative inline-flex h-6 w-12 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`} + className={cn( + { "bg-orange-600": value, "bg-gray-500": !value }, + s.switch + )} > - + +
-
+ ); }; diff --git a/src/ui/components/switch/styles.module.scss b/src/ui/components/switch/styles.module.scss new file mode 100644 index 00000000..9fdcf5cf --- /dev/null +++ b/src/ui/components/switch/styles.module.scss @@ -0,0 +1,9 @@ +.switch { + @apply relative inline-flex h-6 w-12 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors + duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 + disabled:opacity-50; +} + +.toggle { + @apply pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out; +} diff --git a/src/ui/components/tile/component.tsx b/src/ui/components/tile/component.tsx index 17d8a547..11f0c05c 100644 --- a/src/ui/components/tile/component.tsx +++ b/src/ui/components/tile/component.tsx @@ -8,23 +8,47 @@ export interface TileProps { onClick?: () => void; label: string; link?: string; - icon: React.ReactNode; + description?: string; + icon?: React.ReactNode; + selected?: boolean; } -const Tile: FC = ({ label, className, link, onClick, icon }) => { +const Tile: FC = ({ + label, + className, + link, + onClick, + icon, + description, + selected, +}) => { + const content = ( + <> + {icon &&
{icon}
} +
+
{label}
+ {description &&
{description}
} +
+ + ); + if (!link && onClick) { return ( -
- {icon} -
{label}
+
+ {content}
); } return ( - - {icon} -
{label}
+ + {content} ); }; diff --git a/src/ui/components/tile/styles.module.scss b/src/ui/components/tile/styles.module.scss index 02ada70c..73726fa1 100644 --- a/src/ui/components/tile/styles.module.scss +++ b/src/ui/components/tile/styles.module.scss @@ -1,21 +1,35 @@ .card { - @apply cursor-pointer h-28 flex items-center justify-center flex-col gap-4 px-3 odd:border-r border-b even:border-l last:border-b-0 border-neutral-800; - - &:nth-last-child(2) { - @apply border-b-0; - } + @apply cursor-pointer flex items-center gap-4 px-3 border rounded-xl bg-neutral-950 border-neutral-800 transition-colors text-lg py-3; &:hover { - > * { - @apply scale-110; + @apply bg-neutral-900 border-orange-400/50; + + .iconContainer { + @apply bg-orange-400/90 text-black; } } +} - > div { - @apply text-xs text-center font-medium; - } +.label { + @apply text-sm font-medium; +} + +.description { + @apply text-xs text-neutral-500; +} + +.content { + @apply flex gap-1.5 flex-col; +} + +.iconContainer { + @apply bg-neutral-900 rounded-lg p-1; > * { - @apply transition-transform; + @apply p-1; } } + +.selected { + @apply bg-orange-400/5 border-orange-400; +} diff --git a/src/ui/hooks/transactions.ts b/src/ui/hooks/transactions.ts index e2e243c9..5c3a139b 100644 --- a/src/ui/hooks/transactions.ts +++ b/src/ui/hooks/transactions.ts @@ -34,10 +34,22 @@ export function useCreateBellsTxCallback() { ) throw new Error("Failed to get current wallet or account"); const fromAddress = currentAccount.address; - const utxos = await apiController.getUtxos(fromAddress, { - amount: toAmount, + let utxos = await apiController.getUtxos(fromAddress, { + amount: + toAmount + (receiverToPayFee ? 0 : gptFeeCalculate(2, 2, feeRate)), }); + + if ((utxos?.length ?? 0) > 5) { + utxos = await apiController.getUtxos(fromAddress, { + amount: toAmount + gptFeeCalculate(1 + utxos!.length, 2, feeRate), + }); + } + if (!utxos) return; + + if (utxos.length > 1000) + throw new Error(t("hooks.transaction.too_many_utxos")); + const safeBalance = (utxos ?? []).reduce((pre, cur) => pre + cur.value, 0); if (safeBalance < toAmount) { throw new Error( diff --git a/src/ui/hooks/wallet.ts b/src/ui/hooks/wallet.ts index fc0dcee6..cf5d6254 100644 --- a/src/ui/hooks/wallet.ts +++ b/src/ui/hooks/wallet.ts @@ -48,12 +48,12 @@ export const useCreateNewWallet = () => { ); const clearSelected = useClearSelectedAccountStats(); const navigate = useNavigate(); + const { clearTransactions } = useTransactionManagerContext(); return async (props: INewWalletProps) => { const wallet = await walletController.createNewWallet(props); const keyring = await keyringController.serializeKeyringById(wallet.id); - let newWallets = [...wallets, wallet]; - newWallets = (await clearSelected(newWallets))!; + let newWallets = (await clearSelected([...wallets, wallet]))!; await walletController.saveWallets({ phrases: [{ id: wallet.id, phrase: props.payload, data: keyring }], @@ -64,7 +64,9 @@ export const useCreateNewWallet = () => { wallets: newWallets, selectedAccount: 0, selectedWallet: newWallets.length - 1, + vaultIsEmpty: false, }); + clearTransactions(); await notificationController.changedAccount(); navigate("/"); diff --git a/src/ui/index.global.scss b/src/ui/index.global.scss index 31b4db64..dcff9f5c 100644 --- a/src/ui/index.global.scss +++ b/src/ui/index.global.scss @@ -87,7 +87,7 @@ input[type="number"] { } .app { - @apply h-full min-h-screen flex justify-center items-center w-full standard:bg-input-bg standard:rounded-2xl overflow-x-hidden standard:min-h-max standard:pb-6 max-standard:max-w-[355px]; + @apply h-full min-h-screen flex justify-center items-center w-full standard:bg-input-bg standard:rounded-2xl overflow-x-hidden standard:min-h-max standard:pb-6 max-standard:max-w-full; .main-route { @apply w-full flex justify-center; @@ -115,11 +115,11 @@ input[type="number"] { } ::-webkit-scrollbar-thumb { - background: #888; + @apply bg-neutral-500 rounded-xl; } ::-webkit-scrollbar-thumb:hover { - background: #555; + @apply bg-neutral-100; } .profile-card { diff --git a/src/ui/pages/main/inscriptions/inscription-details/components.tsx b/src/ui/pages/main/inscriptions/inscription-details/components.tsx index b58c9902..d5390ab4 100644 --- a/src/ui/pages/main/inscriptions/inscription-details/components.tsx +++ b/src/ui/pages/main/inscriptions/inscription-details/components.tsx @@ -87,12 +87,14 @@ const InscriptionDetails = () => { })); const convertToCompletedInscription = useCallback( - async ( - inscriptionId: string - ): Promise => { + async ({ + inscription_id, + }: { + inscription_id: string; + }): Promise => { const [data, location] = await Promise.all([ - apiController.searchContentInscriptionByInscriptionId(inscriptionId), - apiController.getLocationByInscriptionId(inscriptionId), + apiController.searchContentInscriptionByInscriptionId(inscription_id), + apiController.getLocationByInscriptionId(inscription_id), ]); if (!data || !location) return; const parsedLocation = parseLocation(location.location); @@ -102,17 +104,17 @@ const InscriptionDetails = () => { return { content_length: data.file_size, content_type: data.file_type, - inscription_id: inscriptionId, + inscription_id: inscription_id, inscription_number: data.number, - content: inscriptionId, - preview: inscriptionId, + content: inscription_id, + preview: inscription_id, value: value ? value[0] : 0, owner: location.owner, txid: parsedLocation.txid, vout: parsedLocation.vout, offset: parsedLocation.offset, outpoint: `${parsedLocation.txid}i${parsedLocation.vout}`, - genesis: inscriptionId, + genesis: inscription_id, status: { block_hash: "", block_height: data.creation_block, @@ -126,10 +128,18 @@ const InscriptionDetails = () => { useEffect(() => { if (!location.state) return navigate(-1); + if (location.state?.txid) { + setInscription(location.state); + return; + } convertToCompletedInscription(location.state) .then((completeInscription) => { if (completeInscription) { setInscription(completeInscription); + navigate(location.pathname, { + state: completeInscription, + replace: true, + }); } else { navigate(-1); } diff --git a/src/ui/pages/main/new-wallet/component.tsx b/src/ui/pages/main/new-wallet/component.tsx index 60de9311..8470e89a 100644 --- a/src/ui/pages/main/new-wallet/component.tsx +++ b/src/ui/pages/main/new-wallet/component.tsx @@ -24,11 +24,6 @@ const NewWallet = () => { label: t("new_wallet.restore_from_private_key_label"), link: "/pages/restore-priv-key", }, - { - icon: , - label: t("new_wallet.restore_ordinals_label"), - link: "/pages/restore-ordinals", - }, ]; return (
diff --git a/src/ui/pages/main/new-wallet/new-mnemonic/component.tsx b/src/ui/pages/main/new-wallet/new-mnemonic/component.tsx index 96632957..2b41ce92 100644 --- a/src/ui/pages/main/new-wallet/new-mnemonic/component.tsx +++ b/src/ui/pages/main/new-wallet/new-mnemonic/component.tsx @@ -14,6 +14,7 @@ import { t } from "i18next"; import { AddressType } from "bellhdw"; import Switch from "@/ui/components/switch"; import { ss } from "@/ui/utils"; +import { ADDRESS_TYPES } from "@/shared/constant"; const NewMnemonic = () => { const location = useLocation(); @@ -32,7 +33,7 @@ const NewMnemonic = () => { undefined ); const [addressType, setAddressType] = useState( - AddressType.P2PKH + ADDRESS_TYPES[0].value ); const createNewWallet = useCreateNewWallet(); @@ -73,7 +74,6 @@ const NewMnemonic = () => { hideRoot: true, network, }); - await updateWalletState({ vaultIsEmpty: false }); } catch (e) { console.error(e); if (e instanceof Error) toast.error(e.message); @@ -116,7 +116,7 @@ const NewMnemonic = () => { { - const [step, setStep] = useState(1); - const { updateWalletState } = useWalletState(ss(["updateWalletState"])); - const [addressType, setAddressType] = useState(AddressType.P2PKH); - const [mnemonicPhrase, setMnemonicPhrase] = useState<(string | undefined)[]>( - new Array(12).fill("") - ); - const createNewWallet = useCreateNewWallet(); - const navigate = useNavigate(); - const [loading, setLoading] = useState(false); - const { network } = useAppState(ss(["network"])); - - const setMnemonic = (v: string, index: number) => { - if (!v) { - return; - } - const phrase = v.split(" "); - if (phrase.length === 12) setMnemonicPhrase(phrase); - else - setMnemonicPhrase((prev) => { - prev[index] = v; - return prev; - }); - }; - - const onNextStep = () => { - if (mnemonicPhrase.findIndex((f) => f === undefined) !== -1) - toast.error(t("new_wallet.restore_mnemonic.incomplete_phrase_error")); - else setStep(2); - }; - - const onRestore = async () => { - setLoading(true); - try { - await createNewWallet({ - payload: mnemonicPhrase.join(" "), - walletType: "root", - addressType, - hideRoot: false, - hdPath: "m/44'/3'/0'/0/0", - passphrase: "", - network, - }); - await updateWalletState({ vaultIsEmpty: false }); - navigate("/"); - } catch (e) { - toast.error(t("new_wallet.restore_mnemonic.invalid_words_error")); - setStep(1); - } finally { - setLoading(false); - } - }; - - if (loading) return ; - - return ( -
-
-

{t("new_wallet.step_1")}

-

{t("new_wallet.step_2")}

-
- {step === 1 ? ( -
-
- {new Array(12).fill("").map((_, index) => ( -
-

{index + 1}.

- setMnemonic(v, index)} - /> -
- ))} -
-
- -
-
- ) : ( -
- -
- -
-
- )} -
- ); -}; - -export default RestoreMnemonicOrdinals; diff --git a/src/ui/pages/main/new-wallet/restore-mnemonic-ordinals/styles.module.scss b/src/ui/pages/main/new-wallet/restore-mnemonic-ordinals/styles.module.scss deleted file mode 100644 index 33cf9362..00000000 --- a/src/ui/pages/main/new-wallet/restore-mnemonic-ordinals/styles.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -.restoreMnemonic { - @apply w-full h-full flex flex-col items-center gap-6 text-base p-4 pb-0; -} - -.phrase { - @apply grid grid-cols-2 text-sm; -} - -.stepTitle { - @apply flex justify-between w-1/2; -} - -.active { - color: red; -} - -.word { - @apply flex justify-between items-center p-2.5 gap-2; - - .wordInput { - @apply w-4/5; - } -} - -.step { - @apply h-full w-full flex flex-col justify-between; -} diff --git a/src/ui/pages/main/new-wallet/restore-mnemonic/component.tsx b/src/ui/pages/main/new-wallet/restore-mnemonic/component.tsx index 1229e020..81d947e8 100644 --- a/src/ui/pages/main/new-wallet/restore-mnemonic/component.tsx +++ b/src/ui/pages/main/new-wallet/restore-mnemonic/component.tsx @@ -1,7 +1,7 @@ import s from "./styles.module.scss"; import { useCreateNewWallet } from "@/ui/hooks/wallet"; import { useWalletState } from "@/ui/states/walletState"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import cn from "classnames"; import { useNavigate } from "react-router-dom"; import toast from "react-hot-toast"; @@ -13,11 +13,32 @@ import { TailSpin } from "react-loading-icons"; import Switch from "@/ui/components/switch"; import { useAppState } from "@/ui/states/appState"; import { ss } from "@/ui/utils"; +import { ADDRESS_TYPES, DEFAULT_HD_PATH } from "@/shared/constant"; +import Select from "@/ui/components/select"; + +const selectOptions = [ + { + label: "Default", + value: DEFAULT_HD_PATH, + lecacyDerivation: false, + }, + { + label: "Ordinals", + value: "m/44'/3'/0'/0/0", + lecacyDerivation: true, + isLegacySwitchLocked: true, + }, + { + label: "Custom", + value: "", + }, +]; const RestoreMnemonic = () => { const [step, setStep] = useState(1); const { updateWalletState } = useWalletState(ss(["updateWalletState"])); - const [addressType, setAddressType] = useState(AddressType.P2PKH); + const [addressType, setAddressType] = useState(ADDRESS_TYPES[0].value); + const [hdPath, setHdPath] = useState(DEFAULT_HD_PATH); const [mnemonicPhrase, setMnemonicPhrase] = useState<(string | undefined)[]>( new Array(12).fill("") ); @@ -57,7 +78,6 @@ const RestoreMnemonic = () => { hideRoot: !showRootAcc, network, }); - await updateWalletState({ vaultIsEmpty: false }); navigate("/home"); } catch (e) { console.error(e); @@ -68,9 +88,13 @@ const RestoreMnemonic = () => { } }; - const onSwitch = () => { - setShowRootAcc((p) => !p); - }; + const selectedOption = useMemo(() => { + const v = selectOptions.find((i) => i.value === hdPath)?.label; + + if (v) return { name: v }; + + return { name: selectOptions[selectOptions.length - 1].label }; + }, [hdPath]); if (loading) return ; @@ -93,14 +117,6 @@ const RestoreMnemonic = () => {
))}
-
- -