{!!cardanoWallet && walletInfo && walletState && inMemoryWallet && initialHdDiscoveryCompleted && currentChain ? (
diff --git a/apps/browser-extension-wallet/src/views/nami-mode/indexInternal.tsx b/apps/browser-extension-wallet/src/views/nami-mode/indexInternal.tsx
new file mode 100644
index 000000000..b56de0139
--- /dev/null
+++ b/apps/browser-extension-wallet/src/views/nami-mode/indexInternal.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { useWalletStore } from '@src/stores';
+import { useAppInit } from '@hooks';
+import { MainLoader } from '@components/MainLoader';
+import { withDappContext } from '@src/features/dapp/context';
+import { NamiDappConnectorView } from './NamiDappConnectorView';
+import '../../lib/scripts/keep-alive-ui';
+import './index.scss';
+
+export const NamiDappConnector = withDappContext((): React.ReactElement => {
+ const { hdDiscoveryStatus } = useWalletStore();
+
+ useAppInit();
+
+ return
{hdDiscoveryStatus === 'Idle' ? : }
;
+});
diff --git a/apps/browser-extension-wallet/src/views/nami-mode/utils.ts b/apps/browser-extension-wallet/src/views/nami-mode/utils.ts
new file mode 100644
index 000000000..f63286ad3
--- /dev/null
+++ b/apps/browser-extension-wallet/src/views/nami-mode/utils.ts
@@ -0,0 +1,63 @@
+import { Milliseconds, TimeoutError } from '@cardano-sdk/core';
+import { WalletType } from '@cardano-sdk/web-extension';
+import { Wallet } from '@lace/cardano';
+export const isNamiWallet = (wallet?: Wallet.CardanoWallet): boolean => {
+ if (!wallet || wallet.source.wallet.type !== WalletType.InMemory) return false;
+
+ return !wallet.source.wallet.encryptedSecrets.keyMaterial.toString();
+};
+
+/** Copied from @cardano-js-sdk packages/core/src/util/promiseTimeout.ts */
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const promiseTimeout = async
(promise: Promise, timeout: number) => {
+ let timeoutId: NodeJS.Timeout;
+
+ try {
+ return await Promise.race([
+ promise,
+ // eslint-disable-next-line promise/param-names
+ new Promise(
+ (_, reject) =>
+ (timeoutId = setTimeout(() => reject(new TimeoutError('Failed to resolve the promise in time')), timeout))
+ )
+ ]);
+ } finally {
+ if (timeoutId) clearTimeout(timeoutId);
+ }
+};
+
+/** Copied from @cardano-js-sdk packages/core/src/util/tryGetAssetInfos.ts */
+type TryGetAssetInfosProps = {
+ assetIds: Wallet.Cardano.AssetId[];
+ assetProvider: Wallet.AssetProvider;
+ timeout: Milliseconds;
+};
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const tryGetAssetInfos = async ({ assetIds, assetProvider, timeout }: TryGetAssetInfosProps) => {
+ try {
+ return await promiseTimeout(
+ assetProvider.getAssets({
+ assetIds,
+ extraData: { nftMetadata: true, tokenMetadata: true }
+ }),
+ timeout
+ );
+ } catch (error) {
+ console.error('Error: Failed to retrieve assets', error);
+
+ return assetIds.map((assetId) => {
+ const policyId = Wallet.Cardano.AssetId.getPolicyId(assetId);
+ const name = Wallet.Cardano.AssetId.getAssetName(assetId);
+
+ return {
+ assetId,
+ fingerprint: Wallet.Cardano.AssetFingerprint.fromParts(policyId, name),
+ name,
+ policyId,
+ quantity: BigInt(0),
+ supply: BigInt(0)
+ };
+ });
+ }
+};
diff --git a/apps/browser-extension-wallet/xsy-nami-migration-tool-0.0.39.tgz b/apps/browser-extension-wallet/xsy-nami-migration-tool-0.0.39.tgz
deleted file mode 100644
index ab46fcdb0..000000000
Binary files a/apps/browser-extension-wallet/xsy-nami-migration-tool-0.0.39.tgz and /dev/null differ
diff --git a/packages/cardano/src/wallet/lib/cardano-wallet.ts b/packages/cardano/src/wallet/lib/cardano-wallet.ts
index dd4737777..a7bcaa92b 100644
--- a/packages/cardano/src/wallet/lib/cardano-wallet.ts
+++ b/packages/cardano/src/wallet/lib/cardano-wallet.ts
@@ -36,9 +36,9 @@ export interface AccountMetadata {
name: string;
namiMode?: {
avatar: string;
- balance?: string;
- address?: string;
- recentSendToAddress?: string;
+ balance?: Partial>;
+ address?: Partial>;
+ recentSendToAddress?: Partial>;
};
}
diff --git a/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.module.scss b/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.module.scss
new file mode 100644
index 000000000..a27de77a8
--- /dev/null
+++ b/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.module.scss
@@ -0,0 +1,9 @@
+@import '../../../styles/theme.scss';
+@import '../../../../../../common/src/ui/styles/abstracts/typography';
+
+.wrapper {
+ svg {
+ width: 87px !important;
+ height: 87px !important;
+ }
+}
diff --git a/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.tsx b/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.tsx
index ef5e61c87..b8db3d202 100644
--- a/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.tsx
+++ b/packages/core/src/ui/components/NamiMigration/AllDone/AllDone.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Flex, Box, Button, Message } from '@input-output-hk/lace-ui-toolkit';
+import styles from './AllDone.module.scss';
import { Wizard } from '../Wizard';
@@ -13,7 +14,14 @@ export const AllDone = ({ onClose }: Props): JSX.Element => {
return (
-
+
diff --git a/packages/core/src/ui/components/NamiMigration/Customize/lace.mp4 b/packages/core/src/ui/components/NamiMigration/Customize/lace.mp4
index 00c0aaf60..02bffcc36 100644
Binary files a/packages/core/src/ui/components/NamiMigration/Customize/lace.mp4 and b/packages/core/src/ui/components/NamiMigration/Customize/lace.mp4 differ
diff --git a/packages/core/src/ui/components/NamiMigration/Customize/nami.mp4 b/packages/core/src/ui/components/NamiMigration/Customize/nami.mp4
index c1ed4c63c..24067c448 100644
Binary files a/packages/core/src/ui/components/NamiMigration/Customize/nami.mp4 and b/packages/core/src/ui/components/NamiMigration/Customize/nami.mp4 differ
diff --git a/packages/nami/.eslintignore b/packages/nami/.eslintignore
index b3087e352..f2a13b855 100644
--- a/packages/nami/.eslintignore
+++ b/packages/nami/.eslintignore
@@ -8,3 +8,6 @@ typings
src/ui/app/components/**
src/ui/app/pages/**
**/*.js
+**/*.test.ts
+**/*.stories.tsx
+**/*.mock.ts
diff --git a/packages/nami/.eslintrc.js b/packages/nami/.eslintrc.js
index 849b1e23e..460620380 100644
--- a/packages/nami/.eslintrc.js
+++ b/packages/nami/.eslintrc.js
@@ -8,7 +8,6 @@ module.exports = {
'plugin:unicorn/recommended',
'plugin:storybook/recommended',
'plugin:@typescript-eslint/recommended',
- 'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'plugin:functional/external-typescript-recommended',
'plugin:functional/lite',
@@ -90,6 +89,7 @@ module.exports = {
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
+ 'react/no-multi-comp': 'off',
'@typescript-eslint/naming-convention': [
'error',
{
diff --git a/packages/nami/.gitignore b/packages/nami/.gitignore
index 60a86b72e..0dcd3c883 100755
--- a/packages/nami/.gitignore
+++ b/packages/nami/.gitignore
@@ -1,6 +1,7 @@
# Do not ignore the following
!src/ui/typings/*
+!src/typings/*
storybook-static
dist
-*storybook.log
\ No newline at end of file
+*storybook.log
diff --git a/packages/nami/.storybook/main.ts b/packages/nami/.storybook/main.ts
index 4cb959a98..58b3ecb8e 100644
--- a/packages/nami/.storybook/main.ts
+++ b/packages/nami/.storybook/main.ts
@@ -40,9 +40,19 @@ const config: StorybookConfig = {
);
config.plugins.push(
+ new NormalModuleReplacementPlugin(
+ /features\/common-outside-handles-provider\/useOutsideHandles$/,
+ join(
+ __dirname,
+ '../src/features/common-outside-handles-provider/useOutsideHandles.mock.ts',
+ ),
+ ),
new NormalModuleReplacementPlugin(
/features\/outside-handles-provider\/useOutsideHandles$/,
- join(__dirname, '../src/features/outside-handles-provider/useOutsideHandles.mock.ts'),
+ join(
+ __dirname,
+ '../src/features/outside-handles-provider/useOutsideHandles.mock.ts',
+ ),
),
new NormalModuleReplacementPlugin(
/adapters\/collateral$/,
@@ -76,10 +86,6 @@ const config: StorybookConfig = {
/^react-router-dom$/,
join(__dirname, './mocks/react-router-dom.mock.tsx'),
),
- new NormalModuleReplacementPlugin(
- /loader$/,
- join(__dirname, '../src/api/loader.mock.ts'),
- ),
new NormalModuleReplacementPlugin(
/signTxUtil$/,
join(
diff --git a/packages/nami/.storybook/mocks/cardano-sdk.mock.ts b/packages/nami/.storybook/mocks/cardano-sdk.mock.ts
index 3780debe5..be0a71b14 100644
--- a/packages/nami/.storybook/mocks/cardano-sdk.mock.ts
+++ b/packages/nami/.storybook/mocks/cardano-sdk.mock.ts
@@ -16,6 +16,11 @@ export const WalletType = {
}
export const Serialization = {
+ Transaction: {
+ fromCbor: (d) => ({
+ toCore: () => d
+ })
+ },
TransactionOutput: function () {},
Value: function () {
return { setMultiasset: () => {} };
diff --git a/packages/nami/.storybook/mocks/react-router-dom.mock.tsx b/packages/nami/.storybook/mocks/react-router-dom.mock.tsx
index 8cb80269a..db061bf97 100644
--- a/packages/nami/.storybook/mocks/react-router-dom.mock.tsx
+++ b/packages/nami/.storybook/mocks/react-router-dom.mock.tsx
@@ -5,15 +5,17 @@ export * from 'react-router-dom';
export let mockedHistory: string[] = [];
-export const useHistory = fn(() => ({
+export const useHistory: jest.Mock = fn(() => ({
push: () => void 0,
replace: (route: string) => (mockedHistory = [route]),
})).mockName('useHistory');
-export const HashRouter = fn(({ children }) => <>{children} >).mockName(
+export const useLocation: jest.Mock = fn().mockName('useHistory');
+
+export const HashRouter: jest.Mock = fn(({ children }) => <>{children} >).mockName(
'HashRouter',
);
-export const Switch = fn(({ children }) => <>{children} >).mockName('Switch');
+export const Switch: jest.Mock = fn(({ children }) => <>{children} >).mockName('Switch');
-export const Route = fn().mockName('Route');
+export const Route: jest.Mock = fn().mockName('Route');
diff --git a/packages/nami/.storybook/preview.tsx b/packages/nami/.storybook/preview.tsx
index 4d1b83cf0..3328f1653 100644
--- a/packages/nami/.storybook/preview.tsx
+++ b/packages/nami/.storybook/preview.tsx
@@ -5,8 +5,10 @@ import 'focus-visible/dist/focus-visible';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import { theme } from '../src/ui/theme';
-import { Scrollbars } from '../src/ui/app/components/scrollbar';
+import { Scrollbars } from 'react-custom-scrollbars-2';
import { OutsideHandlesProvider } from '../src/features/outside-handles-provider';
+import { CommonOutsideHandlesProvider } from '../src/features/common-outside-handles-provider';
+import { WalletType } from '@cardano-sdk/web-extension';
const noop = (async () => {}) as any;
const mock = {} as any;
@@ -23,55 +25,103 @@ const preview: Preview = {
loaders: [],
};
-const cardanoCoin = {
- id: '1',
- name: 'Cardano',
- decimals: 6,
- symbol: 't₳'
-};
-
export const decorators = [
(Story, { parameters: { colorMode, ...props } }) => (
({
- status: true,
- url: 'https://cardano-preprod.blockfrost.io/api/v0'
- })
- }
- cardanoCoin={cardanoCoin}
- sendEventToPostHog={noop}
- theme="light"
- fiatCurrency="USD"
- currentChain={{ networkId: 0, networkMagic: 0 }}
+ collateralFee={BigInt(200)}
+ isInitializingCollateral={false}
+ initializeCollateralTx={noop}
+ submitCollateralTx={noop}
+ addAccount={noop}
+ removeDapp={noop}
+ connectedDapps={[]}
isAnalyticsOptIn={false}
- walletAddress=""
- inMemoryWallet={mock}
- walletManager={mock}
- walletRepository={mock}
handleAnalyticsChoice={noop}
createWallet={noop}
- getMnemonic={noop}
deleteWallet={noop}
+ fiatCurrency="USD"
setFiatCurrency={noop}
+ theme="light"
setTheme={noop}
- withSignTxConfirmation={noop}
+ currentChain={{ networkId: 0, networkMagic: 0 }}
+ cardanoPrice={0.3}
+ walletManager={mock}
+ walletRepository={mock}
+ switchNetwork={noop}
+ environmentName="Preprod"
+ availableChains={['Mainnet', 'Preprod']}
+ enableCustomNode={noop}
+ getCustomSubmitApiForNetwork={() => ({
+ status: true,
+ url: 'https://cardano-preprod.blockfrost.io/api/v0',
+ })}
+ defaultSubmitApi={''}
+ isValidURL={() => true}
+ setAvatar={noop}
+ buildDelegation={noop}
+ signAndSubmitTransaction={noop}
+ passwordUtil={{
+ clearSecrets: noop,
+ password: { input: noop, value: 'pw' },
+ setPassword: noop,
+ }}
+ delegationTxFee="200"
+ delegationStoreDelegationTxBuilder={noop}
+ collateralTxBuilder={noop}
+ setSelectedStakePool={noop}
+ isBuildingTx={false}
+ stakingError={''}
+ getStakePoolInfo={noop}
+ resetDelegationState={noop}
+ hasNoFunds={false}
+ switchWalletMode={noop}
+ openExternalLink={noop}
+ walletAddresses={['']}
+ eraSummaries={[
+ {
+ parameters: {
+ epochLength: 20,
+ slotLength: 20000 as any,
+ },
+ start: {
+ slot: 1,
+ time: new Date(),
+ },
+ },
+ ]}
+ transactions={[]}
+ getTxInputsValueAndAddress={noop}
+ certificateInspectorFactory={noop}
+ connectHW={noop}
+ createHardwareWalletRevamped={noop}
+ saveHardwareWallet={noop}
+ removeWallet={noop}
+ setDeletingWallet={noop}
>
-
-
- {Story({ args: { colorMode, ...props } })}
-
-
+
+ {Story({ args: { colorMode, ...props } })}
+
+
+
),
];
diff --git a/packages/nami/package.json b/packages/nami/package.json
index 1f9a1780a..8db01ec0b 100644
--- a/packages/nami/package.json
+++ b/packages/nami/package.json
@@ -49,7 +49,7 @@
"prepack": "yarn build",
"prestart": "yarn build",
"start": "node dist/index.js",
- "storybook": "storybook dev -p 6006",
+ "storybook": "storybook dev -p 6006 --debug",
"test": "NODE_ENV=test run -T jest -c ./test/jest.config.js --silent",
"test-storybook:ci": "echo \"@lace/nami: no test-storybook:ci specified\"",
"type-check": "echo \"@lace/nami: no type-check command specified\"",
@@ -60,6 +60,7 @@
"@cardano-sdk/core": "0.41.1",
"@cardano-sdk/crypto": "0.1.30",
"@cardano-sdk/tx-construction": "0.21.9",
+ "@cardano-sdk/util": "0.15.5",
"@cardano-sdk/web-extension": "0.34.11",
"@chakra-ui/css-reset": "1.0.0",
"@chakra-ui/icons": "1.0.13",
@@ -78,6 +79,7 @@
"focus-visible": "^5.2.0",
"framer-motion": "^4.1.16",
"javascript-time-ago": "^2.5.10",
+ "lodash": "^4.17.21",
"promise-latest": "^1.0.4",
"react-custom-scrollbars-2": "^4.5.0",
"react-icons": "4.2.0",
@@ -104,6 +106,7 @@
"@storybook/test": "^8.1.6",
"@svgr/rollup": "^6.1.2",
"@types/chrome": "^0.0.268",
+ "@types/lodash": "^4.17.7",
"@types/react": "17.0.2",
"@types/react-kawaii": "^0.17.3",
"@types/react-lazy-load-image-component": "1.6.4",
diff --git a/packages/nami/src/adapters/account.test.ts b/packages/nami/src/adapters/account.test.ts
index 3cc399806..96b39a746 100644
--- a/packages/nami/src/adapters/account.test.ts
+++ b/packages/nami/src/adapters/account.test.ts
@@ -1,7 +1,7 @@
import { act, renderHook } from '@testing-library/react-hooks';
import { BehaviorSubject, of } from 'rxjs';
-import { getNextAccountIndex, useAccount } from './account';
+import { getNextAccountIndex, useAccountUtil } from './account';
import type {
AnyWallet,
@@ -43,7 +43,7 @@ const wallet2 = genWallet('wallet2', 'InMemory');
const trezorWallet1 = genWallet('trezor wallet1', 'Trezor');
const trezorWallet2 = genWallet('trezor wallet2', 'Trezor');
const ledgerWallet1 = genWallet('ledger wallet1', 'Ledger');
-const ledgerrWallet2 = genWallet('ledger wallet2', 'Ledger');
+const ledgerWallet2 = genWallet('ledger wallet2', 'Ledger');
const walletRepository = [
wallet1,
@@ -51,7 +51,7 @@ const walletRepository = [
trezorWallet1,
trezorWallet2,
ledgerWallet1,
- ledgerrWallet2,
+ ledgerWallet2,
];
const getAccountData = (
@@ -90,7 +90,7 @@ describe('useAccount', () => {
}) as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -109,9 +109,15 @@ describe('useAccount', () => {
getAccountData(trezorWallet1, 0),
getAccountData(trezorWallet1, 1),
getAccountData(trezorWallet1, 2),
+ getAccountData(trezorWallet2, 0),
+ getAccountData(trezorWallet2, 1),
+ getAccountData(trezorWallet2, 2),
getAccountData(ledgerWallet1, 0),
getAccountData(ledgerWallet1, 1),
getAccountData(ledgerWallet1, 2),
+ getAccountData(ledgerWallet2, 0),
+ getAccountData(ledgerWallet2, 1),
+ getAccountData(ledgerWallet2, 2),
]);
expect(result.current.allAccounts).toEqual([
@@ -121,9 +127,15 @@ describe('useAccount', () => {
getAccountData(trezorWallet1, 0),
getAccountData(trezorWallet1, 1),
getAccountData(trezorWallet1, 2),
+ getAccountData(trezorWallet2, 0),
+ getAccountData(trezorWallet2, 1),
+ getAccountData(trezorWallet2, 2),
getAccountData(ledgerWallet1, 0),
getAccountData(ledgerWallet1, 1),
getAccountData(ledgerWallet1, 2),
+ getAccountData(ledgerWallet2, 0),
+ getAccountData(ledgerWallet2, 1),
+ getAccountData(ledgerWallet2, 2),
]);
});
@@ -135,7 +147,7 @@ describe('useAccount', () => {
}) as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -156,9 +168,15 @@ describe('useAccount', () => {
getAccountData(wallet1, 2),
getAccountData(trezorWallet1, 0),
getAccountData(trezorWallet1, 2),
+ getAccountData(trezorWallet2, 0),
+ getAccountData(trezorWallet2, 1),
+ getAccountData(trezorWallet2, 2),
getAccountData(ledgerWallet1, 0),
getAccountData(ledgerWallet1, 1),
getAccountData(ledgerWallet1, 2),
+ getAccountData(ledgerWallet2, 0),
+ getAccountData(ledgerWallet2, 1),
+ getAccountData(ledgerWallet2, 2),
]);
expect(result.current.allAccounts).toEqual([
@@ -168,9 +186,15 @@ describe('useAccount', () => {
getAccountData(trezorWallet1, 0),
getAccountData(trezorWallet1, 1),
getAccountData(trezorWallet1, 2),
+ getAccountData(trezorWallet2, 0),
+ getAccountData(trezorWallet2, 1),
+ getAccountData(trezorWallet2, 2),
getAccountData(ledgerWallet1, 0),
getAccountData(ledgerWallet1, 1),
getAccountData(ledgerWallet1, 2),
+ getAccountData(ledgerWallet2, 0),
+ getAccountData(ledgerWallet2, 1),
+ getAccountData(ledgerWallet2, 2),
]);
});
@@ -182,7 +206,7 @@ describe('useAccount', () => {
}) as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -226,7 +250,7 @@ describe('useAccount', () => {
}) as unknown as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -252,7 +276,7 @@ describe('useAccount', () => {
}) as unknown as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$: wallets$ as unknown as Wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -311,7 +335,7 @@ describe('useAccount', () => {
}) as unknown as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -348,7 +372,7 @@ describe('useAccount', () => {
}) as unknown as WalletManagerApi['activeWalletId$'];
const { result } = renderHook(() =>
- useAccount({
+ useAccountUtil({
wallets$,
activeWalletId$,
updateAccountMetadata: mockUpdateAccountMetadata,
@@ -364,7 +388,7 @@ describe('useAccount', () => {
walletId: 'wallet',
});
- expect(mockRemoveWallet).toHaveBeenCalledWith('wallet');
+ expect(mockRemoveWallet).toHaveBeenCalledWith();
expect(mockRemoveAccount).not.toHaveBeenCalled();
});
diff --git a/packages/nami/src/adapters/account.ts b/packages/nami/src/adapters/account.ts
index d02f1f29e..98f344128 100644
--- a/packages/nami/src/adapters/account.ts
+++ b/packages/nami/src/adapters/account.ts
@@ -4,6 +4,7 @@ import {
WalletType,
type WalletId,
type HardwareWallet,
+ type InMemoryWallet,
type Bip32WalletAccount,
type AnyWallet,
type RemoveAccountProps,
@@ -34,7 +35,7 @@ interface AccountsProps {
removeAccount: (
props: Readonly,
) => Promise;
- removeWallet: (props: WalletId) => Promise;
+ removeWallet: (isChangePasswordFlow?: boolean) => Promise;
updateAccountMetadata: (
props: Readonly>,
) => Promise>;
@@ -45,8 +46,9 @@ export interface Account {
walletId: string;
name: string;
avatar?: string;
- balance?: string;
- recentSendToAddress?: string;
+ balance?: Partial>;
+ address?: Partial>;
+ recentSendToAddress?: Partial>;
type?: WalletType;
}
@@ -116,6 +118,10 @@ export const getNextAccountIndex = (
return walletAccounts.length;
};
+type NonScriptWallet =
+ | HardwareWallet
+ | InMemoryWallet;
+
const getAcountsMapper =
(
wallet: Readonly>,
@@ -132,44 +138,54 @@ const getAcountsMapper =
});
export const useAccount = ({
- chainId = Wallet.Cardano.ChainIds.Mainnet,
wallets$,
activeWalletId$,
- addAccount,
- activateAccount,
- removeAccount,
- removeWallet,
- updateAccountMetadata,
-}: Readonly): UseAccount => {
+}: Readonly>) => {
const activeWallet = useObservable(activeWalletId$);
const wallets = useObservable(wallets$);
const { walletId, accountIndex } = activeWallet ?? {};
const allAccountsSorted = useMemo(() => {
const allWallets = wallets?.filter(
- (w): w is HardwareWallet =>
- w.type !== WalletType.Script,
+ (w): w is NonScriptWallet => w.type !== WalletType.Script,
);
const groupedWallets = groupBy(allWallets, ({ type }) => type);
+
+ // TODO: refactor and reduce complexity + nesting
return flatten(
Object.entries(groupedWallets)
.sort(([type1], [type2]) => {
- if (type1 === WalletType.InMemory && type2 !== WalletType.InMemory)
+ if (
+ (type1 as WalletType) === WalletType.InMemory &&
+ (type2 as WalletType) !== WalletType.InMemory
+ )
return -1;
- if (type2 === WalletType.InMemory && type1 !== WalletType.InMemory)
+ if (
+ (type2 as WalletType) === WalletType.InMemory &&
+ (type1 as WalletType) !== WalletType.InMemory
+ )
return 1;
return 0;
})
.map(([_type, wallets]) => {
- const wallet =
- wallets.find(w => w.walletId === walletId) ?? wallets[0];
- const accountsMapper = getAcountsMapper(wallet);
- return 'accounts' in wallet
- ? wallet.accounts
- // eslint-disable-next-line functional/prefer-tacit
- .map(account => accountsMapper(account))
- .sort((a, b) => a.index - b.index)
- : [];
+ const filteredWallets =
+ (_type as WalletType) === WalletType.InMemory
+ ? // show only current/first in memory wallet accouts
+ [wallets.find(w => w.walletId === walletId) ?? wallets[0]]
+ : // show all hws accouts
+ wallets;
+
+ return flatten(
+ filteredWallets.map(wallet => {
+ const accountsMapper = getAcountsMapper(wallet);
+ return 'accounts' in wallet
+ ? wallet.accounts
+ // eslint-disable-next-line functional/prefer-tacit
+ .map(account => accountsMapper(account))
+ .sort((a, b) => a.index - b.index)
+ : [];
+ }),
+ );
}),
);
}, [wallets, walletId, accountIndex]);
@@ -185,6 +201,34 @@ export const useAccount = ({
[allAccountsSorted, accountIndex, activeWallet?.walletId],
);
+ return useMemo(
+ () => ({
+ allAccountsSorted,
+ activeAccount,
+ }),
+ [allAccountsSorted, activeAccount],
+ );
+};
+
+export const useAccountUtil = ({
+ chainId = Wallet.Cardano.ChainIds.Mainnet,
+ wallets$,
+ activeWalletId$,
+ addAccount,
+ activateAccount,
+ removeAccount,
+ removeWallet,
+ updateAccountMetadata,
+}: Readonly): UseAccount => {
+ const activeWallet = useObservable(activeWalletId$);
+ const wallets = useObservable(wallets$);
+ const { walletId, accountIndex } = activeWallet ?? {};
+
+ const { allAccountsSorted, activeAccount } = useAccount({
+ wallets$,
+ activeWalletId$,
+ });
+
return {
allAccounts: allAccountsSorted,
activeAccount,
@@ -219,9 +263,9 @@ export const useAccount = ({
const isLastAccount = !allAccountsSorted.some(
a => a.walletId === walletId && a.index !== accountIndex,
);
- console.log(isLastAccount, { accountIndex, walletId });
+
await (isLastAccount
- ? removeWallet(walletId)
+ ? removeWallet()
: removeAccount({ accountIndex, walletId }));
},
[removeAccount, walletId, allAccountsSorted],
diff --git a/packages/nami/src/adapters/assets.test.ts b/packages/nami/src/adapters/assets.test.ts
index ceb41bd91..2733b8e68 100644
--- a/packages/nami/src/adapters/assets.test.ts
+++ b/packages/nami/src/adapters/assets.test.ts
@@ -66,7 +66,7 @@ const testCases = [
quantity: '1',
unit: 'aa0f536f65c1ffd33001a831c418f1e2f3105cfd9741bbcb6202aedc001bc280676f6f7365',
decimals: 0,
- image: null,
+ image: undefined,
},
],
[
@@ -101,7 +101,7 @@ const testCases = [
quantity: '38709316',
unit: '659ab0b5658687c2e74cd10dba8244015b713bf503b90557769d77a757696e67526964657273',
decimals: 6,
- image: null,
+ image: undefined,
},
],
[
@@ -169,7 +169,7 @@ const testCases = [
quantity: '67280096',
unit: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed724d494e',
decimals: 0,
- image: null,
+ image: undefined,
},
],
[
@@ -196,7 +196,7 @@ const testCases = [
quantity: '1',
unit: '359289937f6cd0478f2c0737eed4ba879725c09d9d80caeeadf4a67f6261746d616e',
decimals: 0,
- image: null,
+ image: undefined,
},
],
[
diff --git a/packages/nami/src/adapters/collateral.mock.ts b/packages/nami/src/adapters/collateral.mock.ts
index aca50c840..6456b0c8a 100644
--- a/packages/nami/src/adapters/collateral.mock.ts
+++ b/packages/nami/src/adapters/collateral.mock.ts
@@ -6,6 +6,6 @@ import * as actualApi from './collateral';
export * from './collateral';
-export const useCollateral = fn(actualApi.useCollateral).mockName(
+export const useCollateral: jest.Mock = fn(actualApi.useCollateral).mockName(
'useCollateral',
);
diff --git a/packages/nami/src/adapters/collateral.test.ts b/packages/nami/src/adapters/collateral.test.ts
index 60f5c0284..7b0b6e0b7 100644
--- a/packages/nami/src/adapters/collateral.test.ts
+++ b/packages/nami/src/adapters/collateral.test.ts
@@ -1,8 +1,7 @@
-import { useObservable } from '@lace/common';
import { renderHook } from '@testing-library/react-hooks';
import { of } from 'rxjs';
-import { useCollateral } from './collateral';
+import { getCollateralUtxo, useCollateral } from './collateral';
import type { Wallet } from '@lace/cardano';
@@ -138,4 +137,22 @@ describe('useCollateral', () => {
);
expect(mockWithSignTxConfirmation).toBeCalledTimes(1);
});
+
+ describe('getCollateralUtxo', () => {
+ test('should return the UTXO that matches the tx ID and has enough ADA', async () => {
+ const utxo = [{ txId: 'txId' }, { value: { coins: BigInt(5_000_000) } }];
+ const available$ = of([utxo]);
+
+ const foundUtxo = await getCollateralUtxo(
+ 'txId' as Wallet.Cardano.TransactionId,
+ {
+ utxo: {
+ available$,
+ },
+ } as unknown as Wallet.ObservableWallet,
+ );
+
+ expect(foundUtxo).toEqual(utxo);
+ });
+ });
});
diff --git a/packages/nami/src/adapters/collateral.ts b/packages/nami/src/adapters/collateral.ts
index 039efbed2..a447f26df 100644
--- a/packages/nami/src/adapters/collateral.ts
+++ b/packages/nami/src/adapters/collateral.ts
@@ -1,6 +1,9 @@
import { useCallback, useMemo } from 'react';
+import { isNotNil } from '@cardano-sdk/util';
import { useObservable } from '@lace/common';
+import { firstValueFrom } from 'rxjs';
+import { filter, map, take } from 'rxjs/operators';
import type { Wallet } from '@lace/cardano';
@@ -36,3 +39,20 @@ export const useCollateral = ({
return { hasCollateral, reclaimCollateral, submitCollateral };
};
+
+export const getCollateralUtxo = async (
+ txId: Readonly,
+ inMemoryWallet: Wallet.ObservableWallet,
+): Promise => {
+ return await firstValueFrom(
+ inMemoryWallet.utxo.available$.pipe(
+ map(utxos =>
+ utxos.find(
+ o => o[0].txId === txId && o[1].value.coins === BigInt('5000000'),
+ ),
+ ),
+ filter(isNotNil),
+ take(1),
+ ),
+ );
+};
diff --git a/packages/nami/src/adapters/delegation.mock.ts b/packages/nami/src/adapters/delegation.mock.ts
index 6ae3261c9..4a9a29a19 100644
--- a/packages/nami/src/adapters/delegation.mock.ts
+++ b/packages/nami/src/adapters/delegation.mock.ts
@@ -6,6 +6,6 @@ import * as actualApi from './delegation';
export * from './delegation';
-export const useDelegation = fn(actualApi.useDelegation).mockName(
+export const useDelegation: jest.Mock = fn(actualApi.useDelegation).mockName(
'useDelegation',
);
diff --git a/packages/nami/src/adapters/transactions.mock.ts b/packages/nami/src/adapters/transactions.mock.ts
index 8fc2973a3..0bddcaf4c 100644
--- a/packages/nami/src/adapters/transactions.mock.ts
+++ b/packages/nami/src/adapters/transactions.mock.ts
@@ -6,4 +6,6 @@ import * as actualApi from './transactions';
export * from './transactions';
-export const useTxInfo = fn(actualApi.useTxInfo).mockName('useTxInfo');
+export const useTxInfo: jest.Mock = fn(actualApi.useTxInfo).mockName(
+ 'useTxInfo',
+);
diff --git a/packages/nami/src/adapters/transactions.ts b/packages/nami/src/adapters/transactions.ts
index db7462c78..98a585ac0 100644
--- a/packages/nami/src/adapters/transactions.ts
+++ b/packages/nami/src/adapters/transactions.ts
@@ -8,10 +8,12 @@ import {
poolRetirementInspector,
poolRegistrationInspector,
coalesceValueQuantities,
+ Serialization,
} from '@cardano-sdk/core';
import { Wallet } from '@lace/cardano';
import { useObservable } from '@lace/common';
+import { useCommonOutsideHandles } from '../features/common-outside-handles-provider';
import { useOutsideHandles } from '../features/outside-handles-provider/useOutsideHandles';
import { toAsset } from './assets';
@@ -55,13 +57,15 @@ const getTxType = ({
if (inputsAddr.every(addr => addr === currentAddress)) {
// sender
+ const internalOrExternalOut = outputsAddr.some(
+ addr => addresses.includes(addr) && addr !== currentAddress,
+ )
+ ? 'internalOut'
+ : 'externalOut';
+
return outputsAddr.every(addr => addr === currentAddress)
? 'self'
- : outputsAddr.some(
- addr => addresses.includes(addr) && addr !== currentAddress,
- )
- ? 'internalOut'
- : 'externalOut';
+ : internalOrExternalOut;
} else if (inputsAddr.every(addr => addr !== currentAddress)) {
// receiver
return inputsAddr.some(addr => addresses.includes(addr))
@@ -118,20 +122,53 @@ interface CalculateAmountProps {
validContract: boolean;
}
+const getAddressCredentials = (
+ address: string,
+): [
+ Wallet.Crypto.Hash28ByteBase16 | undefined,
+ Wallet.Crypto.Hash28ByteBase16 | undefined,
+] => {
+ const addr = Wallet.Cardano.Address.fromBech32(address);
+ return [
+ addr.getProps().paymentPart?.hash,
+ addr.getProps().delegationPart?.hash,
+ ];
+};
+
+const matchesAnyCredential = (
+ address: Wallet.Cardano.PaymentAddress | undefined,
+ [ownPaymentCred, ownStakingCred]: [
+ Wallet.Crypto.Hash28ByteBase16 | undefined,
+ Wallet.Crypto.Hash28ByteBase16 | undefined,
+ ],
+) => {
+ if (!address) return false;
+ const [otherPaymentCred, otherStakingCred] = getAddressCredentials(
+ address.toString(),
+ );
+ return (
+ otherPaymentCred === ownPaymentCred || otherStakingCred === ownStakingCred
+ );
+};
+
const calculateAmount = ({
currentAddress,
uTxOList,
validContract = false,
}: CalculateAmountProps): CalculatedAmount[] => {
+ const ownCredentials = getAddressCredentials(currentAddress);
+
const inputs = compileOutputs(
uTxOList.inputs.filter(
({ address, txId }) =>
- address === currentAddress &&
+ matchesAnyCredential(address, ownCredentials) &&
!(uTxOList.collaterals?.find(c => c.txId === txId) && validContract),
),
);
const outputs = compileOutputs(
- uTxOList.outputs.filter(({ address }) => address === currentAddress),
+ uTxOList.outputs.filter(({ address }) =>
+ matchesAnyCredential(address, ownCredentials),
+ ),
);
const amounts: Amount[] = [];
@@ -250,17 +287,39 @@ export type TxInfo = Pick &
refund: string;
};
+export interface EncodeToCborArgs {
+ body: Wallet.Cardano.TxBody;
+ witness?: Wallet.Cardano.Witness;
+ auxiliaryData?: Wallet.Cardano.AuxiliaryData;
+}
+
+export const encodeToCbor = (args: EncodeToCborArgs): Serialization.TxCBOR => {
+ const transaction = new Serialization.Transaction(
+ Serialization.TransactionBody.fromCore(args.body),
+ Serialization.TransactionWitnessSet.fromCore(
+ args.witness
+ ? (args.witness as Cardano.Witness)
+ : { signatures: new Map() },
+ ),
+ args.auxiliaryData
+ ? Serialization.AuxiliaryData.fromCore(args.auxiliaryData)
+ : undefined,
+ );
+
+ return transaction.toCbor();
+};
+
export const useTxInfo = (
- tx: Wallet.Cardano.HydratedTx,
+ tx: Wallet.Cardano.HydratedTx | Wallet.TxInFlight,
): TxInfo | undefined => {
const [txInfo, setTxInfo] = useState();
const {
getTxInputsValueAndAddress,
- inMemoryWallet,
eraSummaries,
walletAddresses,
certificateInspectorFactory,
} = useOutsideHandles();
+ const { inMemoryWallet } = useCommonOutsideHandles();
const protocolParameters = useObservable(inMemoryWallet.protocolParameters$);
const assetsInfo = useObservable(inMemoryWallet.assetInfo$);
const rewardAccounts = useObservable(
@@ -273,7 +332,7 @@ export const useTxInfo = (
const currentAddress = walletAddresses[0];
useEffect(() => {
- if (!protocolParameters) return;
+ if (!protocolParameters || !('blockHeader' in tx)) return;
void (async () => {
const implicitCoin = Wallet.Cardano.util.computeImplicitCoin(
protocolParameters,
@@ -297,9 +356,12 @@ export const useTxInfo = (
validContract: tx.inputSource === Wallet.Cardano.InputSource.inputs,
});
const assets = amounts.filter(amount => amount.unit !== 'lovelace');
- const lovelace = BigInt(
- amounts.find(amount => amount.unit === 'lovelace')!.quantity,
- );
+ const lovelaceAsset = amounts.find(amount => amount.unit === 'lovelace');
+ const lovelace = BigInt(lovelaceAsset?.quantity ?? '');
+ const deposit =
+ Number.parseInt(implicitCoin.deposit?.toString() ?? '') > 0
+ ? BigInt(implicitCoin.deposit?.toString() ?? 0)
+ : BigInt(0);
const info: TxInfo = {
txHash: tx.id.toString(),
@@ -326,9 +388,7 @@ export const useTxInfo = (
? BigInt(lovelace.toString())
: BigInt(lovelace.toString()) +
BigInt(tx.body.fee.toString()) +
- (Number.parseInt(implicitCoin.deposit?.toString() ?? '') > 0
- ? BigInt(implicitCoin.deposit?.toString() ?? 0)
- : BigInt(0)),
+ deposit,
assets: assets
.map(asset => {
const assetInfo = assetsInfo?.get(
diff --git a/packages/nami/src/adapters/wallet.test.ts b/packages/nami/src/adapters/wallet.test.ts
index 1d83af371..46f16f234 100644
--- a/packages/nami/src/adapters/wallet.test.ts
+++ b/packages/nami/src/adapters/wallet.test.ts
@@ -1,3 +1,8 @@
+/* eslint-disable @typescript-eslint/await-thenable */
+const mockEmip3decrypt = jest.fn();
+const mockEmip3encrypt = jest.fn();
+
+import { Wallet } from '@lace/cardano';
import { renderHook } from '@testing-library/react-hooks';
import { of } from 'rxjs';
@@ -9,7 +14,6 @@ import type {
WalletManagerApi,
WalletRepositoryApi,
} from '@cardano-sdk/web-extension';
-import type { Wallet } from '@lace/cardano';
const extendedAccountPublicKey =
'ba4f80dea2632a17c99ae9d8b934abf0' +
@@ -17,9 +21,30 @@ const extendedAccountPublicKey =
'2ac1f1560a893ea7937c5bfbfdeab459' +
'b1a396f1174b9c5a673a640d01880c35';
+const rootPrivateKeyBytes =
+ 'a135ceec60dc6c5c29a9eba03b9f15c754aaa9498cec09a1f50cdf5d88c81efeef7f5b849e6d893c06e95726f6216294b487d4a782fb78d55560eba19f9971cfa22ba1cbf24d2833221af53c877421455fb91140f3ec9171e8fe0f6b7194fd461c1dd559e99a498910bcb2ff660289756e065079109483c0c9cbebc00565c22c113faf1eb8c19e9ea054b070b0bb4d73d4efe10ff8042a982a96af0c';
+
+const newRootPrivateKeyBytes =
+ 'f24e8d5a67102f9d92f93df08b2ce2be325bb813e782d9b0c3000f8d7eec1b3026eba7719d72a84a0b0b472455cc569ab23ec119dfa65008140df4d79e65983c0e85b3dd92f7bd61ff979762c71f2dd115face757cd5efc246f825d3cbdcbf566f007f62b016355459154e1b4b1d4b087742f9bd29de8b5c1f040f0f44d877652f18db0d0f4bef2b581889cd7af457f91619db894c549f594754f532';
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ KeyManagement: {
+ ...actual.Wallet.KeyManagement,
+ emip3decrypt: mockEmip3decrypt,
+ emip3encrypt: mockEmip3encrypt,
+ },
+ },
+ };
+});
+
describe('useChangePassword', () => {
const mockCreateWallet = jest.fn();
- const mockGetMnemonic = jest.fn();
const mockDeleteWallet = jest.fn();
const mockAddAccount = jest.fn();
const mockActivateWallet = jest.fn();
@@ -30,6 +55,8 @@ describe('useChangePassword', () => {
});
it('should change the password correctly for wallet with on account', async () => {
+ const currentPassword = 'newP123Ff';
+ const newPassword = 'new-password';
const mockActiveWalletId$ = of({
walletId: 'wallet1',
accountIndex: 0,
@@ -37,6 +64,9 @@ describe('useChangePassword', () => {
const mockWallets$ = of([
{
walletId: 'wallet1',
+ encryptedSecrets: {
+ rootPrivateKeyBytes,
+ },
metadata: { name: 'test-wallet' },
accounts: [
{
@@ -50,15 +80,18 @@ describe('useChangePassword', () => {
Wallet.WalletMetadata,
Wallet.AccountMetadata
>['wallets$'];
- mockGetMnemonic.mockResolvedValue(['mnemonic']);
mockCreateWallet.mockResolvedValue({
source: { wallet: { walletId: 'wallet1' } },
});
+
+ mockEmip3encrypt.mockImplementation(
+ async () => await newRootPrivateKeyBytes,
+ );
+
const { result } = renderHook(() =>
useChangePassword({
chainId: { networkId: 0, networkMagic: 0 },
createWallet: mockCreateWallet,
- getMnemonic: mockGetMnemonic,
deleteWallet: mockDeleteWallet,
updateAccountMetadata: mockUpdateAccountMetadata,
wallets$: mockWallets$,
@@ -68,16 +101,15 @@ describe('useChangePassword', () => {
}),
);
- await result.current('current-password', 'new-password');
+ await result.current(currentPassword, newPassword);
- expect(mockGetMnemonic).toHaveBeenCalledWith(
- Buffer.from('current-password'),
- );
expect(mockDeleteWallet).toHaveBeenCalledWith(false);
expect(mockCreateWallet).toHaveBeenCalledWith({
- mnemonic: ['mnemonic'],
name: 'test-wallet',
- password: 'new-password',
+ rootPrivateKeyBytes: Wallet.HexBlob.fromBytes(
+ newRootPrivateKeyBytes as unknown as Uint8Array,
+ ),
+ extendedAccountPublicKey,
});
expect(mockUpdateAccountMetadata).toHaveBeenCalled();
expect(mockAddAccount).not.toHaveBeenCalled();
@@ -89,6 +121,8 @@ describe('useChangePassword', () => {
});
it('should change the password correctly for wallet with two accounts and second account active', async () => {
+ const currentPassword = 'currentPassword';
+ const newPassword = 'new-password';
const mockActiveWalletId$ = of({
walletId: 'wallet1',
accountIndex: 1,
@@ -97,6 +131,9 @@ describe('useChangePassword', () => {
{
walletId: 'wallet1',
metadata: { name: 'test-wallet' },
+ encryptedSecrets: {
+ rootPrivateKeyBytes,
+ },
accounts: [
{
accountIndex: 0,
@@ -114,7 +151,11 @@ describe('useChangePassword', () => {
Wallet.WalletMetadata,
Wallet.AccountMetadata
>['wallets$'];
- mockGetMnemonic.mockResolvedValue(['mnemonic']);
+
+ mockEmip3encrypt.mockImplementation(
+ async () => await newRootPrivateKeyBytes,
+ );
+
mockCreateWallet.mockResolvedValue({
source: { wallet: { walletId: 'wallet1' } },
});
@@ -122,7 +163,6 @@ describe('useChangePassword', () => {
useChangePassword({
chainId: { networkId: 0, networkMagic: 0 },
createWallet: mockCreateWallet,
- getMnemonic: mockGetMnemonic,
deleteWallet: mockDeleteWallet,
updateAccountMetadata: mockUpdateAccountMetadata,
wallets$: mockWallets$,
@@ -132,16 +172,15 @@ describe('useChangePassword', () => {
}),
);
- await result.current('current-password', 'new-password');
+ await result.current(currentPassword, newPassword);
- expect(mockGetMnemonic).toHaveBeenCalledWith(
- Buffer.from('current-password'),
- );
expect(mockDeleteWallet).toHaveBeenCalledWith(false);
expect(mockCreateWallet).toHaveBeenCalledWith({
- mnemonic: ['mnemonic'],
name: 'test-wallet',
- password: 'new-password',
+ rootPrivateKeyBytes: Wallet.HexBlob.fromBytes(
+ newRootPrivateKeyBytes as unknown as Uint8Array,
+ ),
+ extendedAccountPublicKey,
});
expect(mockUpdateAccountMetadata).toHaveBeenCalledWith({
walletId: 'wallet1',
@@ -162,7 +201,6 @@ describe('useChangePassword', () => {
});
it('should throw an error if the password is wrong', async () => {
- mockGetMnemonic.mockRejectedValue(new Error('error'));
const mockActiveWalletId$ = of({
walletId: 'wallet1',
accountIndex: 0,
@@ -170,6 +208,9 @@ describe('useChangePassword', () => {
const mockWallets$ = of([
{
walletId: 'wallet1',
+ encryptedSecrets: {
+ rootPrivateKeyBytes,
+ },
metadata: { name: 'test-wallet' },
accounts: [{ accountIndex: 0, metadata: {} }],
},
@@ -178,11 +219,12 @@ describe('useChangePassword', () => {
Wallet.AccountMetadata
>['wallets$'];
+ mockEmip3decrypt.mockRejectedValue(new Error('wrong pass'));
+
const { result } = renderHook(() =>
useChangePassword({
chainId: { networkId: 0, networkMagic: 0 },
createWallet: mockCreateWallet,
- getMnemonic: mockGetMnemonic,
deleteWallet: mockDeleteWallet,
updateAccountMetadata: mockUpdateAccountMetadata,
wallets$: mockWallets$,
diff --git a/packages/nami/src/adapters/wallet.ts b/packages/nami/src/adapters/wallet.ts
index 56c62d02b..918a90c4b 100644
--- a/packages/nami/src/adapters/wallet.ts
+++ b/packages/nami/src/adapters/wallet.ts
@@ -20,7 +20,6 @@ interface ChangePasswordProps {
createWallet: (
args: Readonly,
) => Promise;
- getMnemonic: (passphrase: Uint8Array) => Promise;
activeWalletId$: Readonly;
wallets$: Observable<
AnyWallet[]
@@ -45,7 +44,6 @@ export const useChangePassword = ({
addAccount,
activateWallet,
createWallet,
- getMnemonic,
deleteWallet,
updateAccountMetadata,
wallets$,
@@ -59,22 +57,36 @@ export const useChangePassword = ({
return useCallback(
async (currentPassword: string, newPassword: string) => {
try {
- if (!wallet?.metadata?.name) {
+ if (
+ !wallet?.metadata?.name ||
+ !('encryptedSecrets' in wallet) ||
+ !('accounts' in wallet)
+ ) {
return;
}
- const mnemonic = await getMnemonic(Buffer.from(currentPassword));
+
+ const decryptedRootPrivateKeyBytes =
+ await Wallet.KeyManagement.emip3decrypt(
+ Buffer.from(wallet.encryptedSecrets.rootPrivateKeyBytes, 'hex'),
+ Buffer.from(currentPassword, 'utf8'),
+ );
+
+ const encryptedRootPrivateKeyBytes =
+ await Wallet.KeyManagement.emip3encrypt(
+ decryptedRootPrivateKeyBytes,
+ Buffer.from(newPassword, 'utf8'),
+ );
+
await deleteWallet(false);
const newWallet = await createWallet({
- mnemonic,
name: wallet.metadata.name,
- password: newPassword,
+ rootPrivateKeyBytes: Wallet.HexBlob.fromBytes(
+ encryptedRootPrivateKeyBytes,
+ ),
+ extendedAccountPublicKey: wallet.accounts[0].extendedAccountPublicKey,
});
const { walletId } = newWallet.source.wallet;
- if (!('accounts' in wallet)) {
- return;
- }
-
for await (const account of wallet.accounts) {
const { accountIndex, metadata, extendedAccountPublicKey } = account;
await (accountIndex === 0
@@ -100,7 +112,6 @@ export const useChangePassword = ({
[
chainId,
accountIndex,
- getMnemonic,
createWallet,
deleteWallet,
updateAccountMetadata,
diff --git a/packages/nami/src/api/extension/api.mock.ts b/packages/nami/src/api/extension/api.mock.ts
index 11960656b..317037b37 100644
--- a/packages/nami/src/api/extension/api.mock.ts
+++ b/packages/nami/src/api/extension/api.mock.ts
@@ -7,75 +7,25 @@ import type { HardwareDeviceInfo } from '../../ui/app/hw/types';
export * from './';
-export const createTab = fn(actualApi.createTab).mockName('createTab');
-
-export const getAccounts = fn(actualApi.getAccounts).mockName('getAccounts');
-
-export const getHwAccounts = fn(
- // eslint-disable-next-line @typescript-eslint/require-await
- async ({ device, id }: Readonly) =>
- actualApi.getHwAccounts({ device, id }),
-).mockName('getHwAccounts');
-
-export const getCurrentAccount = fn(actualApi.getCurrentAccount).mockName(
- 'getCurrentAccount',
-);
-
-export const getCurrentAccountIndex = fn(
- actualApi.getCurrentAccountIndex,
-).mockName('getCurrentAccountIndex');
-
-export const getDelegation = fn(actualApi.getDelegation).mockName(
- 'getDelegation',
-);
-
-export const getNetwork = fn(actualApi.getNetwork).mockName('getNetwork');
-
-export const getTransactions = fn(actualApi.getTransactions).mockName(
- 'getTransactions',
-);
-
-export const updateAccount = fn(actualApi.updateAccount).mockName(
- 'updateAccount',
+export const createTab: jest.Mock = fn(actualApi.createTab).mockName(
+ 'createTab',
);
-export const onAccountChange = fn(actualApi.onAccountChange).mockName(
- 'onAccountChange',
-);
-
-export const isValidAddress = fn(actualApi.isValidAddress).mockName(
+export const isValidAddress: jest.Mock = fn(actualApi.isValidAddress).mockName(
'isValidAddress',
);
-export const getUtxos = fn(actualApi.getUtxos).mockName('getUtxos');
-
-export const getAdaHandle = fn(actualApi.getAdaHandle).mockName('getAdaHandle');
-
-export const getAsset = fn(actualApi.getAsset).mockName('getAsset');
-
-export const updateTxInfo = fn(actualApi.updateTxInfo).mockName('updateTxInfo');
-
-export const setTransactions = fn(actualApi.setTransactions).mockName(
- 'setTransactions',
+export const getAdaHandle: jest.Mock = fn(actualApi.getAdaHandle).mockName(
+ 'getAdaHandle',
);
-export const setTxDetail = fn(actualApi.setTxDetail).mockName('setTxDetail');
-
-export const createHWAccounts = fn(actualApi.createHWAccounts).mockName(
- 'createHWAccounts',
-);
-
-export const initHW = fn(
+export const initHW: jest.Mock = fn(
async ({ device, id }: Readonly) => {
- return actualApi.initHW({ device, id });
+ return actualApi.initHW();
},
).mockName('initHW');
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-export const getFavoriteIcon = fn(actualApi.getFavoriteIcon).mockName(
- 'getFavoriteIcon',
-);
-
-export const extractKeyOrScriptHash = fn(
- actualApi.extractKeyOrScriptHash,
-).mockName('extractKeyOrScriptHash');
+export const getFavoriteIcon: jest.Mock = fn(
+ actualApi.getFavoriteIcon,
+).mockName('getFavoriteIcon');
diff --git a/packages/nami/src/api/extension/index.ts b/packages/nami/src/api/extension/index.ts
index bda05dd7e..8086c27c4 100644
--- a/packages/nami/src/api/extension/index.ts
+++ b/packages/nami/src/api/extension/index.ts
@@ -1,620 +1,24 @@
-/* eslint-disable unicorn/no-array-reduce */
-/* eslint-disable max-params */
+/* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
/* eslint-disable unicorn/no-null */
-/* eslint-disable functional/prefer-immutable-types */
-import Ada, { HARDENED } from '@cardano-foundation/ledgerjs-hw-app-cardano';
import {
Cardano,
+ Serialization,
ProviderError,
ProviderFailure,
- TxCBOR,
} from '@cardano-sdk/core';
import { createAvatar } from '@dicebear/avatars';
import * as style from '@dicebear/avatars-bottts-sprites';
-import TrezorConnect from '@trezor/connect-web';
-import {
- // ADA_HANDLE,
- APIError,
- DataSignError,
- ERROR,
- EVENT,
- HW,
- // LOCAL_STORAGE,
- // NODE,
- SENDER,
- STORAGE,
- TARGET,
- TxSendError,
- TxSignError,
-} from '../../config/config';
-// import { POPUP_WINDOW } from '../../config/config';
-// import { mnemonicToEntropy } from 'bip39';
-// import cryptoRandomString from 'crypto-random-string';
-import { Loader } from '../loader';
-// import { initTx } from './wallet';
-import {
- blockfrostRequest,
- // networkNameToId,
- // utxoFromJson,
- // assetsToValue,
- txToLedger,
- txToTrezor,
- // linkToSrc,
- // convertMetadataPropToString,
- // fromAssetUnit,
- // toAssetUnit,
- // Data,
-} from '../util';
+import { APIError, TxSendError } from '../../config/config';
import type { HandleProvider } from '@cardano-sdk/core';
-import type { HexBlob } from '@cardano-sdk/util';
import type { Wallet } from '@lace/cardano';
-import type { NetworkType } from 'types';
-
-// import TransportWebUSB from '@ledgerhq/hw-transport-webusb';
-// import AssetFingerprint from '@emurgo/cip14-js';
-
-export const getStorage = async key =>
- new Promise((res, rej) => {
- chrome.storage.local.get(key, result => {
- if (chrome.runtime.lastError) rej(undefined);
- res(key ? result[key] : result);
- });
- });
-export const setStorage = async item =>
- new Promise((res, rej) => {
- chrome.storage.local.set(item, () => {
- if (chrome.runtime.lastError) rej(chrome.runtime.lastError);
- res(true);
- });
- });
-
-// export const removeStorage = (item) =>
-// new Promise((res, rej) =>
-// chrome.storage.local.remove(item, () => {
-// if (chrome.runtime.lastError) rej(chrome.runtime.lastError);
-// res(true);
-// })
-// );
-
-export const encryptWithPassword = async (password, rootKeyBytes) => {
- return await Promise.resolve('');
- // await Loader.load();
- // const rootKeyHex = Buffer.from(rootKeyBytes, 'hex').toString('hex');
- // const passwordHex = Buffer.from(password).toString('hex');
- // const salt = cryptoRandomString({ length: 2 * 32 });
- // const nonce = cryptoRandomString({ length: 2 * 12 });
- // return Loader.Cardano.encrypt_with_password(
- // passwordHex,
- // salt,
- // nonce,
- // rootKeyHex
- // );
-};
-
-export const decryptWithPassword = async (password, encryptedKeyHex) => {
- return await Promise.resolve('');
- // await Loader.load();
- // const passwordHex = Buffer.from(password).toString('hex');
- // let decryptedHex;
- // try {
- // decryptedHex = Loader.Cardano.decrypt_with_password(
- // passwordHex,
- // encryptedKeyHex
- // );
- // } catch (err) {
- // throw new Error(ERROR.wrongPassword);
- // }
- // return decryptedHex;
-};
-export const getFavoriteIcon = domain => {
+export const getFavoriteIcon = (domain: string) => {
return `chrome-extension://${chrome.runtime.id}/_favicon/?pageUrl=${domain}&size=32`;
- // const result = await getStorage(STORAGE.whitelisted);
- // return result ? result : [];
-};
-
-// export const isWhitelisted = async (_origin) => {
-// const whitelisted = await getWhitelisted();
-// let access = false;
-// if (whitelisted.includes(_origin)) access = true;
-// return access;
-// };
-
-export const setWhitelisted = async origin => {
- await Promise.resolve();
- // let whitelisted = await getWhitelisted();
- // whitelisted ? whitelisted.push(origin) : (whitelisted = [origin]);
- // return await setStorage({ [STORAGE.whitelisted]: whitelisted });
-};
-
-// export const setCurrency = (currency) =>
-// setStorage({ [STORAGE.currency]: currency });
-
-export const getDelegation = async () => {
- const currentAccount = await getCurrentAccount();
- const stake = await blockfrostRequest(
- `/accounts/${currentAccount.rewardAddr}`,
- );
- if (!stake || stake.error || !stake.pool_id) return {};
- const delegation = await blockfrostRequest(
- `/pools/${stake.pool_id}/metadata`,
- );
- if (!delegation || delegation.error) return {};
- return {
- active: stake.active,
- rewards: stake.withdrawable_amount,
- homepage: delegation.homepage,
- poolId: stake.pool_id,
- ticker: delegation.ticker,
- description: delegation.description,
- name: delegation.name,
- };
-};
-
-export const getPoolMetadata = async poolId => {
- return await Promise.resolve({});
- // if (!poolId) {
- // throw new Error('poolId argument not provided');
- // }
-
- // const delegation = await blockfrostRequest(`/pools/${poolId}/metadata`);
-
- // if (delegation.error) {
- // throw new Error(delegation.message);
- // }
-
- // return {
- // ticker: delegation.ticker,
- // name: delegation.name,
- // id: poolId,
- // hex: delegation.hex,
- // };
-};
-
-// export const getBalance = async () => {
-// await Loader.load();
-// const currentAccount = await getCurrentAccount();
-// const result = await blockfrostRequest(
-// `/addresses/${currentAccount.paymentKeyHashBech32}`
-// );
-// if (result.error) {
-// if (result.status_code === 400) throw APIError.InvalidRequest;
-// else if (result.status_code === 500) throw APIError.InternalError;
-// else return Loader.Cardano.Value.new(Loader.Cardano.BigNum.from_str('0'));
-// }
-// const value = await assetsToValue(result.amount);
-// return value;
-// };
-
-// export const getBalanceExtended = async () => {
-// const currentAccount = await getCurrentAccount();
-// const result = await blockfrostRequest(
-// `/addresses/${currentAccount.paymentKeyHashBech32}/extended`
-// );
-// if (result.error) {
-// if (result.status_code === 400) throw APIError.InvalidRequest;
-// else if (result.status_code === 500) throw APIError.InternalError;
-// else return [];
-// }
-// return result.amount;
-// };
-
-// export const getFullBalance = async () => {
-// const currentAccount = await getCurrentAccount();
-// const result = await blockfrostRequest(
-// `/accounts/${currentAccount.rewardAddr}`
-// );
-// if (result.error) return '0';
-// return (
-// BigInt(result.controlled_amount) - BigInt(result.withdrawable_amount)
-// ).toString();
-// };
-
-export const getTransactions = async (paginate = 1, count = 10) => {
- const currentAccount = await getCurrentAccount();
- const result = await blockfrostRequest(
- `/addresses/${currentAccount.paymentKeyHashBech32}/transactions?page=${paginate}&order=desc&count=${count}`,
- );
- if (!result || result.error) return [];
- return result.map(tx => ({
- txHash: tx.tx_hash,
- txIndex: tx.tx_index,
- blockHeight: tx.block_height,
- }));
-};
-
-// export const getTxInfo = async (txHash) => {
-// const result = await blockfrostRequest(`/txs/${txHash}`);
-// if (!result || result.error) return null;
-// return result;
-// };
-
-// export const getBlock = async (blockHashOrNumb) => {
-// const result = await blockfrostRequest(`/blocks/${blockHashOrNumb}`);
-// if (!result || result.error) return null;
-// return result;
-// };
-
-// export const getTxUTxOs = async (txHash) => {
-// const result = await blockfrostRequest(`/txs/${txHash}/utxos`);
-// if (!result || result.error) return null;
-// return result;
-// };
-
-// export const getTxMetadata = async (txHash) => {
-// const result = await blockfrostRequest(`/txs/${txHash}/metadata`);
-// if (!result || result.error) return null;
-// return result;
-// };
-
-export const updateTxInfo = async txHash => {
- return await Promise.resolve({});
- // const currentAccount = await getCurrentAccount();
- // const network = await getNetwork();
- // let detail = await currentAccount[network.id].history.details[txHash];
- // if (typeof detail !== 'object' || Object.keys(detail).length < 4) {
- // detail = {};
- // const info = getTxInfo(txHash);
- // const uTxOs = getTxUTxOs(txHash);
- // const metadata = getTxMetadata(txHash);
- // detail.info = await info;
- // if (info) detail.block = await getBlock(detail.info.block_height);
- // detail.utxos = await uTxOs;
- // detail.metadata = await metadata;
-};
-
-// return detail;
-// };
-
-export const setTxDetail = async txObject => {
- return await Promise.resolve(true);
- // const currentIndex = await getCurrentAccountIndex();
- // const network = await getNetwork();
- // const accounts = await getStorage(STORAGE.accounts);
- // for (const txHash of Object.keys(txObject)) {
- // const txDetail = txObject[txHash];
- // accounts[currentIndex][network.id].history.details[txHash] = txDetail;
- // await setStorage({
- // [STORAGE.accounts]: {
- // ...accounts,
- // },
- // });
- // delete txObject[txHash];
- // }
- // return true;
};
-export const getSpecificUtxo = async (txHash, txId) => {
- const result = await blockfrostRequest(`/txs/${txHash}/utxos`);
- if (!result || result.error) return null;
- return result.outputs[txId];
-};
-
-/**
- *
- * @param {string} amount - cbor value
- * @param {Object} paginate
- * @param {number} paginate.page
- * @param {number} paginate.limit
- * @returns
- */
-export const getUtxos = async (amount = undefined, paginate = undefined) => {
- return await Promise.resolve([]);
- // const currentAccount = await getCurrentAccount();
- // let result = [];
- // let page = paginate && paginate.page ? paginate.page + 1 : 1;
- // const limit = paginate && paginate.limit ? `&count=${paginate.limit}` : '';
- // while (true) {
- // let pageResult = await blockfrostRequest(
- // `/addresses/${currentAccount.paymentKeyHashBech32}/utxos?page=${page}${limit}`
- // );
- // if (pageResult.error) {
- // if (result.status_code === 400) throw APIError.InvalidRequest;
- // else if (result.status_code === 500) throw APIError.InternalError;
- // else {
- // pageResult = [];
- // }
- // }
- // result = result.concat(pageResult);
- // if (pageResult.length <= 0 || paginate) break;
- // page++;
- // }
-
- // // exclude collateral input from overall utxo set
- // if (currentAccount.collateral) {
- // result = result.filter(
- // (utxo) =>
- // !(
- // utxo.tx_hash === currentAccount.collateral.txHash &&
- // utxo.output_index === currentAccount.collateral.txId
- // )
- // );
- // }
-
- // const address = await getAddress();
- // let converted = await Promise.all(
- // result.map(async (utxo) => await utxoFromJson(utxo, address))
- // );
- // // filter utxos
- // if (amount) {
- // await Loader.load();
- // let filterValue;
- // try {
- // filterValue = Loader.Cardano.Value.from_bytes(Buffer.from(amount, 'hex'));
- // } catch (e) {
- // throw APIError.InvalidRequest;
- // }
-
- // converted = converted.filter(
- // (unspent) =>
- // !unspent.output().amount().compare(filterValue) ||
- // unspent.output().amount().compare(filterValue) !== -1
- // );
- // }
- // if ((amount || paginate) && converted.length <= 0) {
- // return null;
- // }
- // return converted;
-};
-
-// const checkCollateral = async (currentAccount, network, checkTx) => {
-// if (checkTx) {
-// const transactions = await getTransactions();
-// if (
-// transactions.length <= 0 ||
-// currentAccount[network.id].history.confirmed.includes(
-// transactions[0].txHash
-// )
-// )
-// return;
-// }
-// let result = [];
-// let page = 1;
-// while (true) {
-// let pageResult = await blockfrostRequest(
-// `/addresses/${currentAccount.paymentKeyHashBech32}/utxos?page=${page}`
-// );
-// if (pageResult.error) {
-// if (result.status_code === 400) throw APIError.InvalidRequest;
-// else if (result.status_code === 500) throw APIError.InternalError;
-// else {
-// pageResult = [];
-// }
-// }
-// result = result.concat(pageResult);
-// if (pageResult.length <= 0) break;
-// page++;
-// }
-
-// // exclude collateral input from overall utxo set
-// if (currentAccount[network.id].collateral) {
-// const initialSize = result.length;
-// result = result.filter(
-// (utxo) =>
-// !(
-// utxo.tx_hash === currentAccount[network.id].collateral.txHash &&
-// utxo.output_index === currentAccount[network.id].collateral.txId
-// )
-// );
-
-// if (initialSize == result.length) {
-// delete currentAccount[network.id].collateral;
-// return true;
-// }
-// }
-// };
-
-// export const getCollateral = async () => {
-// await Loader.load();
-// const currentIndex = await getCurrentAccountIndex();
-// const accounts = await getStorage(STORAGE.accounts);
-// const currentAccount = accounts[currentIndex];
-// const network = await getNetwork();
-// if (await checkCollateral(currentAccount, network, true)) {
-// await setStorage({ [STORAGE.accounts]: accounts });
-// }
-// const collateral = currentAccount[network.id].collateral;
-// if (collateral) {
-// const collateralUtxo = Loader.Cardano.TransactionUnspentOutput.new(
-// Loader.Cardano.TransactionInput.new(
-// Loader.Cardano.TransactionHash.from_bytes(
-// Buffer.from(collateral.txHash, 'hex')
-// ),
-// Loader.Cardano.BigNum.from_str(collateral.txId.toString())
-// ),
-// Loader.Cardano.TransactionOutput.new(
-// Cardano.Address.fromBech32(
-// currentAccount[network.id].paymentAddr
-// ),
-// Loader.Cardano.Value.new(
-// Loader.Cardano.BigNum.from_str(collateral.lovelace)
-// )
-// )
-// );
-// return [collateralUtxo];
-// }
-// const utxos = await getUtxos();
-// return utxos.filter(
-// (utxo) =>
-// utxo
-// .output()
-// .amount()
-// .coin()
-// .compare(Loader.Cardano.BigNum.from_str('50000000')) <= 0 &&
-// !utxo.output().amount().multiasset()
-// );
-// };
-
-export const getAddress = async () => {
- const currentAccount = await getCurrentAccount();
- const paymentAddr = Buffer.from(
- Cardano.Address.fromBech32(currentAccount.paymentAddr).toBytes(),
- 'hex',
- ).toString('hex');
- return paymentAddr;
-};
-
-// export const getRewardAddress = async () => {
-// await Loader.load();
-// const currentAccount = await getCurrentAccount();
-// const rewardAddr = Buffer.from(
-// Cardano.Address.fromBech32(currentAccount.rewardAddr).to_bytes(),
-// 'hex'
-// ).toString('hex');
-// return rewardAddr;
-// };
-
-export const getCurrentAccountIndex = async () =>
- getStorage(STORAGE.currentAccount);
-
-export const getNetwork = async (): Promise<{
- id: string;
- name: NetworkType;
- node: string;
-}> =>
- getStorage(STORAGE.network) as Promise<{
- id: string;
- name: NetworkType;
- node: string;
- }>;
-
-// export const setNetwork = async (network) => {
-// const currentNetwork = await getNetwork();
-// let id;
-// let node;
-// if (network.id === NETWORK_ID.mainnet) {
-// id = NETWORK_ID.mainnet;
-// node = NODE.mainnet;
-// } else if (network.id === NETWORK_ID.testnet) {
-// id = NETWORK_ID.testnet;
-// node = NODE.testnet;
-// } else if (network.id === NETWORK_ID.preview) {
-// id = NETWORK_ID.preview;
-// node = NODE.preview;
-// } else {
-// id = NETWORK_ID.preprod;
-// node = NODE.preprod;
-// }
-// if (network.node) node = network.node;
-// if (currentNetwork && currentNetwork.id !== id)
-// emitNetworkChange(networkNameToId(id));
-// await setStorage({
-// [STORAGE.network]: {
-// id,
-// node,
-// mainnetSubmit: network.mainnetSubmit,
-// testnetSubmit: network.testnetSubmit,
-// },
-// });
-// return true;
-// };
-
-const accountToNetworkSpecific = (account, network) => {
- const assets = account[network.id].assets;
- const lovelace = account[network.id].lovelace;
- const history = account[network.id].history;
- const minAda = account[network.id].minAda;
- const collateral = account[network.id].collateral;
- const recentSendToAddresses = account[network.id].recentSendToAddresses;
- const paymentAddr = account[network.id].paymentAddr;
- const rewardAddr = account[network.id].rewardAddr;
-
- return {
- ...account,
- paymentAddr,
- rewardAddr,
- assets,
- lovelace,
- minAda,
- collateral,
- history,
- recentSendToAddresses,
- };
-};
-
-// /** Returns account with network specific settings (e.g. address, reward address, etc.) */
-export const getCurrentAccount = async () => {
- return await Promise.resolve({});
- // const currentAccountIndex = await getCurrentAccountIndex();
- // const accounts = await getStorage(STORAGE.accounts);
- // const network = await getNetwork();
- // if (network && accounts) {
- // return accountToNetworkSpecific(accounts[currentAccountIndex], network);
- // }
-
- // return {};
-};
-
-// /** Returns accounts with network specific settings (e.g. address, reward address, etc.) */
-export const getAccounts = async () => {
- return await Promise.resolve([]);
- // const accounts = await getStorage(STORAGE.accounts);
- // const network = await getNetwork();
- // for (const index in accounts) {
- // accounts[index] = await accountToNetworkSpecific(accounts[index], network);
- // }
- // return accounts;
-};
-
-// export const createPopup = async (popup) => {
-// let left = 0;
-// let top = 0;
-// try {
-// const lastFocused = await new Promise((res, rej) => {
-// chrome.windows.getLastFocused((windowObject) => {
-// return res(windowObject);
-// });
-// });
-// top = lastFocused.top;
-// left =
-// lastFocused.left +
-// Math.round((lastFocused.width - POPUP_WINDOW.width) / 2);
-// } catch (_) {
-// // The following properties are more than likely 0, due to being
-// // opened from the background chrome process for the extension that
-// // has no physical dimensions
-// const { screenX, screenY, outerWidth } = window;
-// top = Math.max(screenY, 0);
-// left = Math.max(screenX + (outerWidth - POPUP_WINDOW.width), 0);
-// }
-
-// const { popupWindow, tab } = await new Promise((res, rej) =>
-// chrome.tabs.create(
-// {
-// url: chrome.runtime.getURL(popup + '.html'),
-// active: false,
-// },
-// function (tab) {
-// chrome.windows.create(
-// {
-// tabId: tab.id,
-// type: 'popup',
-// focused: true,
-// ...POPUP_WINDOW,
-// left,
-// top,
-// },
-// function (newWindow) {
-// return res({ popupWindow: newWindow, tab });
-// }
-// );
-// }
-// )
-// );
-
-// if (popupWindow.left !== left && popupWindow.state !== 'fullscreen') {
-// await new Promise((res, rej) => {
-// chrome.windows.update(popupWindow.id, { left, top }, () => {
-// return res();
-// });
-// });
-// }
-// return tab;
-// };
-
-export const createTab = async (tab, query = '') =>
+export const createTab = async (tab: string, query = '') =>
new Promise((res, rej) => {
chrome.tabs.create(
{
@@ -635,35 +39,9 @@ export const createTab = async (tab, query = '') =>
);
});
-// export const getCurrentWebpage = () =>
-// new Promise((res, rej) => {
-// chrome.tabs.query(
-// {
-// active: true,
-// lastFocusedWindow: true,
-// status: 'complete',
-// windowType: 'normal',
-// },
-// function (tabs) {
-// res({
-// url: new URL(tabs[0].url).origin,
-// favicon: tabs[0].favIconUrl,
-// tabId: tabs[0].id,
-// });
-// }
-// );
-// });
-
-const harden = (num: number) => {
- return 0x80_00_00_00 + num;
-};
-
-export const bytesAddressToBinary = bytes =>
- bytes.reduce((str, byte) => `${str}${byte.toString(2).padStart(8, '0')}`, '');
-
export const isValidAddress = (
address: string,
- currentChain: Wallet.Cardano.ChainId,
+ currentChain: Readonly,
) => {
try {
const addr = Cardano.Address.fromBech32(address);
@@ -695,478 +73,17 @@ export const isValidAddress = (
return false;
};
-const isValidAddressBytes = (
- address: HexBlob,
- currentChain: Wallet.Cardano.ChainId,
-) => {
- try {
- const addr = Cardano.Address.fromBytes(address);
- if (
- (addr.getNetworkId() === Cardano.NetworkId.Mainnet &&
- currentChain.networkMagic === Cardano.NetworkMagics.Mainnet) ||
- (addr.getNetworkId() === Cardano.NetworkId.Testnet &&
- (currentChain.networkMagic === Cardano.NetworkMagics.Preview ||
- currentChain.networkMagic === Cardano.NetworkMagics.Preprod))
- )
- return true;
- return false;
- } catch {}
- try {
- const addr = Cardano.ByronAddress.fromAddress(
- Cardano.Address.fromBase58(address),
- )?.toAddress();
- if (
- (addr?.getNetworkId() === Cardano.NetworkId.Mainnet &&
- currentChain.networkMagic === Cardano.NetworkMagics.Mainnet) ||
- (addr?.getNetworkId() === Cardano.NetworkId.Testnet &&
- (currentChain.networkMagic === Cardano.NetworkMagics.Preview ||
- currentChain.networkMagic === Cardano.NetworkMagics.Preprod))
- )
- return true;
- return false;
- } catch {}
- return false;
-};
-
-// export const extractKeyHash = async (address) => {
-// await Loader.load();
-// if (!(await isValidAddressBytes(Buffer.from(address, 'hex'))))
-// throw DataSignError.InvalidFormat;
-// try {
-// const addr = Loader.Cardano.BaseAddress.from_address(
-// Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex'))
-// );
-// return addr.payment_cred().to_keyhash().to_bech32('addr_vkh');
-// } catch (e) {}
-// try {
-// const addr = Loader.Cardano.EnterpriseAddress.from_address(
-// Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex'))
-// );
-// return addr.payment_cred().to_keyhash().to_bech32('addr_vkh');
-// } catch (e) {}
-// try {
-// const addr = Loader.Cardano.PointerAddress.from_address(
-// Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex'))
-// );
-// return addr.payment_cred().to_keyhash().to_bech32('addr_vkh');
-// } catch (e) {}
-// try {
-// const addr = Loader.Cardano.RewardAddress.from_address(
-// Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex'))
-// );
-// return addr.payment_cred().to_keyhash().to_bech32('stake_vkh');
-// } catch (e) {}
-// throw DataSignError.AddressNotPK;
-// };
-
-export const extractKeyOrScriptHash = async address => {
- await Loader.load();
- if (!isValidAddressBytes(Buffer.from(address, 'hex')))
- throw DataSignError.InvalidFormat;
- try {
- const addr = Loader.Cardano.BaseAddress.from_address(
- Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')),
- );
-
- const credential = addr.payment_cred();
- if (credential.kind() === 0)
- return credential.to_keyhash().to_bech32('addr_vkh');
- if (credential.kind() === 1)
- return credential.to_scripthash().to_bech32('script');
- } catch {}
- try {
- const addr = Loader.Cardano.EnterpriseAddress.from_address(
- Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')),
- );
- const credential = addr.payment_cred();
- if (credential.kind() === 0)
- return credential.to_keyhash().to_bech32('addr_vkh');
- if (credential.kind() === 1)
- return credential.to_scripthash().to_bech32('script');
- } catch {}
- try {
- const addr = Loader.Cardano.PointerAddress.from_address(
- Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')),
- );
- const credential = addr.payment_cred();
- if (credential.kind() === 0)
- return credential.to_keyhash().to_bech32('addr_vkh');
- if (credential.kind() === 1)
- return credential.to_scripthash().to_bech32('script');
- } catch {}
- try {
- const addr = Loader.Cardano.RewardAddress.from_address(
- Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')),
- );
- const credential = addr.payment_cred();
- if (credential.kind() === 0)
- return credential.to_keyhash().to_bech32('stake_vkh');
- if (credential.kind() === 1)
- return credential.to_scripthash().to_bech32('script');
- } catch {}
- throw new Error('No address type matched.');
-};
-
-// export const verifySigStructure = async (sigStructure) => {
-// await Loader.load();
-// try {
-// Loader.Message.SigStructure.from_bytes(Buffer.from(sigStructure, 'hex'));
-// } catch (e) {
-// throw DataSignError.InvalidFormat;
-// }
-// };
-
-// export const verifyPayload = (payload) => {
-// if (Buffer.from(payload, 'hex').length <= 0)
-// throw DataSignError.InvalidFormat;
-// };
-
-// export const verifyTx = async (tx) => {
-// await Loader.load();
-// const network = await getNetwork();
-// try {
-// const parseTx = Loader.Cardano.Transaction.from_bytes(
-// Buffer.from(tx, 'hex')
-// );
-// let networkId = parseTx.body().network_id()
-// ? parseTx.body().network_id().kind()
-// : null;
-// if (!networkId && networkId != 0) {
-// networkId = parseTx.body().outputs().get(0).address().network_id();
-// }
-// if (networkId != networkNameToId(network.id)) throw Error('Wrong network');
-// } catch (e) {
-// throw APIError.InvalidRequest;
-// }
-// };
-
-// /**
-// * @param {string} address - cbor
-// * @param {string} payload - hex encoded utf8 string
-// * @param {string} password
-// * @param {number} accountIndex
-// * @returns
-// */
-
-// //deprecated soon
-// export const signData = async (address, payload, password, accountIndex) => {
-// await Loader.load();
-// const keyHash = await extractKeyHash(address);
-// const prefix = keyHash.startsWith('addr_vkh') ? 'addr_vkh' : 'stake_vkh';
-// let { paymentKey, stakeKey } = await requestAccountKey(
-// password,
-// accountIndex
-// );
-// const accountKey = prefix === 'addr_vkh' ? paymentKey : stakeKey;
-
-// const publicKey = accountKey.to_public();
-// if (keyHash !== publicKey.hash().to_bech32(prefix))
-// throw DataSignError.ProofGeneration;
-
-// const protectedHeaders = Loader.Message.HeaderMap.new();
-// protectedHeaders.set_algorithm_id(
-// Loader.Message.Label.from_algorithm_id(Loader.Message.AlgorithmId.EdDSA)
-// );
-// protectedHeaders.set_key_id(publicKey.as_bytes());
-// protectedHeaders.set_header(
-// Loader.Message.Label.new_text('address'),
-// Loader.Message.CBORValue.new_bytes(Buffer.from(address, 'hex'))
-// );
-// const protectedSerialized =
-// Loader.Message.ProtectedHeaderMap.new(protectedHeaders);
-// const unprotectedHeaders = Loader.Message.HeaderMap.new();
-// const headers = Loader.Message.Headers.new(
-// protectedSerialized,
-// unprotectedHeaders
-// );
-// const builder = Loader.Message.COSESign1Builder.new(
-// headers,
-// Buffer.from(payload, 'hex'),
-// false
-// );
-// const toSign = builder.make_data_to_sign().to_bytes();
-
-// const signedSigStruc = accountKey.sign(toSign).to_bytes();
-// const coseSign1 = builder.build(signedSigStruc);
-
-// stakeKey.free();
-// stakeKey = null;
-// paymentKey.free();
-// paymentKey = null;
-
-// return Buffer.from(coseSign1.to_bytes(), 'hex').toString('hex');
-// };
-
-// export const signDataCIP30 = async (
-// address,
-// payload,
-// password,
-// accountIndex
-// ) => {
-// await Loader.load();
-// const keyHash = await extractKeyHash(address);
-// const prefix = keyHash.startsWith('addr_vkh') ? 'addr_vkh' : 'stake_vkh';
-// let { paymentKey, stakeKey } = await requestAccountKey(
-// password,
-// accountIndex
-// );
-// const accountKey = prefix === 'addr_vkh' ? paymentKey : stakeKey;
-
-// const publicKey = accountKey.to_public();
-// if (keyHash !== publicKey.hash().to_bech32(prefix))
-// throw DataSignError.ProofGeneration;
-// const protectedHeaders = Loader.Message.HeaderMap.new();
-// protectedHeaders.set_algorithm_id(
-// Loader.Message.Label.from_algorithm_id(Loader.Message.AlgorithmId.EdDSA)
-// );
-// // protectedHeaders.set_key_id(publicKey.as_bytes()); // Removed to adhere to CIP-30
-// protectedHeaders.set_header(
-// Loader.Message.Label.new_text('address'),
-// Loader.Message.CBORValue.new_bytes(Buffer.from(address, 'hex'))
-// );
-// const protectedSerialized =
-// Loader.Message.ProtectedHeaderMap.new(protectedHeaders);
-// const unprotectedHeaders = Loader.Message.HeaderMap.new();
-// const headers = Loader.Message.Headers.new(
-// protectedSerialized,
-// unprotectedHeaders
-// );
-// const builder = Loader.Message.COSESign1Builder.new(
-// headers,
-// Buffer.from(payload, 'hex'),
-// false
-// );
-// const toSign = builder.make_data_to_sign().to_bytes();
-
-// const signedSigStruc = accountKey.sign(toSign).to_bytes();
-// const coseSign1 = builder.build(signedSigStruc);
-
-// stakeKey.free();
-// stakeKey = null;
-// paymentKey.free();
-// paymentKey = null;
-
-// const key = Loader.Message.COSEKey.new(
-// Loader.Message.Label.from_key_type(Loader.Message.KeyType.OKP)
-// );
-// key.set_algorithm_id(
-// Loader.Message.Label.from_algorithm_id(Loader.Message.AlgorithmId.EdDSA)
-// );
-// key.set_header(
-// Loader.Message.Label.new_int(
-// Loader.Message.Int.new_negative(Loader.Message.BigNum.from_str('1'))
-// ),
-// Loader.Message.CBORValue.new_int(
-// Loader.Message.Int.new_i32(6) //Loader.Message.CurveType.Ed25519
-// )
-// ); // crv (-1) set to Ed25519 (6)
-// key.set_header(
-// Loader.Message.Label.new_int(
-// Loader.Message.Int.new_negative(Loader.Message.BigNum.from_str('2'))
-// ),
-// Loader.Message.CBORValue.new_bytes(publicKey.as_bytes())
-// ); // x (-2) set to public key
-
-// return {
-// signature: Buffer.from(coseSign1.to_bytes()).toString('hex'),
-// key: Buffer.from(key.to_bytes()).toString('hex'),
-// };
-// };
-
-export const signTx = async (
- tx: string,
- keyHashes: string[],
- password: string,
- accountIndex: number,
- partialSign = false,
-) => {
- let { paymentKey, stakeKey } = await requestAccountKey(
- password,
- accountIndex,
- );
- const paymentKeyHash = Buffer.from(
- paymentKey.to_public().hash().to_bytes(),
- 'hex',
- ).toString('hex');
- const stakeKeyHash = Buffer.from(
- stakeKey.to_public().hash().to_bytes(),
- 'hex',
- ).toString('hex');
-
- const rawTx = Loader.Cardano.Transaction.from_bytes(Buffer.from(tx, 'hex'));
-
- const txWitnessSet = Loader.Cardano.TransactionWitnessSet.new();
- const vkeyWitnesses = Loader.Cardano.Vkeywitnesses.new();
- const txHash = Loader.Cardano.hash_transaction(rawTx.body());
- for (const keyHash of keyHashes) {
- let signingKey;
- if (keyHash === paymentKeyHash) signingKey = paymentKey;
- else if (keyHash === stakeKeyHash) signingKey = stakeKey;
- else if (partialSign) {
- continue;
- } else {
- throw TxSignError.ProofGeneration;
- }
- const vkey = Loader.Cardano.make_vkey_witness(txHash, signingKey);
- vkeyWitnesses.add(vkey);
- }
-
- stakeKey.free();
- stakeKey = null;
- paymentKey.free();
- paymentKey = null;
-
- txWitnessSet.set_vkeys(vkeyWitnesses);
- return txWitnessSet;
-};
-
-export const signTxHW = async (
- tx,
- keyHashes,
- account,
- hw,
- partialSign = false,
-) => {
- const rawTx = Loader.Cardano.Transaction.from_bytes(Buffer.from(tx, 'hex'));
- const address = Cardano.Address.fromBech32(account.paymentAddr);
- const network = address.getNetworkId();
- const keys = {
- payment: { hash: null, path: null },
- stake: { hash: null, path: null },
- };
- if (hw.device === HW.ledger) {
- const appAda = hw.appAda;
- for (const keyHash of keyHashes) {
- if (keyHash === account.paymentKeyHash)
- keys.payment = {
- hash: keyHash,
- path: [
- HARDENED + 1852,
- HARDENED + 1815,
- HARDENED + (hw.account as number),
- 0,
- 0,
- ],
- };
- else if (keyHash === account.stakeKeyHash)
- keys.stake = {
- hash: keyHash,
- path: [
- HARDENED + 1852,
- HARDENED + 1815,
- HARDENED + (hw.account as number),
- 2,
- 0,
- ],
- };
- else if (partialSign) {
- continue;
- } else {
- throw TxSignError.ProofGeneration;
- }
- }
- const ledgerTx = await txToLedger(
- rawTx,
- network,
- keys,
- Buffer.from(address.toBytes()).toString('hex'),
- hw.account,
- );
- const result = await appAda.signTransaction(ledgerTx);
- // getting public keys
- const witnessSet = Loader.Cardano.TransactionWitnessSet.new();
- const vkeys = Loader.Cardano.Vkeywitnesses.new();
- for (const witness of result.witnesses) {
- if (
- witness.path[3] == 0 // payment key
- ) {
- const vkey = Loader.Cardano.Vkey.new(
- Loader.Cardano.Bip32PublicKey.from_bytes(
- Buffer.from(account.publicKey, 'hex'),
- )
- .derive(0)
- .derive(0)
- .to_raw_key(),
- );
- const signature = Loader.Cardano.Ed25519Signature.from_hex(
- witness.witnessSignatureHex,
- );
- vkeys.add(Loader.Cardano.Vkeywitness.new(vkey, signature));
- } else if (
- witness.path[3] == 2 // stake key
- ) {
- const vkey = Loader.Cardano.Vkey.new(
- Loader.Cardano.Bip32PublicKey.from_bytes(
- Buffer.from(account.publicKey, 'hex'),
- )
- .derive(2)
- .derive(0)
- .to_raw_key(),
- );
- const signature = Loader.Cardano.Ed25519Signature.from_hex(
- witness.witnessSignatureHex,
- );
- vkeys.add(Loader.Cardano.Vkeywitness.new(vkey, signature));
- }
- }
- witnessSet.set_vkeys(vkeys);
- return witnessSet;
- } else {
- for (const keyHash of keyHashes) {
- if (keyHash === account.paymentKeyHash)
- keys.payment = {
- hash: keyHash,
- path: `m/1852'/1815'/${hw.account}'/0/0`,
- };
- else if (keyHash === account.stakeKeyHash)
- keys.stake = {
- hash: keyHash,
- path: `m/1852'/1815'/${hw.account}'/2/0`,
- };
- else if (partialSign) {
- continue;
- } else {
- throw TxSignError.ProofGeneration;
- }
- }
- const trezorTx = await txToTrezor(
- rawTx,
- network,
- keys,
- Buffer.from(address.toBytes()).toString('hex'),
- hw.account,
- );
- const result = await TrezorConnect.cardanoSignTransaction(trezorTx);
- if (!result.success) throw new Error('Trezor could not sign tx');
- // getting public keys
- const witnessSet = Loader.Cardano.TransactionWitnessSet.new();
- const vkeys = Loader.Cardano.Vkeywitnesses.new();
- for (const witness of result.payload.witnesses) {
- const vkey = Loader.Cardano.Vkey.new(
- Loader.Cardano.PublicKey.from_bytes(Buffer.from(witness.pubKey, 'hex')),
- );
- const signature = Loader.Cardano.Ed25519Signature.from_hex(
- witness.signature,
- );
- vkeys.add(Loader.Cardano.Vkeywitness.new(vkey, signature));
- }
- witnessSet.set_vkeys(vkeys);
- return witnessSet;
- }
-};
-
// /**
// *
// * @param {string} tx - cbor hex string
// * @returns
// */
-
export const submitTx = async (
tx: string,
inMemoryWallet: Wallet.ObservableWallet,
): Promise => {
try {
- const result = await inMemoryWallet.submitTx(TxCBOR(tx));
+ const result = await inMemoryWallet.submitTx(Serialization.TxCBOR(tx));
return result;
} catch (error) {
if (
@@ -1179,239 +96,8 @@ export const submitTx = async (
}
};
-// const emitNetworkChange = async (networkId) => {
-// //to webpage
-// chrome.tabs.query({}, (tabs) => {
-// tabs.forEach((tab) =>
-// chrome.tabs.sendMessage(tab.id, {
-// data: networkId,
-// target: TARGET,
-// sender: SENDER.extension,
-// event: EVENT.networkChange,
-// })
-// );
-// });
-// };
-
-// const emitAccountChange = async (addresses) => {
-// //to extenstion itself
-// if (typeof window !== 'undefined') {
-// window.postMessage({
-// data: addresses,
-// target: TARGET,
-// sender: SENDER.extension,
-// event: EVENT.accountChange,
-// });
-// }
-// //to webpage
-// chrome.tabs.query({}, (tabs) => {
-// tabs.forEach((tab) =>
-// chrome.tabs.sendMessage(tab.id, {
-// data: addresses,
-// target: TARGET,
-// sender: SENDER.extension,
-// event: EVENT.accountChange,
-// })
-// );
-// });
-// };
-
-export const onAccountChange = callback => {
- const responseHandler = e => {
- const response = e.data;
- if (
- typeof response !== 'object' ||
- response === null ||
- !response.target ||
- response.target !== TARGET ||
- !response.event ||
- response.event !== EVENT.accountChange ||
- !response.sender ||
- response.sender !== SENDER.extension
- )
- return;
- callback(response.data);
- };
- window.addEventListener('message', responseHandler);
- return {
- remove: () => {
- window.removeEventListener('message', responseHandler);
- },
- };
-};
-
-export const requestAccountKey = async (password, accountIndex) => {
- await Loader.load();
- const encryptedRootKey = await getStorage(STORAGE.encryptedKey);
- let accountKey;
- try {
- accountKey = Loader.Cardano.Bip32PrivateKey.from_bytes(
- Buffer.from(await decryptWithPassword(password, encryptedRootKey), 'hex'),
- )
- .derive(harden(1852)) // purpose
- .derive(harden(1815)) // coin type;
- .derive(harden(Number.parseInt(accountIndex)));
- } catch {
- throw ERROR.wrongPassword;
- }
-
- return {
- accountKey,
- paymentKey: accountKey.derive(0).derive(0).to_raw_key(),
- stakeKey: accountKey.derive(2).derive(0).to_raw_key(),
- };
-};
-
-export const createHWAccounts = async accounts => {
- await Loader.load();
- const existingAccounts = await getStorage(STORAGE.accounts);
- for (const account of accounts) {
- const publicKey = Loader.Cardano.Bip32PublicKey.from_bytes(
- Buffer.from(account.publicKey, 'hex'),
- );
-
- const paymentKeyHashRaw = publicKey.derive(0).derive(0).to_raw_key().hash();
- const stakeKeyHashRaw = publicKey.derive(2).derive(0).to_raw_key().hash();
-
- const paymentKeyHash = Buffer.from(paymentKeyHashRaw.to_bytes()).toString(
- 'hex',
- );
-
- const paymentKeyHashBech32 = paymentKeyHashRaw.to_bech32('addr_vkh');
-
- const stakeKeyHash = Buffer.from(stakeKeyHashRaw.to_bytes()).toString(
- 'hex',
- );
-
- const paymentAddrMainnet = Loader.Cardano.BaseAddress.new(
- Loader.Cardano.NetworkInfo.mainnet().network_id(),
- Loader.Cardano.StakeCredential.from_keyhash(paymentKeyHashRaw),
- Loader.Cardano.StakeCredential.from_keyhash(stakeKeyHashRaw),
- )
- .to_address()
- .to_bech32();
-
- const rewardAddrMainnet = Loader.Cardano.RewardAddress.new(
- Loader.Cardano.NetworkInfo.mainnet().network_id(),
- Loader.Cardano.StakeCredential.from_keyhash(stakeKeyHashRaw),
- )
- .to_address()
- .to_bech32();
-
- const paymentAddrTestnet = Loader.Cardano.BaseAddress.new(
- Loader.Cardano.NetworkInfo.testnet().network_id(),
- Loader.Cardano.StakeCredential.from_keyhash(paymentKeyHashRaw),
- Loader.Cardano.StakeCredential.from_keyhash(stakeKeyHashRaw),
- )
- .to_address()
- .to_bech32();
-
- const rewardAddrTestnet = Loader.Cardano.RewardAddress.new(
- Loader.Cardano.NetworkInfo.testnet().network_id(),
- Loader.Cardano.StakeCredential.from_keyhash(stakeKeyHashRaw),
- )
- .to_address()
- .to_bech32();
-
- const index = account.accountIndex;
- const name = account.name;
-
- const networkDefault = {
- lovelace: null,
- minAda: 0,
- assets: [],
- history: { confirmed: [], details: {} },
- };
-
- existingAccounts[index] = {
- index,
- publicKey: Buffer.from(publicKey.as_bytes()).toString('hex'),
- paymentKeyHash,
- paymentKeyHashBech32,
- stakeKeyHash,
- name,
- [NETWORK_ID.mainnet]: {
- ...networkDefault,
- paymentAddr: paymentAddrMainnet,
- rewardAddr: rewardAddrMainnet,
- },
- [NETWORK_ID.testnet]: {
- ...networkDefault,
- paymentAddr: paymentAddrTestnet,
- rewardAddr: rewardAddrTestnet,
- },
- [NETWORK_ID.preview]: {
- ...networkDefault,
- paymentAddr: paymentAddrTestnet,
- rewardAddr: rewardAddrTestnet,
- },
- [NETWORK_ID.preprod]: {
- ...networkDefault,
- paymentAddr: paymentAddrTestnet,
- rewardAddr: rewardAddrTestnet,
- },
- avatar: Math.random().toString(),
- };
- }
- await setStorage({
- [STORAGE.accounts]: existingAccounts,
- });
-};
-
-export const indexToHw = accountIndex => ({
- device: accountIndex.split('-')[0],
- id: accountIndex.split('-')[1],
- account: Number.parseInt(accountIndex.split('-')[2]),
-});
-
-export const getHwAccounts = async ({ device, id }) => {
- const accounts = await getStorage(STORAGE.accounts);
- const hwAccounts = {};
- for (const accountIndex of Object.keys(accounts).filter(
- accountIndex =>
- isHW(accountIndex) &&
- indexToHw(accountIndex).device == device &&
- indexToHw(accountIndex).id == id,
- ))
- hwAccounts[accountIndex] = accounts[accountIndex];
- return hwAccounts;
-};
-
-export const isHW = accountIndex =>
- accountIndex != undefined &&
- accountIndex != 0 &&
- typeof accountIndex !== 'number' &&
- (accountIndex.startsWith(HW.trezor) || accountIndex.startsWith(HW.ledger));
-
-export const initHW = async ({ device, id }) => {
+export const initHW = async () => {
return await Promise.resolve({});
- // if (device == HW.ledger) {
- // const foundDevice = await new Promise((res, rej) =>
- // navigator.usb
- // .getDevices()
- // .then((devices) =>
- // res(
- // devices.find(
- // (device) =>
- // device.productId == id && device.manufacturerName === 'Ledger'
- // )
- // )
- // )
- // );
- // const transport = await TransportWebUSB.open(foundDevice);
- // const appAda = new Ada(transport);
- // await appAda.getVersion(); // check if Ledger has Cardano app opened
- // return appAda;
- // } else if (device == HW.trezor) {
- // try {
- // await TrezorConnect.init({
- // manifest: {
- // email: 'namiwallet.cardano@gmail.com',
- // appUrl: 'http://namiwallet.io',
- // },
- // });
- // } catch (e) {}
- // }
};
// /**
@@ -1436,89 +122,7 @@ export const getAdaHandle = async (
}
};
-// export const createWallet = async (name, seedPhrase, password) => {
-// await Loader.load();
-
-// let entropy = mnemonicToEntropy(seedPhrase);
-// let rootKey = Loader.Cardano.Bip32PrivateKey.from_bip39_entropy(
-// Buffer.from(entropy, 'hex'),
-// Buffer.from('')
-// );
-// entropy = null;
-// seedPhrase = null;
-
-// const encryptedRootKey = await encryptWithPassword(
-// password,
-// rootKey.as_bytes()
-// );
-// rootKey.free();
-// rootKey = null;
-
-// const checkStore = await getStorage(STORAGE.encryptedKey);
-// if (checkStore) throw new Error(ERROR.storeNotEmpty);
-// await setStorage({ [STORAGE.encryptedKey]: encryptedRootKey });
-// await setStorage({
-// [STORAGE.network]: { id: NETWORK_ID.mainnet, node: NODE.mainnet },
-// });
-
-// await setStorage({
-// [STORAGE.currency]: 'usd',
-// });
-
-// const index = await createAccount(name, password);
-
-// //check for sub accounts
-// let searchIndex = 1;
-// while (true) {
-// let { paymentKey, stakeKey } = await requestAccountKey(
-// password,
-// searchIndex
-// );
-// const paymentKeyHashBech32 = paymentKey
-// .to_public()
-// .hash()
-// .to_bech32('addr_vkh');
-// // const stakeKeyHash = stakeKey.to_public().hash();
-// paymentKey.free();
-// // stakeKey.free();
-// paymentKey = null;
-// // stakeKey = null;
-// // const paymentAddr = Loader.Cardano.BaseAddress.new(
-// // Loader.Cardano.NetworkInfo.mainnet().network_id(),
-// // Loader.Cardano.StakeCredential.from_keyhash(paymentKeyHash),
-// // Loader.Cardano.StakeCredential.from_keyhash(stakeKeyHash)
-// // )
-// // .to_address()
-// // .to_bech32();
-// const transactions = await blockfrostRequest(
-// `/addresses/${paymentKeyHashBech32}/transactions`
-// );
-// if (transactions && !transactions.error && transactions.length >= 1)
-// createAccount(`Account ${searchIndex}`, password, searchIndex);
-// else break;
-// searchIndex++;
-// }
-
-// password = null;
-// await switchAccount(index);
-
-// return true;
-// };
-
-// export const mnemonicToObject = (mnemonic) => {
-// const mnemonicMap = {};
-// mnemonic.split(' ').forEach((word, index) => (mnemonicMap[index + 1] = word));
-// return mnemonicMap;
-// };
-
-// export const mnemonicFromObject = (mnemonicMap) => {
-// return Object.keys(mnemonicMap).reduce(
-// (acc, key) => (acc ? acc + ' ' + mnemonicMap[key] : acc + mnemonicMap[key]),
-// ''
-// );
-// };
-
-export const avatarToImage = avatar => {
+export const avatarToImage = (avatar: string) => {
const blob = new Blob(
[
createAvatar(style, {
@@ -1530,231 +134,16 @@ export const avatarToImage = avatar => {
return URL.createObjectURL(blob);
};
-export const getAsset = async unit => {
- return await Promise.resolve({});
- // if (!window.assets) {
- // window.assets = JSON.parse(
- // localStorage.getItem(LOCAL_STORAGE.assets) || '{}'
- // );
- // }
- // const assets = window.assets;
- // const asset = assets[unit] || {};
- // const time = Date.now();
- // const h1 = 6000000;
- // if (asset && asset.time && time - asset.time <= h1 && !asset.mint) {
- // return asset;
- // } else {
- // const { policyId, name, label } = fromAssetUnit(unit);
- // const bufferName = Buffer.from(name, 'hex');
- // asset.unit = unit;
- // asset.policy = policyId;
- // asset.fingerprint = AssetFingerprint.fromParts(
- // Buffer.from(policyId, 'hex'),
- // bufferName
- // ).fingerprint();
- // asset.name = Number.isInteger(label)
- // ? `(${label}) ` + bufferName.toString()
- // : bufferName.toString();
-
- // // CIP-0067 & CIP-0068 (support 222 and 333 sub standards)
-
- // if (label === 222) {
- // const refUnit = toAssetUnit(policyId, name, 100);
- // try {
- // const owners = await blockfrostRequest(`/assets/${refUnit}/addresses`);
- // if (!owners || owners.error) {
- // throw new Error('No owner found.');
- // }
- // const [refUtxo] = await blockfrostRequest(
- // `/addresses/${owners[0].address}/utxos/${refUnit}`
- // );
- // const datum =
- // refUtxo?.inline_datum ||
- // (await blockfrostRequest(`/scripts/datum/${refUtxo?.data_hash}/cbor`))
- // ?.cbor;
- // const metadataDatum = datum && (await Data.from(datum));
-
- // if (metadataDatum.index !== 0) throw new Error('No correct metadata.');
-
- // const metadata = metadataDatum && Data.toJson(metadataDatum.fields[0]);
-
- // asset.displayName = metadata.name;
- // asset.image = metadata.image ? linkToSrc(convertMetadataPropToString(metadata.image)) : '';
- // asset.decimals = 0;
- // } catch (_e) {
- // asset.displayName = asset.name;
- // asset.mint = true;
- // }
- // } else if (label === 333) {
- // const refUnit = toAssetUnit(policyId, name, 100);
- // try {
- // const owners = await blockfrostRequest(`/assets/${refUnit}/addresses`);
- // if (!owners || owners.error) {
- // throw new Error('No owner found.');
- // }
- // const [refUtxo] = await blockfrostRequest(
- // `/addresses/${owners[0].address}/utxos/${refUnit}`
- // );
- // const datum =
- // refUtxo?.inline_datum ||
- // (await blockfrostRequest(`/scripts/datum/${refUtxo?.data_hash}/cbor`))
- // ?.cbor;
- // const metadataDatum = datum && (await Data.from(datum));
-
- // if (metadataDatum.index !== 0) throw new Error('No correct metadata.');
-
- // const metadata = metadataDatum && Data.toJson(metadataDatum.fields[0]);
-
- // asset.displayName = metadata.name;
- // asset.image = linkToSrc(convertMetadataPropToString(metadata.logo)) || '';
- // asset.decimals = metadata.decimals || 0;
- // } catch (_e) {
- // asset.displayName = asset.name;
- // asset.mint = true;
- // }
- // } else {
- // let result = await blockfrostRequest(`/assets/${unit}`);
- // if (!result || result.error) {
- // result = {};
- // asset.mint = true;
- // }
- // const onchainMetadata =
- // result.onchain_metadata &&
- // ((result.onchain_metadata.version === 2 &&
- // result.onchain_metadata?.[`0x${policyId}`]?.[`0x${name}`]) ||
- // result.onchain_metadata);
- // asset.displayName =
- // (onchainMetadata && onchainMetadata.name) ||
- // (result.metadata && result.metadata.name) ||
- // asset.name;
- // asset.image =
- // (onchainMetadata &&
- // onchainMetadata.image &&
- // linkToSrc(convertMetadataPropToString(onchainMetadata.image))) ||
- // (result.metadata &&
- // result.metadata.logo &&
- // linkToSrc(result.metadata.logo, true)) ||
- // '';
- // asset.decimals = (result.metadata && result.metadata.decimals) || 0;
- // if (!asset.name) {
- // if (asset.displayName) asset.name = asset.displayName[0];
- // else asset.name = '-';
- // }
- // }
- // asset.time = Date.now();
- // assets[unit] = asset;
- // window.assets = assets;
- // localStorage.setItem(LOCAL_STORAGE.assets, JSON.stringify(assets));
- // return asset;
- // }
-};
-
-// export const updateBalance = async (currentAccount, network) => {
-// await Loader.load();
-// const assets = await getBalanceExtended();
-// const amount = await assetsToValue(assets);
-// await checkCollateral(currentAccount, network);
-
-// if (assets.length > 0) {
-// currentAccount[network.id].lovelace = assets.find(
-// (am) => am.unit === 'lovelace'
-// ).quantity;
-// currentAccount[network.id].assets = assets.filter(
-// (am) => am.unit !== 'lovelace'
-// );
-// if (currentAccount[network.id].assets.length > 0) {
-// const protocolParameters = await initTx();
-// const checkOutput = Loader.Cardano.TransactionOutput.new(
-// Loader.Cardano.Address.from_bech32(
-// currentAccount[network.id].paymentAddr
-// ),
-// amount
-// );
-// const minAda = Loader.Cardano.min_ada_required(
-// checkOutput,
-// Loader.Cardano.BigNum.from_str(protocolParameters.coinsPerUtxoWord)
-// ).to_str();
-// currentAccount[network.id].minAda = minAda;
-// } else {
-// currentAccount[network.id].minAda = 0;
-// }
-// } else {
-// currentAccount[network.id].lovelace = 0;
-// currentAccount[network.id].assets = [];
-// currentAccount[network.id].minAda = 0;
-// }
-// return true;
-// };
-
-// const updateTransactions = async (currentAccount, network) => {
-// const transactions = await getTransactions();
-// if (
-// transactions.length <= 0 ||
-// currentAccount[network.id].history.confirmed.includes(
-// transactions[0].txHash
-// )
-// )
-// return false;
-// let txHashes = transactions.map((tx) => tx.txHash);
-// txHashes = txHashes.concat(currentAccount[network.id].history.confirmed);
-// const txSet = new Set(txHashes);
-// currentAccount[network.id].history.confirmed = Array.from(txSet);
-// return true;
-// };
-
-export const setTransactions = async txs => {
- // const currentIndex = await getCurrentAccountIndex();
- // const network = await getNetwork();
- // const accounts = await getStorage(STORAGE.accounts);
- // accounts[currentIndex][network.id].history.confirmed = txs;
- // return await setStorage({
- // [STORAGE.accounts]: {
- // ...accounts,
- // },
- // });
-};
-
-export const updateAccount = async (forceUpdate = false) => {
- // const currentIndex = await getCurrentAccountIndex();
- // const accounts = await getStorage(STORAGE.accounts);
- // const currentAccount = accounts[currentIndex];
- // const network = await getNetwork();
- // await updateTransactions(currentAccount, network);
- // if (
- // currentAccount[network.id].history.confirmed[0] ==
- // currentAccount[network.id].lastUpdate &&
- // !forceUpdate &&
- // !currentAccount[network.id].forceUpdate
- // ) {
- // if (currentAccount[network.id].lovelace == null) {
- // // first initilization of account
- // currentAccount[network.id].lovelace = '0';
- // await setStorage({
- // [STORAGE.accounts]: {
- // ...accounts,
- // },
- // });
- // }
- // return;
- // }
- // // forcing acccount update for in case of breaking changes in an Nami update
- // if (currentAccount[network.id].forceUpdate)
- // delete currentAccount[network.id].forceUpdate;
- // await updateBalance(currentAccount, network);
- // currentAccount[network.id].lastUpdate =
- // currentAccount[network.id].history.confirmed[0];
- // return await setStorage({
- // [STORAGE.accounts]: {
- // ...accounts,
- // },
- // });
-};
+export const displayUnit = (
+ quantity?: bigint | number | string,
+ decimals: number | string = 6,
+) => {
+ if (quantity === undefined) return 0;
-export const displayUnit = (quantity, decimals = 6) => {
- return Number.parseInt(quantity) / 10 ** decimals;
+ return Number.parseInt(quantity.toString()) / 10 ** Number(decimals);
};
-export const toUnit = (amount, decimals = 6) => {
+export const toUnit = (amount: string, decimals = 6) => {
if (!amount) return '0';
let result = Number.parseFloat(
amount.toString().replace(/[\s,]/g, ''),
@@ -1762,7 +151,8 @@ export const toUnit = (amount, decimals = 6) => {
const split = result.split('.');
const front = split[0].replace(/[\s,]/g, '');
result =
- (front == 0 ? '' : front) + (split[1] ? split[1].slice(0, decimals) : '');
+ (Number(front) == 0 ? '' : front) +
+ (split[1] ? split[1].slice(0, decimals) : '');
if (!result) return '0';
else if (result == 'NaN') return '0';
return result;
diff --git a/packages/nami/src/api/extension/wallet.mock.ts b/packages/nami/src/api/extension/wallet.mock.ts
index ff52da679..54ae84a49 100644
--- a/packages/nami/src/api/extension/wallet.mock.ts
+++ b/packages/nami/src/api/extension/wallet.mock.ts
@@ -4,10 +4,4 @@ import * as actualApi from './wallet';
export * from './wallet';
-export const initTx = fn(actualApi.initTx).mockName('initTx');
-
-export const buildTx = fn(actualApi.buildTx).mockName('buildTx');
-
-export const undelegateTx = fn(actualApi.undelegateTx).mockName('undelegateTx');
-
-export const withdrawalTx = fn(actualApi.withdrawalTx).mockName('withdrawalTx');
+export const buildTx: jest.Mock = fn(actualApi.buildTx).mockName('buildTx');
diff --git a/packages/nami/src/api/extension/wallet.ts b/packages/nami/src/api/extension/wallet.ts
index 6ec03d3a4..cfdd57e85 100644
--- a/packages/nami/src/api/extension/wallet.ts
+++ b/packages/nami/src/api/extension/wallet.ts
@@ -1,54 +1,24 @@
-/* eslint-disable max-params */
-import { Cardano, Serialization } from '@cardano-sdk/core';
+import { Cardano } from '@cardano-sdk/core';
import { firstValueFrom } from 'rxjs';
-import { ERROR, TX } from '../../config/config';
-// import { Loader } from '../loader';
-import { blockfrostRequest } from '../util';
+import { TX } from '../../config/config';
-import { signTxHW, submitTx } from '.';
+import { submitTx } from '.';
-import type { OutsideHandlesContextValue } from '../../ui';
+import type { CommonOutsideHandlesContextValue } from '../../features/common-outside-handles-provider';
+import type { Serialization } from '@cardano-sdk/core';
import type { UnwitnessedTx } from '@cardano-sdk/tx-construction';
import type { Wallet } from '@lace/cardano';
-// const WEIGHTS = Uint32Array.from([
-// 200, // weight ideal > 100 inputs
-// 1000, // weight ideal < 100 inputs
-// 1500, // weight assets if plutus
-// 800, // weight assets if not plutus
-// 800, // weight distance if not plutus
-// 5000, // weight utxos
-// ]);
-
-export const initTx = async () => {
- return await Promise.resolve({});
- // const latest_block = await blockfrostRequest('/blocks/latest');
- // const p = await blockfrostRequest(`/epochs/latest/parameters`);
- // return {
- // linearFee: {
- // minFeeA: p.min_fee_a.toString(),
- // minFeeB: p.min_fee_b.toString(),
- // },
- // minUtxo: '1000000', //p.min_utxo, minUTxOValue protocol paramter has been removed since Alonzo HF. Calulation of minADA works differently now, but 1 minADA still sufficient for now
- // poolDeposit: p.pool_deposit,
- // keyDeposit: p.key_deposit,
- // coinsPerUtxoWord: p.coins_per_utxo_size.toString(),
- // maxValSize: p.max_val_size,
- // priceMem: p.price_mem,
- // priceStep: p.price_step,
- // maxTxSize: Number.parseInt(p.max_tx_size),
- // slot: Number.parseInt(latest_block.slot),
- // collateralPercentage: Number.parseInt(p.collateral_percent),
- // maxCollateralInputs: Number.parseInt(p.max_collateral_inputs),
- // };
-};
-
-export const buildTx = async (
- output: Serialization.TransactionOutput,
- auxiliaryData: Serialization.AuxiliaryData,
- inMemoryWallet: Wallet.ObservableWallet,
-): Promise => {
+export const buildTx = async ({
+ output,
+ auxiliaryData,
+ inMemoryWallet,
+}: Readonly<{
+ output: Serialization.TransactionOutput;
+ auxiliaryData: Serialization.AuxiliaryData;
+ inMemoryWallet: Wallet.ObservableWallet;
+}>): Promise => {
const txBuilder = inMemoryWallet.createTxBuilder();
const metadata = auxiliaryData.metadata()?.toCore();
const tip = await firstValueFrom(inMemoryWallet.tip$);
@@ -62,266 +32,22 @@ export const buildTx = async (
invalidHereafter: Cardano.Slot(tip.slot + TX.invalid_hereafter),
});
- const transaction = txBuilder.build();
-
- return transaction;
+ return txBuilder.build();
};
-export const signAndSubmit = async (
- tx: UnwitnessedTx,
- password: string,
- withSignTxConfirmation: OutsideHandlesContextValue['withSignTxConfirmation'],
- inMemoryWallet: Wallet.ObservableWallet,
-) =>
+export const signAndSubmit = async ({
+ tx,
+ password,
+ withSignTxConfirmation,
+ inMemoryWallet,
+}: Readonly<{
+ tx: UnwitnessedTx;
+ password: string;
+ withSignTxConfirmation: CommonOutsideHandlesContextValue['withSignTxConfirmation'];
+ inMemoryWallet: Wallet.ObservableWallet;
+}>) =>
withSignTxConfirmation(async () => {
const { cbor: signedTx } = await tx.sign();
- const txHash = await submitTx(signedTx, inMemoryWallet);
-
- return txHash;
+ return await submitTx(signedTx, inMemoryWallet);
}, password);
-
-export const signAndSubmitHW = async (
- tx: Serialization.Transaction,
- {
- keyHashes,
- account,
- hw,
- partialSign,
- }: Readonly<{ keyHashes: any; account: any; hw: any; partialSign?: boolean }>,
-) => {
- const witnessSet = await signTxHW(
- tx.toCbor(),
- keyHashes,
- account,
- hw,
- partialSign,
- );
-
- const transaction = new Serialization.Transaction(
- tx.body(),
- witnessSet,
- tx.auxiliaryData(),
- );
-
- try {
- const txHash = await submitTx(transaction.toCbor());
- return txHash;
- } catch {
- throw ERROR.submit;
- }
-};
-
-export const delegationTx = async (
- account,
- delegation,
- protocolParameters,
- poolKeyHash,
-) => {
- return await Promise.resolve({});
- // await Loader.load();
-
- // const txBuilderConfig = Loader.Cardano.TransactionBuilderConfigBuilder.new()
- // .coins_per_utxo_byte(
- // Loader.Cardano.BigNum.from_str(protocolParameters.coinsPerUtxoWord)
- // )
- // .fee_algo(
- // Loader.Cardano.LinearFee.new(
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeA),
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeB)
- // )
- // )
- // .key_deposit(Loader.Cardano.BigNum.from_str(protocolParameters.keyDeposit))
- // .pool_deposit(
- // Loader.Cardano.BigNum.from_str(protocolParameters.poolDeposit)
- // )
- // .max_tx_size(protocolParameters.maxTxSize)
- // .max_value_size(protocolParameters.maxValSize)
- // .ex_unit_prices(Loader.Cardano.ExUnitPrices.from_float(0, 0))
- // .collateral_percentage(protocolParameters.collateralPercentage)
- // .max_collateral_inputs(protocolParameters.maxCollateralInputs)
- // .build();
-
- // const txBuilder = Loader.Cardano.TransactionBuilder.new(txBuilderConfig);
-
- // if (!delegation.active)
- // txBuilder.add_certificate(
- // Loader.Cardano.Certificate.new_stake_registration(
- // Loader.Cardano.StakeRegistration.new(
- // Loader.Cardano.StakeCredential.from_keyhash(
- // Loader.Cardano.Ed25519KeyHash.from_bytes(
- // Buffer.from(account.stakeKeyHash, 'hex')
- // )
- // )
- // )
- // )
- // );
-
- // txBuilder.add_certificate(
- // Loader.Cardano.Certificate.new_stake_delegation(
- // Loader.Cardano.StakeDelegation.new(
- // Loader.Cardano.StakeCredential.from_keyhash(
- // Loader.Cardano.Ed25519KeyHash.from_bytes(
- // Buffer.from(account.stakeKeyHash, 'hex')
- // )
- // ),
- // Loader.Cardano.Ed25519KeyHash.from_bytes(
- // Buffer.from(poolKeyHash, 'hex')
- // )
- // )
- // )
- // );
-
- // txBuilder.set_ttl(
- // Loader.Cardano.BigNum.from_str(
- // (protocolParameters.slot + TX.invalid_hereafter).toString()
- // )
- // );
-
- // const utxos = await getUtxos();
-
- // const utxosCore = Loader.Cardano.TransactionUnspentOutputs.new();
- // utxos.forEach((utxo) => utxosCore.add(utxo));
-
- // txBuilder.add_inputs_from(
- // utxosCore,
- // Loader.Cardano.Address.from_bech32(account.paymentAddr),
- // WEIGHTS
- // );
-
- // txBuilder.balance(Loader.Cardano.Address.from_bech32(account.paymentAddr));
-
- // const transaction = await txBuilder.construct();
-
- // return transaction;
-};
-
-export const withdrawalTx = async (account, delegation, protocolParameters) => {
- return await Promise.resolve({});
- // await Loader.load();
-
- // const txBuilderConfig = Loader.Cardano.TransactionBuilderConfigBuilder.new()
- // .coins_per_utxo_byte(
- // Loader.Cardano.BigNum.from_str(protocolParameters.coinsPerUtxoWord)
- // )
- // .fee_algo(
- // Loader.Cardano.LinearFee.new(
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeA),
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeB)
- // )
- // )
- // .key_deposit(Loader.Cardano.BigNum.from_str(protocolParameters.keyDeposit))
- // .pool_deposit(
- // Loader.Cardano.BigNum.from_str(protocolParameters.poolDeposit)
- // )
- // .max_tx_size(protocolParameters.maxTxSize)
- // .max_value_size(protocolParameters.maxValSize)
- // .ex_unit_prices(Loader.Cardano.ExUnitPrices.from_float(0, 0))
- // .collateral_percentage(protocolParameters.collateralPercentage)
- // .max_collateral_inputs(protocolParameters.maxCollateralInputs)
- // .build();
-
- // const txBuilder = Loader.Cardano.TransactionBuilder.new(txBuilderConfig);
-
- // txBuilder.add_withdrawal(
- // Loader.Cardano.RewardAddress.from_address(
- // Loader.Cardano.Address.from_bech32(account.rewardAddr)
- // ),
- // Loader.Cardano.BigNum.from_str(delegation.rewards)
- // );
-
- // txBuilder.set_ttl(
- // Loader.Cardano.BigNum.from_str(
- // (protocolParameters.slot + TX.invalid_hereafter).toString()
- // )
- // );
-
- // const utxos = await getUtxos();
-
- // const utxosCore = Loader.Cardano.TransactionUnspentOutputs.new();
- // utxos.forEach((utxo) => utxosCore.add(utxo));
-
- // txBuilder.add_inputs_from(
- // utxosCore,
- // Loader.Cardano.Address.from_bech32(account.paymentAddr),
- // WEIGHTS
- // );
-
- // txBuilder.balance(Loader.Cardano.Address.from_bech32(account.paymentAddr));
-
- // const transaction = await txBuilder.construct();
-
- // return transaction;
-};
-
-export const undelegateTx = async (account, delegation, protocolParameters) => {
- return await Promise.resolve({});
- // await Loader.load();
-
- // const txBuilderConfig = Loader.Cardano.TransactionBuilderConfigBuilder.new()
- // .coins_per_utxo_byte(
- // Loader.Cardano.BigNum.from_str(protocolParameters.coinsPerUtxoWord)
- // )
- // .fee_algo(
- // Loader.Cardano.LinearFee.new(
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeA),
- // Loader.Cardano.BigNum.from_str(protocolParameters.linearFee.minFeeB)
- // )
- // )
- // .key_deposit(Loader.Cardano.BigNum.from_str(protocolParameters.keyDeposit))
- // .pool_deposit(
- // Loader.Cardano.BigNum.from_str(protocolParameters.poolDeposit)
- // )
- // .max_tx_size(protocolParameters.maxTxSize)
- // .max_value_size(protocolParameters.maxValSize)
- // .ex_unit_prices(Loader.Cardano.ExUnitPrices.from_float(0, 0))
- // .collateral_percentage(protocolParameters.collateralPercentage)
- // .max_collateral_inputs(protocolParameters.maxCollateralInputs)
- // .build();
-
- // const txBuilder = Loader.Cardano.TransactionBuilder.new(txBuilderConfig);
-
- // if (delegation.rewards > 0) {
- // txBuilder.add_withdrawal(
- // Loader.Cardano.RewardAddress.from_address(
- // Loader.Cardano.Address.from_bech32(account.rewardAddr)
- // ),
- // Loader.Cardano.BigNum.from_str(delegation.rewards)
- // );
- // }
-
- // txBuilder.add_certificate(
- // Loader.Cardano.Certificate.new_stake_deregistration(
- // Loader.Cardano.StakeDeregistration.new(
- // Loader.Cardano.StakeCredential.from_keyhash(
- // Loader.Cardano.Ed25519KeyHash.from_bytes(
- // Buffer.from(account.stakeKeyHash, 'hex')
- // )
- // )
- // )
- // )
- // );
-
- // txBuilder.set_ttl(
- // Loader.Cardano.BigNum.from_str(
- // (protocolParameters.slot + TX.invalid_hereafter).toString()
- // )
- // );
-
- // const utxos = await getUtxos();
-
- // const utxosCore = Loader.Cardano.TransactionUnspentOutputs.new();
- // utxos.forEach((utxo) => utxosCore.add(utxo));
-
- // txBuilder.add_inputs_from(
- // utxosCore,
- // Loader.Cardano.Address.from_bech32(account.paymentAddr),
- // WEIGHTS
- // );
-
- // txBuilder.balance(Loader.Cardano.Address.from_bech32(account.paymentAddr));
-
- // const transaction = await txBuilder.construct();
-
- // return transaction;
-};
diff --git a/packages/nami/src/api/loader.mock.ts b/packages/nami/src/api/loader.mock.ts
deleted file mode 100644
index 0dc90e992..000000000
--- a/packages/nami/src/api/loader.mock.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-/* eslint-disable functional/immutable-data */
-
-import { currentAccount } from '../mocks/account.mock';
-
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-export const Loader = (): any => void 0;
-Loader.load = async () => await Promise.resolve(true);
-Loader.Cardano = {
- TransactionOutput: {
- new: () => ({
- add: () => void 0,
- }),
- },
- TransactionOutputs: {
- new: () => ({
- add: () => void 0,
- }),
- },
- Transaction: {
- from_bytes: () => ({
- body: () => ({
- fee: () => ({ to_str: () => '214341' }),
- outputs: () => ({
- len: () => 1,
- get: () => ({
- datum: () => void 0,
- amount: () => void 0,
- address: () => ({
- to_bytes: () => [
- 0, 232, 252, 40, 72, 12, 115, 72, 109, 40, 128, 116, 197, 172,
- 118, 96, 173, 6, 17, 174, 92, 229, 5, 222, 25, 67, 83, 70, 105,
- 97, 234, 112, 175, 29, 231, 23, 149, 223, 82, 230, 45, 28, 15,
- 44, 136, 23, 241, 59, 92, 212, 180, 14, 4, 202, 181, 173, 106,
- ],
- to_bech32: () => currentAccount.paymentKeyHashBech32,
- }),
- }),
- }),
- collateral: () => ({ len: () => 0 }),
- certs: () => ({ len: () => 0 }),
- withdrawals: () => ({ keys: () => ({ len: () => 0 }) }),
- required_signers: () => ({ len: () => 0 }),
- mint: () => ({ len: () => 0 }),
- script_data_hash: () => ({ len: () => 0 }),
- }),
- witness_set: () => ({
- native_scripts: () => ({ len: () => 0 }),
- }),
- auxiliary_data: () => ({
- metadata: () => ({
- get: () => void 0,
- keys: () => ({ len: () => 1, get: () => ({ to_str: () => '674' }) }),
- }),
- }),
- }),
- },
- AuxiliaryData: {
- new: () => ({
- set_metadata: () => void 0,
- metadata: () => void 0,
- }),
- },
- GeneralTransactionMetadata: {
- new: () => ({
- insert: () => void 0,
- len: () => void 0,
- }),
- },
- Address: {
- from_bech32: () => void 0,
- from_bytes: () => void 0,
- },
- TransactionUnspentOutput: {
- from_bytes: () => void 0,
- },
- BigNum: {
- from_str: () => void 0,
- },
- Value: {
- zero: () => void 0,
- new: () => ({
- checked_add: () => void 0,
- }),
- },
- encode_json_str_to_metadatum: () => void 0,
- decode_metadatum_to_json_str: () => '{"msg":["Swap request"]}',
-};
-
-Loader.Message = {};
diff --git a/packages/nami/src/api/loader.ts b/packages/nami/src/api/loader.ts
deleted file mode 100644
index 60ebe280f..000000000
--- a/packages/nami/src/api/loader.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// import * as wasm from '../wasm/cardano_multiplatform_lib/cardano_multiplatform_lib.generated';
-// import * as wasm2 from '../wasm/cardano_message_signing/cardano_message_signing.generated';
-
-const wasm = {};
-const wasm2 = {};
-
-/**
- * Loads the WASM modules
- */
-
-class LoaderClass {
- async load() {
- if (this._wasm && this._wasm2) return;
- try {
- await wasm.instantiate();
- await wasm2.instantiate();
- } catch {
- // Only happens when running with Jest (Node.js)
- }
- /**
- * @private
- */
- this._wasm = wasm;
- /**
- * @private
- */
- this._wasm2 = wasm2;
- }
-
- get Cardano() {
- return this._wasm;
- }
-
- get Message() {
- return this._wasm2;
- }
-}
-
-// export const Loader = new LoaderClass();
-export const Loader = () => void 0;
diff --git a/packages/nami/src/api/util.mock.ts b/packages/nami/src/api/util.mock.ts
index 6254d6876..c356f1d2b 100644
--- a/packages/nami/src/api/util.mock.ts
+++ b/packages/nami/src/api/util.mock.ts
@@ -1,5 +1,3 @@
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { fn } from '@storybook/test';
import * as actualApi from './util';
@@ -9,9 +7,3 @@ export * from './util';
export const minAdaRequired = fn(actualApi.minAdaRequired).mockName(
'minAdaRequired',
);
-
-export const sumUtxos = fn(actualApi.sumUtxos).mockName('sumUtxos');
-
-export const valueToAssets = fn(actualApi.valueToAssets).mockName(
- 'valueToAssets',
-);
diff --git a/packages/nami/src/api/util.ts b/packages/nami/src/api/util.ts
index f4314ea97..b620ef9c5 100644
--- a/packages/nami/src/api/util.ts
+++ b/packages/nami/src/api/util.ts
@@ -3,79 +3,13 @@
/* eslint-disable max-params */
/* eslint-disable unicorn/no-null */
/* eslint-disable @typescript-eslint/naming-convention */
-import {
- AddressType,
- CertificateType,
- DatumType,
- HARDENED,
- PoolKeyType,
- PoolOwnerType,
- PoolRewardAccountType,
- RelayType,
- StakeCredentialParamsType,
- TransactionSigningMode,
- TxAuxiliaryDataType,
- TxOutputDestinationType,
- TxOutputFormat,
- TxRequiredSignerType,
-} from '@cardano-foundation/ledgerjs-hw-app-cardano';
import { Serialization } from '@cardano-sdk/core';
import { minAdaRequired as minAdaRequiredSDK } from '@cardano-sdk/tx-construction';
-import AssetFingerprint from '@emurgo/cip14-js';
-import { PROTO } from '@trezor/connect-web';
import { crc8 } from 'crc';
import { CurrencyCode } from '../adapters/currency';
-import { NETWORK_ID } from '../config/config';
import provider from '../config/provider';
-import { getNetwork } from './extension';
-import { Loader } from './loader';
-
-const {
- CardanoAddressType,
- CardanoCertificateType,
- CardanoPoolRelayType,
- CardanoTxSigningMode,
-} = PROTO;
-
-export const delay = async delayInMs =>
- new Promise(resolve => {
- setTimeout(() => {
- resolve(null);
- }, delayInMs);
- });
-
-export const blockfrostRequest = async (
- endpoint: string,
- headers?: any,
- body?: any,
- signal?: any,
-) => {
- const network = await getNetwork();
- let result;
-
- while (!result || result.status_code === 500) {
- if (result) {
- await delay(100);
- }
- const rawResult = await fetch(provider.api.base(network.node) + endpoint, {
- headers: {
- ...provider.api.key(network.name || network.id),
- ...provider.api.header,
- ...headers,
- 'Cache-Control': 'no-cache',
- },
- method: body ? 'POST' : 'GET',
- body,
- signal,
- });
- result = await rawResult.json();
- }
-
- return result;
-};
-
/**
*
* @param {string} currency - eg. usd
@@ -89,72 +23,16 @@ export const currencyToSymbol = (currency: CurrencyCode) => {
return currencyMap[currency];
};
-// /**
-// *
-// * @param {string} hex
-// * @returns
-// */
-export const hexToAscii = hex => Buffer.from(hex, 'hex').toString();
-
-export const networkNameToId = name => {
- const names = {
- [NETWORK_ID.mainnet]: 1,
- [NETWORK_ID.testnet]: 0,
- [NETWORK_ID.preview]: 0,
- [NETWORK_ID.preprod]: 0,
- };
- return names[name];
-};
-
-/**
- *
- * @param {MultiAsset} multiAsset
- * @returns
- */
-export const multiAssetCount = async multiAsset => {
- await Loader.load();
- if (!multiAsset) return 0;
- let count = 0;
- const policies = multiAsset.keys();
- for (let j = 0; j < multiAsset.len(); j++) {
- const policy = policies.get(j);
- const policyAssets = multiAsset.get(policy);
- const assetNames = policyAssets.keys();
- for (let k = 0; k < assetNames.len(); k++) {
- count++;
- }
- }
- return count;
-};
-
-/**
- * @typedef {Object} Amount - Unit/Quantity pair
- * @property {string} unit - Token Type
- * @property {int} quantity - Token Amount
- */
-
-/**
- * @typedef {Amount[]} AmountList - List of unit/quantity pair
- */
-
-/**
- * @typedef {Output[]} OutputList - List of Output
- */
-
-/**
- * @typedef {Object} Output - Outputs Format
- * @property {string} address - Address Output
- * @property {AmountList} amount - Amount (lovelace & Native Token)
- */
-
/** Cardano metadata properties can hold a max of 64 bytes. The alternative is to use an array of strings. */
-export const convertMetadataPropToString = src => {
+export const convertMetadataPropToString = (
+ src: Readonly,
+) => {
if (typeof src === 'string') return src;
else if (Array.isArray(src)) return src.join('');
return null;
};
-export const linkToSrc = (link, base64 = false) => {
+export const linkToSrc = (link: string, base64 = false) => {
const base64regex =
/^([\d+/A-Za-z]{4})*(([\d+/A-Za-z]{2}==)|([\d+/A-Za-z]{3}=))?$/;
if (link.startsWith('https://')) return link;
@@ -172,77 +50,29 @@ export const linkToSrc = (link, base64 = false) => {
} else if (base64 && base64regex.test(link))
return 'data:image/png;base64,' + link;
else if (link.startsWith('data:image')) return link;
- return null;
-};
-
-/**
- *
- * @param {JSON} output
- * @param {BaseAddress} address
- * @returns
- */
-export const utxoFromJson = async (output, address) => {
- await Loader.load();
- return Loader.Cardano.TransactionUnspentOutput.new(
- Loader.Cardano.TransactionInput.new(
- Loader.Cardano.TransactionHash.from_bytes(
- Buffer.from(output.tx_hash || output.txHash, 'hex'),
- ),
- Loader.Cardano.BigNum.from_str(
- (output.output_index ?? output.txId).toString(),
- ),
- ),
- Loader.Cardano.TransactionOutput.new(
- Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')),
- assetsToValue(output.amount),
- ),
- );
+ return undefined;
};
-/**
- *
- * @param {TransactionUnspentOutput[]} utxos
- * @returns
- */
-export const sumUtxos = utxos => {
- let value = Loader.Cardano.Value.new(Loader.Cardano.BigNum.from_str('0'));
- for (const utxo of utxos) value = value.checked_add(utxo.output().amount());
- return value;
-};
-
-/**
- *
- *
- *
- * @param {TransactionUnspentOutput} utxo
- * @returns
- */
-export const utxoToJson = async utxo => {
- await Loader.load();
- const assets = await valueToAssets(utxo.output().amount());
- return {
- txHash: Buffer.from(
- utxo.input().transaction_id().to_bytes(),
- 'hex',
- ).toString('hex'),
- txId: utxo.input().index(),
- amount: assets,
- };
-};
-
-export const assetsToValue = assets => {
+export const assetsToValue = (assets: readonly any[]) => {
const tokenMap = new Map();
- const lovelace = assets.find(asset => asset.unit === 'lovelace');
+ const lovelace = assets.find(
+ (asset: Readonly<{ unit: string }>) => asset.unit === 'lovelace',
+ );
const policies = [
...new Set(
assets
- .filter(asset => asset.unit !== 'lovelace')
- .map(asset => asset.unit.slice(0, 56)),
+ .filter(
+ (asset: Readonly<{ unit: string }>) => asset.unit !== 'lovelace',
+ )
+ .map((asset: Readonly<{ unit: any[] | string }>) =>
+ asset.unit.slice(0, 56),
+ ),
),
];
for (const policy of policies) {
const policyAssets = assets.filter(
- asset => asset.unit.slice(0, 56) === policy,
+ (asset: Readonly<{ unit: any[] | string }>) =>
+ asset.unit.slice(0, 56) === policy,
);
for (const asset of policyAssets) {
if (tokenMap.has(asset.unit)) {
@@ -260,1035 +90,18 @@ export const assetsToValue = assets => {
return value;
};
-// /**
-// *
-// * @param {Value} value
-// */
-export const valueToAssets = async value => {
- await Loader.load();
- const assets = [];
- assets.push({ unit: 'lovelace', quantity: value.coin().to_str() });
- if (value.multiasset()) {
- const multiAssets = value.multiasset().keys();
- for (let j = 0; j < multiAssets.len(); j++) {
- const policy = multiAssets.get(j);
- const policyAssets = value.multiasset().get(policy);
- const assetNames = policyAssets.keys();
- for (let k = 0; k < assetNames.len(); k++) {
- const policyAsset = assetNames.get(k);
- const quantity = policyAssets.get(policyAsset);
- const asset =
- Buffer.from(policy.to_bytes(), 'hex').toString('hex') +
- Buffer.from(policyAsset.name(), 'hex').toString('hex');
- const _policy = asset.slice(0, 56);
- const _name = asset.slice(56);
- const fingerprint = AssetFingerprint.fromParts(
- Buffer.from(_policy, 'hex'),
- Buffer.from(_name, 'hex'),
- ).fingerprint();
- assets.push({
- unit: asset,
- quantity: quantity.to_str(),
- policy: _policy,
- name: hexToAscii(_name),
- fingerprint,
- });
- }
- }
- }
- // if (value.coin().to_str() == '0') return [];
- return assets;
-};
-
-export const minAdaRequired = (output, coinsPerUtxoWord) => {
+export const minAdaRequired = (
+ output: Serialization.TransactionOutput,
+ coinsPerUtxoWord: bigint,
+) => {
return minAdaRequiredSDK(output.toCore(), coinsPerUtxoWord).toString();
};
-const outputsToTrezor = (outputs, address, index) => {
- const trezorOutputs = [];
- for (let i = 0; i < outputs.len(); i++) {
- const output = outputs.get(i);
- const multiAsset = output.amount().multiasset();
- let tokenBundle = null;
- if (multiAsset) {
- tokenBundle = [];
- for (let j = 0; j < multiAsset.keys().len(); j++) {
- const policy = multiAsset.keys().get(j);
- const assets = multiAsset.get(policy);
- const tokens = [];
- for (let k = 0; k < assets.keys().len(); k++) {
- const assetName = assets.keys().get(k);
- const amount = assets.get(assetName).to_str();
- tokens.push({
- assetNameBytes: Buffer.from(assetName.name()).toString('hex'),
- amount,
- });
- }
- // sort canonical
- tokens.sort((a, b) => {
- if (a.assetNameBytes.length == b.assetNameBytes.length) {
- return a.assetNameBytes > b.assetNameBytes ? 1 : -1;
- } else if (a.assetNameBytes.length > b.assetNameBytes.length)
- return 1;
- else return -1;
- });
- tokenBundle.push({
- policyId: Buffer.from(policy.to_bytes()).toString('hex'),
- tokenAmounts: tokens,
- });
- }
- }
- const outputAddress = Buffer.from(output.address().to_bytes()).toString(
- 'hex',
- );
-
- const outputAddressHuman = (() => {
- try {
- return Loader.Cardano.BaseAddress.from_address(output.address())
- .to_address()
- .to_bech32();
- } catch {}
- try {
- return Loader.Cardano.EnterpriseAddress.from_address(output.address())
- .to_address()
- .to_bech32();
- } catch {}
- try {
- return Loader.Cardano.PointerAddress.from_address(output.address())
- .to_address()
- .to_bech32();
- } catch {}
- return Loader.Cardano.ByronAddress.from_address(
- output.address(),
- ).to_base58();
- })();
-
- const destination =
- outputAddress == address
- ? {
- addressParameters: {
- addressType: CardanoAddressType.BASE,
- path: `m/1852'/1815'/${index}'/0/0`,
- stakingPath: `m/1852'/1815'/${index}'/2/0`,
- },
- }
- : {
- address: outputAddressHuman,
- };
- const datumHash =
- output.datum() && output.datum().kind() === 0
- ? Buffer.from(output.datum().as_data_hash().to_bytes()).toString('hex')
- : null;
- const inlineDatum =
- output.datum() && output.datum().kind() === 1
- ? Buffer.from(output.datum().as_data().get().to_bytes()).toString('hex')
- : null;
- const referenceScript = output.script_ref()
- ? Buffer.from(output.script_ref().get().to_bytes()).toString('hex')
- : null;
- const outputRes = {
- amount: output.amount().coin().to_str(),
- tokenBundle,
- datumHash,
- format: output.format(),
- inlineDatum,
- referenceScript,
- ...destination,
- };
- if (!tokenBundle) delete outputRes.tokenBundle;
- if (!datumHash) delete outputRes.datumHash;
- if (!inlineDatum) delete outputRes.inlineDatum;
- if (!referenceScript) delete outputRes.referenceScript;
- trezorOutputs.push(outputRes);
- }
- return trezorOutputs;
-};
-
-/**
- *
- * @param {Transaction} tx
- */
-export const txToTrezor = async (tx, network, keys, address, index) => {
- await Loader.load();
-
- let signingMode = CardanoTxSigningMode.ORDINARY_TRANSACTION;
- const inputs = tx.body().inputs();
- const trezorInputs = [];
- for (let i = 0; i < inputs.len(); i++) {
- const input = inputs.get(i);
- trezorInputs.push({
- prev_hash: Buffer.from(input.transaction_id().to_bytes()).toString('hex'),
- prev_index: Number.parseInt(input.index().to_str()),
- path: keys.payment.path, // needed to include payment key witness if available
- });
- }
-
- const outputs = tx.body().outputs();
- const trezorOutputs = outputsToTrezor(outputs, address, index);
-
- let trezorCertificates = null;
- const certificates = tx.body().certs();
- if (certificates) {
- trezorCertificates = [];
- for (let i = 0; i < certificates.len(); i++) {
- const cert = certificates.get(i);
- const certificate = {};
- if (cert.kind() === 0) {
- const credential = cert.as_stake_registration().stake_credential();
- certificate.type = CardanoCertificateType.STAKE_REGISTRATION;
- if (credential.kind() === 0) {
- certificate.path = keys.stake.path;
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.scriptHash = scriptHash;
- }
- } else if (cert.kind() === 1) {
- const credential = cert.as_stake_deregistration().stake_credential();
- certificate.type = CardanoCertificateType.STAKE_DEREGISTRATION;
- if (credential.kind() === 0) {
- certificate.path = keys.stake.path;
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.scriptHash = scriptHash;
- }
- } else if (cert.kind() === 2) {
- const delegation = cert.as_stake_delegation();
- const credential = delegation.stake_credential();
- const poolKeyHashHex = Buffer.from(
- delegation.pool_keyhash().to_bytes(),
- ).toString('hex');
- certificate.type = CardanoCertificateType.STAKE_DELEGATION;
- if (credential.kind() === 0) {
- certificate.path = keys.stake.path;
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.scriptHash = scriptHash;
- }
- certificate.pool = poolKeyHashHex;
- } else if (cert.kind() === 3) {
- const params = cert.as_pool_registration().pool_params();
- certificate.type = CardanoCertificateType.STAKE_POOL_REGISTRATION;
- const owners = params.pool_owners();
- const poolOwners = [];
- for (let i = 0; i < owners.len(); i++) {
- const keyHash = Buffer.from(owners.get(i).to_bytes()).toString('hex');
- if (keyHash == keys.stake.hash) {
- signingMode = CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER;
- poolOwners.push({
- stakingKeyPath: keys.stake.path,
- });
- } else {
- poolOwners.push({
- stakingKeyHash: keyHash,
- });
- }
- }
- const relays = params.relays();
- const trezorRelays = [];
- for (let i = 0; i < relays.len(); i++) {
- const relay = relays.get(i);
- if (relay.kind() === 0) {
- const singleHostAddr = relay.as_single_host_addr();
- const type = CardanoPoolRelayType.SINGLE_HOST_IP;
- const port = singleHostAddr.port();
- const ipv4Address = singleHostAddr.ipv4()
- ? bytesToIp(singleHostAddr.ipv4().ip())
- : null;
- const ipv6Address = singleHostAddr.ipv6()
- ? bytesToIp(singleHostAddr.ipv6().ip())
- : null;
- trezorRelays.push({ type, port, ipv4Address, ipv6Address });
- } else if (relay.kind() === 1) {
- const type = CardanoPoolRelayType.SINGLE_HOST_NAME;
- const singleHostName = relay.as_single_host_name();
- const port = singleHostName.port();
- const hostName = singleHostName.dns_name().record();
- trezorRelays.push({
- type,
- port,
- hostName,
- });
- } else if (relay.kind() === 2) {
- const type = CardanoPoolRelayType.MULTIPLE_HOST_NAME;
- const multiHostName = relay.as_multi_host_name();
- const hostName = multiHostName.dns_name();
- trezorRelays.push({
- type,
- hostName,
- });
- }
- }
- const cost = params.cost().to_str();
- const margin = params.margin();
- const pledge = params.pledge().to_str();
- const poolId = Buffer.from(params.operator().to_bytes()).toString(
- 'hex',
- );
- const metadata = params.pool_metadata()
- ? {
- url: params.pool_metadata().url().url(),
- hash: Buffer.from(
- params.pool_metadata().pool_metadata_hash().to_bytes(),
- ).toString('hex'),
- }
- : null;
- const rewardAccount = params.reward_account().to_address().to_bech32();
- const vrfKeyHash = Buffer.from(
- params.vrf_keyhash().to_bytes(),
- ).toString('hex');
-
- certificate.poolParameters = {
- poolId,
- vrfKeyHash,
- pledge,
- cost,
- margin: {
- numerator: margin.numerator().to_str(),
- denominator: margin.denominator().to_str(),
- },
- rewardAccount,
- owners: poolOwners,
- relays: trezorRelays,
- metadata,
- };
- }
- trezorCertificates.push(certificate);
- }
- }
- const fee = tx.body().fee().to_str();
- const ttl = tx.body().ttl();
- const withdrawals = tx.body().withdrawals();
- let trezorWithdrawals = null;
- if (withdrawals) {
- trezorWithdrawals = [];
- for (let i = 0; i < withdrawals.keys().len(); i++) {
- const withdrawal = {};
- const rewardAddress = withdrawals.keys().get(i);
- if (rewardAddress.payment_cred().kind() === 0) {
- withdrawal.path = keys.stake.path;
- } else {
- withdrawal.scriptHash = Buffer.from(
- rewardAddress.payment_cred().to_scripthash().to_bytes(),
- ).toString('hex');
- }
- withdrawal.amount = withdrawals.get(rewardAddress).to_str();
- trezorWithdrawals.push(withdrawal);
- }
- }
- const auxiliaryData = tx.body().auxiliary_data_hash()
- ? {
- hash: Buffer.from(tx.body().auxiliary_data_hash().to_bytes()).toString(
- 'hex',
- ),
- }
- : null;
- const validityIntervalStart = tx.body().validity_start_interval()
- ? tx.body().validity_start_interval().to_str()
- : null;
-
- const mint = tx.body().mint();
- let additionalWitnessRequests = null;
- let mintBundle = null;
- if (mint) {
- mintBundle = [];
- for (let j = 0; j < mint.keys().len(); j++) {
- const policy = mint.keys().get(j);
- const assets = mint.get(policy);
- const tokens = [];
- for (let k = 0; k < assets.keys().len(); k++) {
- const assetName = assets.keys().get(k);
- const amount = assets.get(assetName);
- tokens.push({
- assetNameBytes: Buffer.from(assetName.name()).toString('hex'),
- mintAmount: amount.is_positive()
- ? amount.as_positive().to_str()
- : '-' + amount.as_negative().to_str(),
- });
- }
- // sort canonical
- tokens.sort((a, b) => {
- if (a.assetNameBytes.length == b.assetNameBytes.length) {
- return a.assetNameBytes > b.assetNameBytes ? 1 : -1;
- } else if (a.assetNameBytes.length > b.assetNameBytes.length) return 1;
- else return -1;
- });
- mintBundle.push({
- policyId: Buffer.from(policy.to_bytes()).toString('hex'),
- tokenAmounts: tokens,
- });
- }
- additionalWitnessRequests = [];
- if (keys.payment.path) additionalWitnessRequests.push(keys.payment.path);
- if (keys.stake.path) additionalWitnessRequests.push(keys.stake.path);
- }
-
- // Plutus
- const scriptDataHash = tx.body().script_data_hash()
- ? Buffer.from(tx.body().script_data_hash().to_bytes()).toString('hex')
- : null;
-
- let collateralInputs = null;
- if (tx.body().collateral()) {
- collateralInputs = [];
- const coll = tx.body().collateral();
- for (let i = 0; i < coll.len(); i++) {
- const input = coll.get(i);
- if (keys.payment.path) {
- collateralInputs.push({
- prev_hash: Buffer.from(input.transaction_id().to_bytes()).toString(
- 'hex',
- ),
- prev_index: Number.parseInt(input.index().to_str()),
- path: keys.payment.path, // needed to include payment key witness if available
- });
- } else {
- collateralInputs.push({
- prev_hash: Buffer.from(input.transaction_id().to_bytes()).toString(
- 'hex',
- ),
- prev_index: Number.parseInt(input.index().to_str()),
- });
- }
- signingMode = CardanoTxSigningMode.PLUTUS_TRANSACTION;
- }
- }
-
- let requiredSigners = null;
- if (tx.body().required_signers()) {
- requiredSigners = [];
- const r = tx.body().required_signers();
- for (let i = 0; i < r.len(); i++) {
- const signer = Buffer.from(r.get(i).to_bytes()).toString('hex');
- if (signer === keys.payment.hash) {
- requiredSigners.push({
- keyPath: keys.payment.path,
- });
- } else if (signer === keys.stake.hash) {
- requiredSigners.push({
- keyPath: keys.stake.path,
- });
- } else {
- requiredSigners.push({
- keyHash: signer,
- });
- }
- }
- signingMode = CardanoTxSigningMode.PLUTUS_TRANSACTION;
- }
-
- let referenceInputs = null;
- if (tx.body().reference_inputs()) {
- referenceInputs = [];
- const ri = tx.body().reference_inputs();
- for (let i = 0; i < ri.len(); i++) {
- referenceInputs.push({
- prev_hash: ri.get(i).transaction_id().to_hex(),
- prev_index: Number.parseInt(ri.get(i).index().to_str()),
- });
- }
- signingMode = CardanoTxSigningMode.PLUTUS_TRANSACTION;
- }
-
- const totalCollateral = tx.body().total_collateral()
- ? tx.body().total_collateral().to_str()
- : null;
-
- const collateralReturn = (() => {
- if (tx.body().collateral_return()) {
- const outputs = Loader.Cardano.TransactionOutputs.new();
- outputs.add(tx.body().collateral_return());
- const [out] = outputsToTrezor(outputs, address, index);
- return out;
- }
- return null;
- })();
-
- const includeNetworkId = !!tx.body().network_id();
-
- const trezorTx = {
- signingMode,
- inputs: trezorInputs,
- outputs: trezorOutputs,
- fee,
- ttl: ttl ? ttl.to_str() : null,
- validityIntervalStart,
- certificates: trezorCertificates,
- withdrawals: trezorWithdrawals,
- auxiliaryData,
- mint: mintBundle,
- scriptDataHash,
- collateralInputs,
- requiredSigners,
- protocolMagic: network === 1 ? 764_824_073 : 42,
- networkId: network,
- includeNetworkId,
- additionalWitnessRequests,
- collateralReturn,
- totalCollateral,
- referenceInputs,
- };
- for (const key of Object.keys(trezorTx))
- !trezorTx[key] && trezorTx[key] != 0 && delete trezorTx[key];
- return trezorTx;
-};
-
-const outputsToLedger = (outputs, address, index) => {
- const ledgerOutputs = [];
- for (let i = 0; i < outputs.len(); i++) {
- const output = outputs.get(i);
- const multiAsset = output.amount().multiasset();
- let tokenBundle = null;
- if (multiAsset) {
- tokenBundle = [];
- for (let j = 0; j < multiAsset.keys().len(); j++) {
- const policy = multiAsset.keys().get(j);
- const assets = multiAsset.get(policy);
- const tokens = [];
- for (let k = 0; k < assets.keys().len(); k++) {
- const assetName = assets.keys().get(k);
- const amount = assets.get(assetName).to_str();
- tokens.push({
- assetNameHex: Buffer.from(assetName.name()).toString('hex'),
- amount,
- });
- }
- // sort canonical
- tokens.sort((a, b) => {
- if (a.assetNameHex.length == b.assetNameHex.length) {
- return a.assetNameHex > b.assetNameHex ? 1 : -1;
- } else if (a.assetNameHex.length > b.assetNameHex.length) return 1;
- else return -1;
- });
- tokenBundle.push({
- policyIdHex: Buffer.from(policy.to_bytes()).toString('hex'),
- tokens,
- });
- }
- }
-
- const outputAddress = Buffer.from(output.address().to_bytes()).toString(
- 'hex',
- );
- const destination =
- outputAddress == address
- ? {
- type: TxOutputDestinationType.DEVICE_OWNED,
- params: {
- type: AddressType.BASE_PAYMENT_KEY_STAKE_KEY,
- params: {
- spendingPath: [
- HARDENED + 1852,
- HARDENED + 1815,
- HARDENED + index,
- 0,
- 0,
- ],
- stakingPath: [
- HARDENED + 1852,
- HARDENED + 1815,
- HARDENED + index,
- 2,
- 0,
- ],
- },
- },
- }
- : {
- type: TxOutputDestinationType.THIRD_PARTY,
- params: {
- addressHex: outputAddress,
- },
- };
- const datum = output.datum();
- const refScript = output.script_ref();
- const isBabbage = output.format();
- const outputRes = isBabbage
- ? {
- format: TxOutputFormat.MAP_BABBAGE,
- amount: output.amount().coin().to_str(),
- tokenBundle,
- destination,
- datum: datum
- ? datum.kind() === 0
- ? {
- type: DatumType.HASH,
- datumHashHex: Buffer.from(
- datum.as_data_hash().to_bytes(),
- ).toString('hex'),
- }
- : {
- type: DatumType.INLINE,
- datumHex: Buffer.from(
- datum.as_data().get().to_bytes(),
- ).toString('hex'),
- }
- : null,
- referenceScriptHex: refScript
- ? Buffer.from(refScript.get().to_bytes()).toString('hex')
- : null,
- }
- : {
- format: TxOutputFormat.ARRAY_LEGACY,
- amount: output.amount().coin().to_str(),
- tokenBundle,
- destination,
- datumHashHex:
- datum && datum.kind() === 0
- ? Buffer.from(datum.as_data_hash().to_bytes()).toString('hex')
- : null,
- };
- for (const key of Object.keys(outputRes)) {
- if (!outputRes[key]) delete outputRes[key];
- }
- ledgerOutputs.push(outputRes);
- }
- return ledgerOutputs;
-};
-
-/**
- *
- * @param {Transaction} tx
- */
-export const txToLedger = async (tx, network, keys, address, index) => {
- await Loader.load();
-
- let signingMode = TransactionSigningMode.ORDINARY_TRANSACTION;
- const inputs = tx.body().inputs();
- const ledgerInputs = [];
- for (let i = 0; i < inputs.len(); i++) {
- const input = inputs.get(i);
- ledgerInputs.push({
- txHashHex: Buffer.from(input.transaction_id().to_bytes()).toString('hex'),
- outputIndex: Number.parseInt(input.index().to_str()),
- path: keys.payment.path, // needed to include payment key witness if available
- });
- }
-
- const ledgerOutputs = outputsToLedger(tx.body().outputs(), address, index);
-
- let ledgerCertificates = null;
- const certificates = tx.body().certs();
- if (certificates) {
- ledgerCertificates = [];
- for (let i = 0; i < certificates.len(); i++) {
- const cert = certificates.get(i);
- const certificate = {};
- if (cert.kind() === 0) {
- const credential = cert.as_stake_registration().stake_credential();
- certificate.type = CertificateType.STAKE_REGISTRATION;
- if (credential.kind() === 0) {
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.KEY_PATH,
- keyPath: keys.stake.path,
- },
- };
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.SCRIPT_HASH,
- scriptHash,
- },
- };
- }
- } else if (cert.kind() === 1) {
- const credential = cert.as_stake_deregistration().stake_credential();
- certificate.type = CertificateType.STAKE_DEREGISTRATION;
- if (credential.kind() === 0) {
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.KEY_PATH,
- keyPath: keys.stake.path,
- },
- };
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.SCRIPT_HASH,
- scriptHash,
- },
- };
- }
- } else if (cert.kind() === 2) {
- const delegation = cert.as_stake_delegation();
- const credential = delegation.stake_credential();
- const poolKeyHashHex = Buffer.from(
- delegation.pool_keyhash().to_bytes(),
- ).toString('hex');
- certificate.type = CertificateType.STAKE_DELEGATION;
- if (credential.kind() === 0) {
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.KEY_PATH,
- keyPath: keys.stake.path,
- },
- };
- } else {
- const scriptHash = Buffer.from(
- credential.to_scripthash().to_bytes(),
- ).toString('hex');
- certificate.params = {
- stakeCredential: {
- type: StakeCredentialParamsType.SCRIPT_HASH,
- scriptHash,
- },
- };
- }
- certificate.params.poolKeyHashHex = poolKeyHashHex;
- } else if (cert.kind() === 3) {
- const params = cert.as_pool_registration().pool_params();
- certificate.type = CertificateType.STAKE_POOL_REGISTRATION;
- const owners = params.pool_owners();
- const poolOwners = [];
- for (let i = 0; i < owners.len(); i++) {
- const keyHash = Buffer.from(owners.get(i).to_bytes()).toString('hex');
- if (keyHash == keys.stake.hash) {
- signingMode = TransactionSigningMode.POOL_REGISTRATION_AS_OWNER;
- poolOwners.push({
- type: PoolOwnerType.DEVICE_OWNED,
- stakingPath: keys.stake.path,
- });
- } else {
- poolOwners.push({
- type: PoolOwnerType.THIRD_PARTY,
- stakingKeyHashHex: keyHash,
- });
- }
- }
- const relays = params.relays();
- const ledgerRelays = [];
- for (let i = 0; i < relays.len(); i++) {
- const relay = relays.get(i);
- if (relay.kind() === 0) {
- const singleHostAddr = relay.as_single_host_addr();
- const type = RelayType.SINGLE_HOST_IP_ADDR;
- const portNumber = singleHostAddr.port();
- const ipv4 = singleHostAddr.ipv4()
- ? bytesToIp(singleHostAddr.ipv4().ip())
- : null;
- const ipv6 = singleHostAddr.ipv6()
- ? bytesToIp(singleHostAddr.ipv6().ip())
- : null;
- ledgerRelays.push({ type, params: { portNumber, ipv4, ipv6 } });
- } else if (relay.kind() === 1) {
- const type = RelayType.SINGLE_HOST_HOSTNAME;
- const singleHostName = relay.as_single_host_name();
- const portNumber = singleHostName.port();
- const dnsName = singleHostName.dns_name().record();
- ledgerRelays.push({
- type,
- params: { portNumber, dnsName },
- });
- } else if (relay.kind() === 2) {
- const type = RelayType.MULTI_HOST;
- const multiHostName = relay.as_multi_host_name();
- const dnsName = multiHostName.dns_name();
- ledgerRelays.push({
- type,
- params: { dnsName },
- });
- }
- }
- const cost = params.cost().to_str();
- const margin = params.margin();
- const pledge = params.pledge().to_str();
- const operator = Buffer.from(params.operator().to_bytes()).toString(
- 'hex',
- );
- let poolKey;
- if (operator == keys.stake.hash) {
- signingMode = TransactionSigningMode.POOL_REGISTRATION_AS_OPERATOR;
- poolKey = {
- type: PoolKeyType.DEVICE_OWNED,
- params: { path: keys.stake.path },
- };
- } else {
- poolKey = {
- type: PoolKeyType.THIRD_PARTY,
- params: { keyHashHex: operator },
- };
- }
- const metadata = params.pool_metadata()
- ? {
- metadataUrl: params.pool_metadata().url().url(),
- metadataHashHex: Buffer.from(
- params.pool_metadata().pool_metadata_hash().to_bytes(),
- ).toString('hex'),
- }
- : null;
- const rewardAccountHex = Buffer.from(
- params.reward_account().to_address().to_bytes(),
- ).toString('hex');
- const rewardAccount =
- rewardAccountHex == address
- ? {
- type: PoolRewardAccountType.DEVICE_OWNED,
- params: { path: keys.stake.path },
- }
- : {
- type: PoolRewardAccountType.THIRD_PARTY,
- params: { rewardAccountHex },
- };
- const vrfKeyHashHex = Buffer.from(
- params.vrf_keyhash().to_bytes(),
- ).toString('hex');
-
- certificate.params = {
- poolKey,
- vrfKeyHashHex,
- pledge,
- cost,
- margin: {
- numerator: margin.numerator().to_str(),
- denominator: margin.denominator().to_str(),
- },
- rewardAccount,
- poolOwners,
- relays: ledgerRelays,
- metadata,
- };
- }
- ledgerCertificates.push(certificate);
- }
- }
- const fee = tx.body().fee().to_str();
- const ttl = tx.body().ttl() ? tx.body().ttl().to_str() : null;
- const withdrawals = tx.body().withdrawals();
- let ledgerWithdrawals = null;
- if (withdrawals) {
- ledgerWithdrawals = [];
- for (let i = 0; i < withdrawals.keys().len(); i++) {
- const withdrawal = { stakeCredential: {} };
- const rewardAddress = withdrawals.keys().get(i);
- if (rewardAddress.payment_cred().kind() === 0) {
- withdrawal.stakeCredential.type = StakeCredentialParamsType.KEY_PATH;
- withdrawal.stakeCredential.keyPath = keys.stake.path;
- } else {
- withdrawal.stakeCredential.type = StakeCredentialParamsType.SCRIPT_HASH;
- withdrawal.stakeCredential.scriptHash = Buffer.from(
- rewardAddress.payment_cred().to_scripthash().to_bytes(),
- ).toString('hex');
- }
- withdrawal.amount = withdrawals.get(rewardAddress).to_str();
- ledgerWithdrawals.push(withdrawal);
- }
- }
- const auxiliaryData = tx.body().auxiliary_data_hash()
- ? {
- type: TxAuxiliaryDataType.ARBITRARY_HASH,
- params: {
- hashHex: Buffer.from(
- tx.body().auxiliary_data_hash().to_bytes(),
- ).toString('hex'),
- },
- }
- : null;
- const validityIntervalStart = tx.body().validity_start_interval()
- ? tx.body().validity_start_interval().to_str()
- : null;
-
- const mint = tx.body().mint();
- let additionalWitnessPaths = null;
- let mintBundle = null;
- if (mint) {
- mintBundle = [];
- for (let j = 0; j < mint.keys().len(); j++) {
- const policy = mint.keys().get(j);
- const assets = mint.get(policy);
- const tokens = [];
- for (let k = 0; k < assets.keys().len(); k++) {
- const assetName = assets.keys().get(k);
- const amount = assets.get(assetName);
- tokens.push({
- assetNameHex: Buffer.from(assetName.name()).toString('hex'),
- amount: amount.is_positive()
- ? amount.as_positive().to_str()
- : '-' + amount.as_negative().to_str(),
- });
- }
- // sort canonical
- tokens.sort((a, b) => {
- if (a.assetNameHex.length == b.assetNameHex.length) {
- return a.assetNameHex > b.assetNameHex ? 1 : -1;
- } else if (a.assetNameHex.length > b.assetNameHex.length) return 1;
- else return -1;
- });
- mintBundle.push({
- policyIdHex: Buffer.from(policy.to_bytes()).toString('hex'),
- tokens,
- });
- }
- }
- additionalWitnessPaths = [];
- if (keys.payment.path) additionalWitnessPaths.push(keys.payment.path);
- if (keys.stake.path) additionalWitnessPaths.push(keys.stake.path);
-
- // Plutus
- const scriptDataHashHex = tx.body().script_data_hash()
- ? Buffer.from(tx.body().script_data_hash().to_bytes()).toString('hex')
- : null;
-
- let collateralInputs = null;
- if (tx.body().collateral()) {
- collateralInputs = [];
- const coll = tx.body().collateral();
- for (let i = 0; i < coll.len(); i++) {
- const input = coll.get(i);
- if (keys.payment.path) {
- collateralInputs.push({
- txHashHex: Buffer.from(input.transaction_id().to_bytes()).toString(
- 'hex',
- ),
- outputIndex: Number.parseInt(input.index().to_str()),
- path: keys.payment.path, // needed to include payment key witness if available
- });
- } else {
- collateralInputs.push({
- txHashHex: Buffer.from(input.transaction_id().to_bytes()).toString(
- 'hex',
- ),
- outputIndex: Number.parseInt(input.index().to_str()),
- });
- }
- signingMode = TransactionSigningMode.PLUTUS_TRANSACTION;
- }
- }
-
- const collateralOutput = (() => {
- if (tx.body().collateral_return()) {
- const outputs = Loader.Cardano.TransactionOutputs.new();
- outputs.add(tx.body().collateral_return());
- const [out] = outputsToLedger(outputs, address, index);
- return out;
- }
- return null;
- })();
-
- const totalCollateral = tx.body().total_collateral()
- ? tx.body().total_collateral().to_str()
- : null;
-
- let referenceInputs = null;
- if (tx.body().reference_inputs()) {
- referenceInputs = [];
- const refInputs = tx.body().reference_inputs();
- for (let i = 0; i < refInputs.len(); i++) {
- const input = refInputs.get(i);
- referenceInputs.push({
- txHashHex: input.transaction_id().to_hex(),
- outputIndex: Number.parseInt(input.index().to_str()),
- path: null,
- });
- }
- }
-
- let requiredSigners = null;
- if (tx.body().required_signers()) {
- requiredSigners = [];
- const r = tx.body().required_signers();
- for (let i = 0; i < r.len(); i++) {
- const signer = Buffer.from(r.get(i).to_bytes()).toString('hex');
- if (signer === keys.payment.hash) {
- requiredSigners.push({
- type: TxRequiredSignerType.PATH,
- path: keys.payment.path,
- });
- } else if (signer === keys.stake.hash) {
- requiredSigners.push({
- type: TxRequiredSignerType.PATH,
- path: keys.stake.path,
- });
- } else {
- requiredSigners.push({
- type: TxRequiredSignerType.HASH,
- hashHex: signer,
- });
- }
- }
- signingMode = TransactionSigningMode.PLUTUS_TRANSACTION;
- }
-
- const includeNetworkId = !!tx.body().network_id();
-
- const ledgerTx = {
- network: {
- protocolMagic: network === 1 ? 764_824_073 : 42,
- networkId: network,
- },
- inputs: ledgerInputs,
- outputs: ledgerOutputs,
- fee,
- ttl,
- certificates: ledgerCertificates,
- withdrawals: ledgerWithdrawals,
- auxiliaryData,
- validityIntervalStart,
- mint: mintBundle,
- scriptDataHashHex,
- collateralInputs,
- requiredSigners,
- includeNetworkId,
- collateralOutput,
- totalCollateral,
- referenceInputs,
- };
-
- for (const key of Object.keys(ledgerTx))
- !ledgerTx[key] && ledgerTx[key] != 0 && delete ledgerTx[key];
-
- const fullTx = {
- signingMode,
- tx: ledgerTx,
- additionalWitnessPaths,
- };
- for (const key of Object.keys(fullTx))
- !fullTx[key] && fullTx[key] != 0 && delete fullTx[key];
-
- return fullTx;
-};
-
-const bytesToIp = bytes => {
- if (!bytes) return null;
- if (bytes.length === 4) {
- return { ipv4: bytes.join('.') };
- } else if (bytes.length === 16) {
- let ipv6 = '';
- for (let i = 0; i < bytes.length; i += 2) {
- ipv6 += bytes[i].toString(16) + bytes[i + 1].toString(16) + ':';
- }
- ipv6 = ipv6.slice(0, -1);
- return { ipv6 };
- }
- return null;
-};
-
-const checksum = num =>
+const checksum = (num: string) =>
crc8(Buffer.from(num, 'hex')).toString(16).padStart(2, '0');
-// export function toLabel(num) {
-// if (num < 0 || num > 65535) {
-// throw new Error(
-// `Label ${num} out of range: min label 1 - max label 65535.`
-// );
-// }
-// const numHex = num.toString(16).padStart(4, '0');
-// return '0' + numHex + checksum(numHex) + '0';
-// }
-
-export const fromLabel = label => {
- if (label.length !== 8 || !(label[0] === '0' && label[7] === '0')) {
+export const fromLabel = (label: Readonly) => {
+ if (label.length !== 8 || !(label.startsWith('0') && label[7] === '0')) {
return null;
}
const numHex = label.slice(1, 5);
@@ -1297,199 +110,12 @@ export const fromLabel = label => {
return check === checksum(numHex) ? num : null;
};
-export const toAssetUnit = (policyId, name, label) => {
- const hexLabel = Number.isInteger(label) ? toLabel(label) : '';
- const n = name ?? '';
- if ((n + hexLabel).length > 64) {
- throw new Error('Asset name size exceeds 32 bytes.');
- }
- if (policyId.length !== 56) {
- throw new Error(`Policy id invalid: ${policyId}.`);
- }
- return policyId + hexLabel + n;
-};
-
-export const fromAssetUnit = unit => {
+export const fromAssetUnit = (unit: Readonly) => {
const policyId = unit.slice(0, 56);
const label = fromLabel(unit.slice(56, 64));
const name = (() => {
const hexName = Number.isInteger(label) ? unit.slice(64) : unit.slice(56);
- return unit.length === 56 ? '' : hexName || null;
+ return unit.length === 56 ? '' : hexName || '';
})();
return { policyId, name, label };
};
-
-export class Constr {
- constructor(index, fields) {
- this.index = index;
- this.fields = fields;
- }
-}
-
-export const Data = {
- /** Convert PlutusData to Cbor encoded data */
- to: async plutusData => {
- await Loader.load();
- const serialize = data => {
- try {
- if (
- typeof data === 'bigint' ||
- typeof data === 'number' ||
- (typeof data === 'string' &&
- !Number.isNaN(Number.parseInt(data)) &&
- data.endsWith('n'))
- ) {
- const bigint =
- typeof data === 'string' ? BigInt(data.slice(0, -1)) : data;
- return Loader.Cardano.PlutusData.new_integer(
- Loader.Cardano.BigInt.from_str(bigint.toString()),
- );
- } else if (typeof data === 'string') {
- return Loader.Cardano.PlutusData.new_bytes(Buffer.from(data, 'hex'));
- } else if (data instanceof Uint8Array) {
- return Loader.Cardano.PlutusData.new_bytes(data);
- } else if (data instanceof Constr) {
- const { index, fields } = data;
- const plutusList = Loader.Cardano.PlutusList.new();
-
- for (const field of fields) plutusList.add(serialize(field));
-
- return Loader.Cardano.PlutusData.new_constr_plutus_data(
- Loader.Cardano.ConstrPlutusData.new(
- Loader.Cardano.BigNum.from_str(index.toString()),
- plutusList,
- ),
- );
- } else if (Array.isArray(data)) {
- const plutusList = Loader.Cardano.PlutusList.new();
-
- for (const arg of data) plutusList.add(serialize(arg));
-
- return Loader.Cardano.PlutusData.new_list(plutusList);
- } else if (data instanceof Map) {
- const plutusMap = Loader.Cardano.PlutusMap.new();
-
- for (const [key, value] of data.entries()) {
- plutusMap.insert(serialize(key), serialize(value));
- }
-
- return Loader.Cardano.PlutusData.new_map(plutusMap);
- }
- throw new Error('Unsupported type');
- } catch (error) {
- throw new Error('Could not serialize the data: ' + error);
- }
- };
- return toHex(serialize(plutusData).to_bytes());
- },
-
- /** Convert Cbor encoded data to PlutusData */
- from: async data => {
- await Loader.load();
- const plutusData = Loader.Cardano.PlutusData.from_bytes(
- Buffer.from(data, 'hex'),
- );
- const deserialize = data => {
- if (data.kind() === 0) {
- const constr = data.as_constr_plutus_data();
- const l = constr.data();
- const desL = [];
- for (let i = 0; i < l.len(); i++) {
- desL.push(deserialize(l.get(i)));
- }
- return new Constr(Number.parseInt(constr.alternative().to_str()), desL);
- } else if (data.kind() === 1) {
- const m = data.as_map();
- const desM = new Map();
- const keys = m.keys();
- for (let i = 0; i < keys.len(); i++) {
- desM.set(deserialize(keys.get(i)), deserialize(m.get(keys.get(i))));
- }
- return desM;
- } else if (data.kind() === 2) {
- const l = data.as_list();
- const desL = [];
- for (let i = 0; i < l.len(); i++) {
- desL.push(deserialize(l.get(i)));
- }
- return desL;
- } else if (data.kind() === 3) {
- return BigInt(data.as_integer().to_str());
- } else if (data.kind() === 4) {
- return Buffer.from(data.as_bytes()).toString('hex');
- }
- throw new Error('Unsupported type');
- };
- return deserialize(plutusData);
- },
-
- /**
- * Convert conveniently a Json object (e.g. Metadata) to PlutusData.
- * Note: Constructor cannot be used here.
- */
- fromJson: json => {
- const toPlutusData = json => {
- if (typeof json === 'string') {
- return Buffer.from(json);
- }
- if (typeof json === 'number') return BigInt(json);
- if (typeof json === 'bigint') return json;
- if (Array.isArray(json)) return json.map(v => toPlutusData(v));
- if (json instanceof Object) {
- const tempMap = new Map();
- for (const [key, value] of Object.entries(json)) {
- tempMap.set(toPlutusData(key), toPlutusData(value));
- }
- return tempMap;
- }
- throw new Error('Unsupported type');
- };
- return toPlutusData(json);
- },
-
- /**
- * Convert PlutusData to a Json object.
- * Note: Constructor cannot be used here, also only bytes/integers as Json keys.
- */
- toJson: plutusData => {
- const fromPlutusData = data => {
- if (
- typeof data === 'bigint' ||
- typeof data === 'number' ||
- (typeof data === 'string' &&
- !Number.isNaN(Number.parseInt(data)) &&
- data.endsWith('n'))
- ) {
- const bigint =
- typeof data === 'string' ? BigInt(data.slice(0, -1)) : data;
- return Number.parseInt(bigint.toString());
- }
- if (typeof data === 'string') {
- return Buffer.from(data, 'hex').toString();
- }
- if (Array.isArray(data)) return data.map(v => fromPlutusData(v));
- if (data instanceof Map) {
- const tempJson = {};
- for (const [key, value] of data.entries()) {
- const convertedKey = fromPlutusData(key);
- if (
- typeof convertedKey !== 'string' &&
- typeof convertedKey !== 'number'
- ) {
- throw new TypeError(
- 'Unsupported type (Note: Only bytes or integers can be keys of a JSON object)',
- );
- }
- tempJson[convertedKey] = fromPlutusData(value);
- }
- return tempJson;
- }
- throw new Error(
- 'Unsupported type (Note: Constructor cannot be converted to JSON)',
- );
- };
- return fromPlutusData(plutusData);
- },
-
- empty: () => 'd87980',
-};
diff --git a/packages/nami/src/config/config.ts b/packages/nami/src/config/config.ts
index 58b926661..d15138a9d 100644
--- a/packages/nami/src/config/config.ts
+++ b/packages/nami/src/config/config.ts
@@ -1,71 +1,3 @@
-export const TARGET = 'nami-wallet';
-export const SENDER = { extension: 'extension', webpage: 'webpage' };
-export const METHOD = {
- isWhitelisted: 'isWhitelisted',
- enable: 'enable',
- isEnabled: 'isEnabled',
- currentWebpage: 'currentWebpage',
- getNetworkId: 'getNetworkId',
- getBalance: 'getBalance',
- getDelegation: 'getDelegation',
- getUtxos: 'getUtxos',
- getCollateral: 'getCollateral',
- getRewardAddress: 'getRewardAddress',
- getAddress: 'getAddress',
- signData: 'signData',
- signTx: 'signTx',
- submitTx: 'submitTx',
- //internal
- requestData: 'requestData',
- returnData: 'returnData',
-};
-
-/*
-
-localStorage = {
- whitelisted: [string],
- encryptedKey: encrypted string
- accounts: {
- [accountIndex]: {
- index: accountIndex,
- paymentKeyHash: cbor string,
- stakeKeyHash cbor string,
- name: string,
- mainnet: {
- lovelace: 0,
- assets: [],
- history: {},
- },
- testnet: {
- lovelace: 0,
- assets: [],
- history: {},
- },
- avatar: Math.random().toString(),
- },
- },
- currentAccount: accountIndex,
- network: {id: "mainnet" | "testnet", node: "https://blockfrost..."}
-}
-*/
-
-export const STORAGE = {
- whitelisted: 'whitelisted',
- encryptedKey: 'encryptedKey',
- accounts: 'accounts',
- currentAccount: 'currentAccount',
- network: 'network',
- currency: 'currency',
- migration: 'migration',
- analyticsConsent: 'analytics',
- userId: 'userId',
- acceptedLegalDocsVersion: 'acceptedLegalDocsVersion',
-};
-
-export const LOCAL_STORAGE = {
- assets: 'assets',
-};
-
export const NODE = {
mainnet: 'https://cardano-mainnet.blockfrost.io/api/v0',
testnet: 'https://cardano-testnet.blockfrost.io/api/v0',
@@ -73,20 +5,6 @@ export const NODE = {
preprod: 'https://cardano-preprod.blockfrost.io/api/v0',
};
-export const NETWORK_ID = {
- mainnet: 'mainnet',
- testnet: 'testnet',
- preview: 'preview',
- preprod: 'preprod',
-};
-
-export const NETWORKD_ID_NUMBER = {
- mainnet: 1,
- testnet: 0,
- preview: 0,
- preprod: 0,
-};
-
export const POPUP = {
main: 'mainPopup',
internal: 'internalPopup',
@@ -125,20 +43,6 @@ export const TX = {
invalid_hereafter: 3600 * 6, //6h from current slot
};
-export const EVENT = {
- accountChange: 'accountChange',
- networkChange: 'networkChange',
- // TODO
- // connect: 'connect',
- // disconnect: 'disconnect',
- // utxoChange: 'utxoChange',
-};
-
-export const ADA_HANDLE = {
- mainnet: 'f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a',
- testnet: '8d18d786e92776c824607fd8e193ec535c79dc61ea2405ddf3b09fe3',
-};
-
// Errors dApp Connector
export const APIError = {
InvalidRequest: {
@@ -159,22 +63,6 @@ export const APIError = {
},
};
-export const DataSignError = {
- ProofGeneration: {
- code: 1,
- info: 'Wallet could not sign the data (e.g. does not have the secret key associated with the address).',
- },
- AddressNotPK: {
- code: 2,
- info: 'Address was not a P2PK address or Reward address and thus had no SK associated with it.',
- },
- UserDeclined: { code: 3, info: 'User declined to sign the data.' },
- InvalidFormat: {
- code: 4,
- info: 'If a wallet enforces data format requirements, this error signifies that the data did not conform to valid formats.',
- },
-};
-
export const TxSendError = {
Refused: {
code: 1,
diff --git a/packages/nami/src/config/provider.ts b/packages/nami/src/config/provider.ts
index 30e429bdc..eef407af1 100644
--- a/packages/nami/src/config/provider.ts
+++ b/packages/nami/src/config/provider.ts
@@ -1,47 +1,5 @@
-import { version } from '../../package.json';
-import { Network } from '../types';
-import { NetworkType } from '../types';
-
-import { NODE } from './config';
-
-// import secrets from 'secrets';
-
-const networkToProjectId = {
- mainnet: 'preprodT2ItX4qKPEJOiJ7HH3q4zOFMZK4wQrtH',
- testnet: 'preprodT2ItX4qKPEJOiJ7HH3q4zOFMZK4wQrtH',
- preprod: 'preprodT2ItX4qKPEJOiJ7HH3q4zOFMZK4wQrtH',
- preview: 'preprodT2ItX4qKPEJOiJ7HH3q4zOFMZK4wQrtH',
-};
-
-interface PriceData {
- cardano: Record;
-}
-
-interface keyType {
- project_id: string;
-}
-
-// eslint-disable-next-line import/no-default-export
export default {
api: {
ipfs: 'https://ipfs.blockfrost.dev/ipfs',
- base: (node = NODE.mainnet) => node,
- header: { ['secrets.NAMI_HEADER' || 'dummy']: version },
- key: (network: NetworkType = NetworkType.MAINNET): keyType => ({
- project_id: networkToProjectId[network],
- }),
- price: async (currency = 'usd'): Promise => {
- const response = await fetch(
- `https://api.coingecko.com/api/v3/simple/price?ids=cardano&vs_currencies=${currency}`,
- );
-
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
-
- const data: PriceData = await response.json();
-
- return data.cardano[currency];
- },
},
};
diff --git a/packages/nami/src/features/ada-handle/config.ts b/packages/nami/src/features/ada-handle/config.ts
deleted file mode 100644
index b9f80a415..000000000
--- a/packages/nami/src/features/ada-handle/config.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Cardano } from '@cardano-sdk/core';
-import { Wallet } from '@lace/cardano';
-
-export const ADA_HANDLE_POLICY_ID = Wallet.ADA_HANDLE_POLICY_ID;
-export const isAdaHandleEnabled = process.env.USE_ADA_HANDLE === 'true';
-export const HANDLE_SERVER_URLS: Record = {
- [Cardano.NetworkMagics.Mainnet]: process.env.ADA_HANDLE_URL_MAINNET,
- [Cardano.NetworkMagics.Preprod]: process.env.ADA_HANDLE_URL_PREPROD,
- [Cardano.NetworkMagics.Preview]: process.env.ADA_HANDLE_URL_PREVIEW,
- [Cardano.NetworkMagics.Sanchonet]: process.env.ADA_HANDLE_URL_SANCHONET,
-};
diff --git a/packages/nami/src/features/ada-handle/useHandleResolver.ts b/packages/nami/src/features/ada-handle/useHandleResolver.ts
deleted file mode 100644
index d823f0985..000000000
--- a/packages/nami/src/features/ada-handle/useHandleResolver.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { useMemo } from 'react';
-
-import { handleHttpProvider } from '@cardano-sdk/cardano-services-client';
-
-import { HANDLE_SERVER_URLS } from './config';
-
-import type { Cardano, HandleProvider } from '@cardano-sdk/core';
-
-export const useHandleResolver = (
- networkMagic: Cardano.NetworkMagics,
-): HandleProvider => {
- return useMemo(() => {
- const serverUrl = HANDLE_SERVER_URLS[networkMagic];
- return handleHttpProvider({
- baseUrl: serverUrl,
- logger: console,
- });
- }, [networkMagic]);
-};
diff --git a/packages/nami/src/features/analytics/hooks.ts b/packages/nami/src/features/analytics/hooks.ts
index e3f91da25..49de28560 100644
--- a/packages/nami/src/features/analytics/hooks.ts
+++ b/packages/nami/src/features/analytics/hooks.ts
@@ -1,12 +1,15 @@
-/* eslint-disable functional/no-throw-statements */
-import { useOutsideHandles } from '../../ui';
+import { useCommonOutsideHandles } from '../../features/common-outside-handles-provider';
import type { Events } from './events';
+import type { PostHogProperties } from '@lace/common';
export const useCaptureEvent = () => {
- const { sendEventToPostHog } = useOutsideHandles();
+ const { sendEventToPostHog } = useCommonOutsideHandles();
- return async (event: Events): Promise => {
- await sendEventToPostHog(event);
+ return async (
+ event: Events,
+ properties?: PostHogProperties,
+ ): Promise => {
+ await sendEventToPostHog(event, properties);
};
};
diff --git a/packages/nami/src/features/common-outside-handles-provider/OutsideHandlesProvider.tsx b/packages/nami/src/features/common-outside-handles-provider/OutsideHandlesProvider.tsx
new file mode 100644
index 000000000..b078ecdb1
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/OutsideHandlesProvider.tsx
@@ -0,0 +1,20 @@
+import type { PropsWithChildren } from 'react';
+import React, { useMemo } from 'react';
+
+import { Provider } from './context';
+
+import type { CommonOutsideHandlesContextValue } from './types';
+
+type OutsideHandlesProviderProps =
+ PropsWithChildren;
+
+export const OutsideHandlesProvider = ({
+ children,
+ ...props
+}: Readonly): React.ReactElement => {
+ const contextValue = useMemo(
+ () => props,
+ Object.values(props),
+ );
+ return {children};
+};
diff --git a/packages/nami/src/features/common-outside-handles-provider/context.ts b/packages/nami/src/features/common-outside-handles-provider/context.ts
new file mode 100644
index 000000000..f9c57281d
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/context.ts
@@ -0,0 +1,10 @@
+import { createContext } from 'react';
+
+import type { CommonOutsideHandlesContextValue } from './types';
+
+export const context = createContext(
+ // eslint-disable-next-line unicorn/no-null
+ null,
+);
+
+export const { Provider } = context;
diff --git a/packages/nami/src/features/common-outside-handles-provider/index.ts b/packages/nami/src/features/common-outside-handles-provider/index.ts
new file mode 100644
index 000000000..864725f15
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/index.ts
@@ -0,0 +1,3 @@
+export { OutsideHandlesProvider as CommonOutsideHandlesProvider } from './OutsideHandlesProvider';
+export { useOutsideHandles as useCommonOutsideHandles } from './useOutsideHandles';
+export * from './types';
diff --git a/packages/nami/src/features/common-outside-handles-provider/types.ts b/packages/nami/src/features/common-outside-handles-provider/types.ts
new file mode 100644
index 000000000..2c3511ec6
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/types.ts
@@ -0,0 +1,21 @@
+import type { Events } from '../../features/analytics/events';
+import type { HandleProvider } from '@cardano-sdk/core';
+import type { WalletType } from '@cardano-sdk/web-extension';
+import type { Wallet } from '@lace/cardano';
+import type { PostHogProperties } from '@lace/common';
+
+export interface CommonOutsideHandlesContextValue {
+ cardanoCoin: Wallet.CoinId;
+ walletType: WalletType;
+ openHWFlow: (path: string) => void;
+ inMemoryWallet: Wallet.ObservableWallet;
+ withSignTxConfirmation: (
+ action: () => Promise,
+ password?: string,
+ ) => Promise;
+ sendEventToPostHog: (
+ action: Events,
+ properties?: PostHogProperties,
+ ) => Promise;
+ handleResolver: HandleProvider;
+}
diff --git a/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.mock.ts b/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.mock.ts
new file mode 100644
index 000000000..9b2aa9366
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.mock.ts
@@ -0,0 +1,9 @@
+import { fn } from '@storybook/test';
+
+import * as actualApi from './useOutsideHandles';
+
+export * from './useOutsideHandles';
+
+export const useCommonOutsideHandles: jest.Mock = fn(
+ actualApi.useOutsideHandles,
+).mockName('useOutsideHandles');
diff --git a/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.ts b/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.ts
new file mode 100644
index 000000000..b478c4d95
--- /dev/null
+++ b/packages/nami/src/features/common-outside-handles-provider/useOutsideHandles.ts
@@ -0,0 +1,15 @@
+/* eslint-disable functional/no-throw-statements */
+import { useContext } from 'react';
+
+import { context } from './context';
+
+import type { CommonOutsideHandlesContextValue } from './types';
+
+export const useOutsideHandles = ():
+ | CommonOutsideHandlesContextValue
+ | never => {
+ const contextValue = useContext(context);
+ if (!contextValue)
+ throw new Error('Common OutsideHandles context not defined');
+ return contextValue;
+};
diff --git a/packages/nami/src/features/dapp-outside-handles-provider/OutsideHandlesProvider.tsx b/packages/nami/src/features/dapp-outside-handles-provider/OutsideHandlesProvider.tsx
new file mode 100644
index 000000000..a43863fcb
--- /dev/null
+++ b/packages/nami/src/features/dapp-outside-handles-provider/OutsideHandlesProvider.tsx
@@ -0,0 +1,20 @@
+import type { PropsWithChildren } from 'react';
+import React, { useMemo } from 'react';
+
+import { Provider } from './context';
+
+import type { DappOutsideHandlesContextValue } from './types';
+
+type OutsideHandlesProviderProps =
+ PropsWithChildren;
+
+export const OutsideHandlesProvider = ({
+ children,
+ ...props
+}: Readonly): React.ReactElement => {
+ const contextValue = useMemo(
+ () => props,
+ Object.values(props),
+ );
+ return {children};
+};
diff --git a/packages/nami/src/features/dapp-outside-handles-provider/context.ts b/packages/nami/src/features/dapp-outside-handles-provider/context.ts
new file mode 100644
index 000000000..61e94d7d9
--- /dev/null
+++ b/packages/nami/src/features/dapp-outside-handles-provider/context.ts
@@ -0,0 +1,10 @@
+import { createContext } from 'react';
+
+import type { DappOutsideHandlesContextValue } from './types';
+
+export const context = createContext(
+ // eslint-disable-next-line unicorn/no-null
+ null,
+);
+
+export const { Provider } = context;
diff --git a/packages/nami/src/features/dapp-outside-handles-provider/index.ts b/packages/nami/src/features/dapp-outside-handles-provider/index.ts
new file mode 100644
index 000000000..cf4b34f74
--- /dev/null
+++ b/packages/nami/src/features/dapp-outside-handles-provider/index.ts
@@ -0,0 +1,3 @@
+export { OutsideHandlesProvider as DappOutsideHandlesProvider } from './OutsideHandlesProvider';
+export { useOutsideHandles as useDappOutsideHandles } from './useOutsideHandles';
+export * from './types';
diff --git a/packages/nami/src/features/dapp-outside-handles-provider/types.ts b/packages/nami/src/features/dapp-outside-handles-provider/types.ts
new file mode 100644
index 000000000..93bbb3919
--- /dev/null
+++ b/packages/nami/src/features/dapp-outside-handles-provider/types.ts
@@ -0,0 +1,60 @@
+import type { Serialization } from '@cardano-sdk/core';
+import type { Cip30DataSignature } from '@cardano-sdk/dapp-connector';
+import type {
+ WalletManagerApi,
+ WalletRepositoryApi,
+} from '@cardano-sdk/web-extension';
+import type { Wallet } from '@lace/cardano';
+import type { HexBlob } from '@lace/cardano/dist/wallet';
+
+export interface DappConnector {
+ getDappInfo: () => Promise;
+ authorizeDapp: (
+ authorization: 'allow' | 'deny',
+ url: string,
+ onCleanup: () => void,
+ ) => void;
+ getSignTxRequest: () => Promise<{
+ dappInfo: Wallet.DappInfo;
+ request: {
+ data: {
+ tx: Serialization.TxCBOR;
+ addresses: Wallet.KeyManagement.GroupedAddress[];
+ };
+ reject: (onCleanup: () => void) => Promise;
+ sign: (password: string) => Promise;
+ };
+ }>;
+ getSignDataRequest: () => Promise<{
+ dappInfo: Wallet.DappInfo;
+ request: {
+ data: {
+ payload: HexBlob;
+ address:
+ | Wallet.Cardano.DRepID
+ | Wallet.Cardano.PaymentAddress
+ | Wallet.Cardano.RewardAccount;
+ };
+ reject: (onCleanup: () => void) => Promise;
+ sign: (password: string) => Promise;
+ };
+ }>;
+ getAssetInfos: ({
+ assetIds,
+ tx,
+ }: Readonly<{
+ assetIds: Wallet.Cardano.AssetId[];
+ tx: Wallet.Cardano.Tx;
+ }>) => Promise
- ) : (
+ );
+ } else {
+ return (
<>
{
>
- )}
+ );
+ }
+ }
+
+ return (
+
+
+
+ );
+ }, [assets, assetsArray, totalColor, total]);
+
+ return (
+ <>
+
+ {AssetComponent}
@@ -103,8 +121,8 @@ const AssetsViewer = ({ assets }) => {
);
};
-const AssetsGrid = ({ assets }) => {
- const { cardanoCoin } = useOutsideHandles();
+const AssetsGrid = ({ assets }: Readonly<{ assets: NamiAsset[] }>) => {
+ const { cardanoCoin } = useCommonOutsideHandles();
return (
{
0 && 4}
+ mt={(index > 0 && 4) || undefined}
display="flex"
alignItems="center"
justifyContent="center"
@@ -131,10 +149,13 @@ const AssetsGrid = ({ assets }) => {
);
};
-const Search = ({ setSearch, assets }) => {
+const Search = ({
+ setSearch,
+ assets,
+}: Readonly<{ setSearch: (s: string) => void; assets: NamiAsset[] }>) => {
const [input, setInput] = React.useState('');
const iconColor = useColorModeValue('gray.800', 'rgba(255, 255, 255, 0.92)');
- const ref = React.useRef();
+ const ref = React.useRef(null);
React.useEffect(() => {
if (!assets) {
setInput('');
@@ -145,7 +166,7 @@ const Search = ({ setSearch, assets }) => {
setTimeout(() => ref.current.focus())}
+ onOpen={() => setTimeout(() => ref.current?.focus())}
>
{
rounded="md"
placeholder="Search policy, asset, name"
fontSize="xs"
- onInput={(e) => {
- setInput(e.target.value);
+ onInput={e => {
+ setInput((e.target as HTMLInputElement).value);
}}
- onKeyDown={(e) => {
+ onKeyDown={e => {
if (e.key === 'Enter' && input) setSearch(input);
}}
/>
- setInput('')} />
- }
- />
+
+ {
+ setInput('');
+ }}
+ />
+
{
size="sm"
rounded="md"
color="teal.400"
- onClick={() => input && setSearch(input)}
+ onClick={() => {
+ input && setSearch(input);
+ }}
icon={}
/>
diff --git a/packages/nami/src/ui/app/components/avatarLoader.tsx b/packages/nami/src/ui/app/components/avatarLoader.tsx
index 07220c6d2..7475895f4 100644
--- a/packages/nami/src/ui/app/components/avatarLoader.tsx
+++ b/packages/nami/src/ui/app/components/avatarLoader.tsx
@@ -1,16 +1,14 @@
-import { Box } from '@chakra-ui/react';
import React from 'react';
+
+import { Box } from '@chakra-ui/react';
+
import { avatarToImage } from '../../../api/extension';
const AvatarLoader = ({
avatar,
width,
smallRobot,
-}: {
- avatar?: string;
- width: string;
- smallRobot?: boolean;
-}) => {
+}: Readonly<{ avatar?: string; width: string; smallRobot?: boolean }>) => {
const [loaded, setLoaded] = React.useState('');
const fetchAvatar = async () => {
@@ -30,7 +28,7 @@ const AvatarLoader = ({
backgroundImage={loaded ? `url(${loaded})` : 'none'}
backgroundRepeat={'no-repeat'}
backgroundSize={'cover'}
- >
+ />
);
};
diff --git a/packages/nami/src/ui/app/components/changePasswordModal.tsx b/packages/nami/src/ui/app/components/changePasswordModal.tsx
index 0e8faa94f..fa31a95ce 100644
--- a/packages/nami/src/ui/app/components/changePasswordModal.tsx
+++ b/packages/nami/src/ui/app/components/changePasswordModal.tsx
@@ -1,3 +1,7 @@
+/* eslint-disable unicorn/no-null */
+/* eslint-disable react/no-unescaped-entities */
+import React from 'react';
+
import {
Button,
Box,
@@ -14,22 +18,25 @@ import {
useToast,
useDisclosure,
} from '@chakra-ui/react';
-import React from 'react';
-import { useCaptureEvent } from '../../../features/analytics/hooks';
+
import { Events } from '../../../features/analytics/events';
+import { useCaptureEvent } from '../../../features/analytics/hooks';
+
+interface Props {
+ changePassword: (
+ currentPassword: string,
+ newPassword: string,
+ ) => Promise;
+}
-export const ChangePasswordModal = React.forwardRef<
- {},
- {
- changePassword: (
- currentPassword: string,
- newPassword: string,
- ) => Promise;
- }
->(({ changePassword }, ref) => {
+export interface ChangePasswordModalComponentRef {
+ openModal: () => void;
+}
+
+const ChangePasswordModalComponent = ({ changePassword }: Props, ref) => {
const capture = useCaptureEvent();
- const cancelRef = React.useRef();
- const inputRef = React.useRef();
+ const cancelRef = React.useRef(null);
+ const inputRef = React.useRef(null);
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
@@ -39,7 +46,7 @@ export const ChangePasswordModal = React.forwardRef<
newPassword: '',
repeatPassword: '',
matchingPassword: false,
- passwordLen: null,
+ passwordLen: false,
show: false,
});
@@ -49,13 +56,13 @@ export const ChangePasswordModal = React.forwardRef<
newPassword: '',
repeatPassword: '',
matchingPassword: false,
- passwordLen: null,
+ passwordLen: false,
show: false,
});
}, [isOpen]);
React.useImperativeHandle(ref, () => ({
- openModal() {
+ openModal: () => {
onOpen();
},
}));
@@ -82,9 +89,10 @@ export const ChangePasswordModal = React.forwardRef<
capture(Events.SettingsChangePasswordConfirm);
onClose();
- } catch (e) {
+ } catch (error) {
toast({
- title: e instanceof Error ? e.message : 'Password update failed!',
+ title:
+ error instanceof Error ? error.message : 'Password update failed!',
status: 'error',
duration: 5000,
});
@@ -118,16 +126,18 @@ export const ChangePasswordModal = React.forwardRef<
variant="filled"
pr="4.5rem"
type={state.show ? 'text' : 'password'}
- onChange={e =>
- setState(s => ({ ...s, currentPassword: e.target.value }))
- }
+ onChange={e => {
+ setState(s => ({ ...s, currentPassword: e.target.value }));
+ }}
placeholder="Enter current password"
/>
@@ -145,24 +155,26 @@ export const ChangePasswordModal = React.forwardRef<
pr="4.5rem"
isInvalid={state.passwordLen === false}
type={state.show ? 'text' : 'password'}
- onChange={e =>
- setState(s => ({ ...s, newPassword: e.target.value }))
- }
- onBlur={e =>
+ onChange={e => {
+ setState(s => ({ ...s, newPassword: e.target.value }));
+ }}
+ onBlur={e => {
setState(s => ({
...s,
passwordLen: e.target.value
? e.target.value.length >= 8
- : null,
- }))
- }
+ : false,
+ }));
+ }}
placeholder="Enter new password"
/>
@@ -184,21 +196,23 @@ export const ChangePasswordModal = React.forwardRef<
focusBorderColor="teal.400"
variant="filled"
isInvalid={
- state.repeatPassword &&
+ !!state.repeatPassword &&
state.newPassword !== state.repeatPassword
}
pr="4.5rem"
type={state.show ? 'text' : 'password'}
- onChange={e =>
- setState(s => ({ ...s, repeatPassword: e.target.value }))
- }
+ onChange={e => {
+ setState(s => ({ ...s, repeatPassword: e.target.value }));
+ }}
placeholder="Repeat new password"
/>
@@ -235,4 +249,8 @@ export const ChangePasswordModal = React.forwardRef<
);
-});
+};
+
+export const ChangePasswordModal = React.forwardRef(
+ ChangePasswordModalComponent,
+);
diff --git a/packages/nami/src/ui/app/components/collectible.tsx b/packages/nami/src/ui/app/components/collectible.tsx
index b5d1d2f8f..d83bd1109 100644
--- a/packages/nami/src/ui/app/components/collectible.tsx
+++ b/packages/nami/src/ui/app/components/collectible.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable @typescript-eslint/naming-convention */
+import React from 'react';
+
import {
Box,
Avatar,
@@ -5,127 +8,137 @@ import {
Skeleton,
useColorModeValue,
} from '@chakra-ui/react';
-import React from 'react';
+
import './styles.css';
-import { useCaptureEvent } from '../../../features/analytics/hooks';
import { Events } from '../../../features/analytics/events';
+import { useCaptureEvent } from '../../../features/analytics/hooks';
const useIsMounted = () => {
const isMounted = React.useRef(false);
- React.useEffect(() => {
+ React.useEffect((): (() => void) => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
return isMounted;
};
-const Collectible = React.forwardRef(({ asset, ...props }, ref) => {
+const CollectibleComponent = ({ asset, ...props }, ref) => {
const capture = useCaptureEvent();
const background = useColorModeValue('gray.300', 'white');
const [showInfo, setShowInfo] = React.useState(false);
return (
+ {
+ capture(Events.NFTsImageClick);
+ asset && ref.current?.openModal(asset);
+ }}
+ position="relative"
+ display="flex"
+ alignItems="center"
+ justifyContent="center"
+ flexDirection="column"
+ width="160px"
+ height="160px"
+ overflow="hidden"
+ rounded="3xl"
+ background={background}
+ border="solid 1px"
+ borderColor={background}
+ onMouseEnter={() => {
+ setShowInfo(true);
+ }}
+ onMouseLeave={() => {
+ setShowInfo(false);
+ }}
+ cursor="pointer"
+ userSelect="none"
+ data-testid={props.testId}
+ >
{
- capture(Events.NFTsImageClick);
- asset && ref.current.openModal(asset);
- }}
- position="relative"
+ filter={(showInfo && 'brightness(0.6)') || undefined}
+ width="180%"
display="flex"
alignItems="center"
justifyContent="center"
- flexDirection="column"
- width="160px"
- height="160px"
- overflow="hidden"
- rounded="3xl"
- background={background}
- border="solid 1px"
- borderColor={background}
- onMouseEnter={() => setShowInfo(true)}
- onMouseLeave={() => setShowInfo(false)}
- cursor="pointer"
- userSelect="none"
- data-testid={props.testId}
>
+ {asset ? (
+
+ ) : (
+
+ )
+ }
+ />
+ ) : (
+
+ )}
+
+ {asset && (
- {!asset ? (
-
- ) : (
-
- ) : (
-
- )
- }
- />
- )}
-
- {asset && (
+ {asset.name}
+
+
-
- {asset.name}
-
-
- x {asset.quantity}
-
+ x {asset.quantity}
- )}
-
+
+ )}
+
);
-});
+};
+
+const Collectible = React.forwardRef(CollectibleComponent);
+
+Collectible.displayName = 'Collectible';
-const Fallback = ({ name }) => {
+const Fallback = ({ name }: Readonly<{ name?: string }>) => {
const [timedOut, setTimedOut] = React.useState(false);
const isMounted = useIsMounted();
React.useEffect(() => {
- setTimeout(() => isMounted.current && setTimedOut(true), 30000);
+ setTimeout(() => {
+ isMounted.current && setTimedOut(true);
+ }, 30_000);
}, []);
if (timedOut) return ;
return ;
diff --git a/packages/nami/src/ui/app/components/collectiblesViewer.tsx b/packages/nami/src/ui/app/components/collectiblesViewer.tsx
index 15b20709d..fcf9a8380 100644
--- a/packages/nami/src/ui/app/components/collectiblesViewer.tsx
+++ b/packages/nami/src/ui/app/components/collectiblesViewer.tsx
@@ -1,3 +1,8 @@
+/* eslint-disable @typescript-eslint/naming-convention */
+/* eslint-disable unicorn/no-null */
+import React, { useRef } from 'react';
+
+import { SearchIcon, SmallCloseIcon } from '@chakra-ui/icons';
import {
Box,
SimpleGrid,
@@ -21,28 +26,31 @@ import {
useColorModeValue,
Button,
} from '@chakra-ui/react';
-import { SearchIcon, SmallCloseIcon } from '@chakra-ui/icons';
-import React, { useRef } from 'react';
+import { BsArrowUpRight } from 'react-icons/bs';
import { Planet } from 'react-kawaii';
-import Collectible from './collectible';
import { LazyLoadComponent } from 'react-lazy-load-image-component';
-import './styles.css';
-import Copy from './copy';
import { useHistory } from 'react-router-dom';
-import { BsArrowUpRight } from 'react-icons/bs';
-import { useStoreActions, useStoreState } from '../../store';
-import { useCaptureEvent } from '../../../features/analytics/hooks';
-import { Events } from '../../../features/analytics/events';
-import { Asset } from '../../../types/assets';
+
import { searchTokens } from '../../../adapters/assets';
+import { Events } from '../../../features/analytics/events';
+import { useCaptureEvent } from '../../../features/analytics/hooks';
+import { useStoreActions, useStoreState } from '../../store';
+
+import Collectible from './collectible';
+import './styles.css';
+import Copy from './copy';
+
+import type { Asset as NamiAsset } from '../../../types/assets';
interface Props {
- assets: Asset[];
+ assets: NamiAsset[];
setAvatar: (image: string) => void;
}
const CollectiblesViewer = ({ assets, setAvatar }: Readonly) => {
- const [assetsArray, setAssetsArray] = React.useState(null);
+ const [assetsArray, setAssetsArray] = React.useState(
+ null,
+ );
const [search, setSearch] = React.useState('');
const [total, setTotal] = React.useState(0);
const ref = useRef();
@@ -55,7 +63,11 @@ const CollectiblesViewer = ({ assets, setAvatar }: Readonly) => {
return;
}
setAssetsArray(null);
- await new Promise((res, rej) => setTimeout(() => res(), 10));
+ await new Promise(res =>
+ setTimeout(() => {
+ res(void 0);
+ }, 10),
+ );
const filteredAssets = searchTokens(assets, search);
setTotal(filteredAssets.length);
setAssetsArray(filteredAssets);
@@ -79,7 +91,32 @@ const CollectiblesViewer = ({ assets, setAvatar }: Readonly) => {
return (
<>
- {!(assets && assetsArray) ? (
+ {assets && assetsArray ? (
+ assetsArray.length <= 0 ? (
+
+
+
+
+ No Collectibles
+
+
+ ) : (
+ <>
+
+ {total} {total == 1 ? 'Collectible' : 'Collectibles'}
+
+
+
+ >
+ )
+ ) : (
) => {
>
- ) : assetsArray.length <= 0 ? (
-
-
-
-
- No Collectibles
-
-
- ) : (
- <>
-
- {total} {total == 1 ? 'Collectible' : 'Collectibles'}
-
-
-
- >
)}
@@ -121,9 +135,16 @@ const CollectiblesViewer = ({ assets, setAvatar }: Readonly) => {
);
};
-export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
+interface CollectibleModalProps {
+ onUpdateAvatar: (s: string) => Promise;
+}
+
+export const CollectibleModalComponent = (
+ { onUpdateAvatar }: CollectibleModalProps,
+ ref,
+) => {
const { isOpen, onOpen, onClose } = useDisclosure();
- const [asset, setAsset] = React.useState(null);
+ const [asset, setAsset] = React.useState(null);
const [fallback, setFallback] = React.useState(false); // remove short flickering where image is not instantly loaded
const background = useColorModeValue('white', 'gray.800');
const dividerColor = useColorModeValue('gray.200', 'gray.700');
@@ -133,15 +154,17 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
];
const history = useHistory();
const navigate = history.push;
- const timer = React.useRef();
+ const timer = React.useRef();
React.useImperativeHandle(ref, () => ({
- openModal(asset) {
+ openModal: asset => {
setAsset(asset);
- timer.current = setTimeout(() => setFallback(true));
+ timer.current = setTimeout(() => {
+ setFallback(true);
+ });
onOpen();
},
- closeModal() {
+ closeModal: () => {
clearTimeout(timer.current);
setFallback(false);
onClose();
@@ -149,7 +172,7 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
}));
return (
{
{asset && (
@@ -176,14 +199,15 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
width="full"
objectFit="contain"
fallback={
- fallback && (
+ (fallback && (
- )
+ )) ||
+ undefined
}
/>
) : (
@@ -213,7 +237,7 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
top="22px"
size="xs"
onClick={async () => {
- await onUpdateAvatar(asset.image);
+ await onUpdateAvatar(asset.image ?? '');
}}
>
As Avatar
@@ -226,7 +250,7 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
size="xs"
rightIcon={}
onClick={e => {
- setValue({ ...value, assets: [asset] });
+ setValue({ ...value, assets: [{ ...asset, input: '' }] });
navigate('/send');
}}
>
@@ -242,7 +266,12 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
Policy
- e.stopPropagation()}>
+ {
+ e.stopPropagation();
+ }}
+ >
{asset.policy}{' '}
@@ -254,7 +283,12 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
Asset
- e.stopPropagation()}>
+ {
+ e.stopPropagation();
+ }}
+ >
{asset.fingerprint}
@@ -265,36 +299,47 @@ export const CollectibleModal = React.forwardRef(({ onUpdateAvatar }, ref) => {
)}
);
-});
+};
-const AssetsGrid = React.forwardRef(({ assets }, ref) => {
- return (
-
-
- {assets.map((asset) => (
-
-
-
-
-
- ))}
-
-
- );
-});
+const CollectibleModal = React.forwardRef(CollectibleModalComponent);
-const Search = ({ setSearch, assets }) => {
+CollectibleModal.displayName = 'CollectibleModal';
+
+const AssetsGrid = React.forwardRef(
+ ({ assets }: Readonly<{ assets: NamiAsset[] }>, ref) => {
+ return (
+
+
+ {assets.map(asset => (
+
+
+
+
+
+ ))}
+
+
+ );
+ },
+);
+
+AssetsGrid.displayName = 'AssetsGrid';
+
+const Search = ({
+ setSearch,
+ assets,
+}: Readonly<{ setSearch: (s: string) => void; assets: NamiAsset[] }>) => {
const [input, setInput] = React.useState('');
- const ref = React.useRef();
+ const ref = React.useRef(null);
React.useEffect(() => {
if (!assets) {
setInput('');
@@ -305,7 +350,7 @@ const Search = ({ setSearch, assets }) => {
setTimeout(() => ref.current.focus())}
+ onOpen={() => setTimeout(() => ref.current?.focus())}
>
{
placeholder="Search policy, asset, name"
fontSize="xs"
onInput={e => {
- setInput(e.target.value);
+ setInput((e.target as HTMLInputElement).value);
}}
onKeyDown={e => {
if (e.key === 'Enter' && input) setSearch(input);
}}
/>
- setInput('')} />
- }
- />
+
+ {
+ setInput('');
+ }}
+ />
+
{
size="sm"
rounded="md"
color="teal.400"
- onClick={() => input && setSearch(input)}
+ onClick={() => {
+ input && setSearch(input);
+ }}
icon={}
/>
@@ -363,4 +413,6 @@ const Search = ({ setSearch, assets }) => {
);
};
+Search.displayName = 'Search';
+
export default CollectiblesViewer;
diff --git a/packages/nami/src/ui/app/components/confirmModal.tsx b/packages/nami/src/ui/app/components/confirmModal.tsx
index 69d0c4beb..e475d8b99 100644
--- a/packages/nami/src/ui/app/components/confirmModal.tsx
+++ b/packages/nami/src/ui/app/components/confirmModal.tsx
@@ -1,4 +1,8 @@
-import type { PasswordObj as Password } from '@lace/core';
+/* eslint-disable unicorn/prefer-logical-operator-over-ternary */
+/* eslint-disable @typescript-eslint/naming-convention */
+import React from 'react';
+
+import { WalletType } from '@cardano-sdk/web-extension';
import {
Icon,
Box,
@@ -15,78 +19,125 @@ import {
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
-import React from 'react';
import { MdUsb } from 'react-icons/md';
-import { indexToHw, initHW, isHW } from '../../../api/extension';
-import { ERROR, HW } from '../../../config/config';
+
+import { ERROR } from '../../../config/config';
+
+import type { PasswordObj as Password } from '@lace/core';
+import { useCaptureEvent } from '../../../features/analytics/hooks';
+import { Events } from '../../../features/analytics/events';
interface Props {
- ready: boolean;
- onConfirm: (status: boolean, tx: string) => void;
- sign: (password: string, hw: object) => Promise;
- setPassword: (pw: Readonly>) => void;
- onCloseBtn: () => void;
- title: React.ReactNode;
- info: React.ReactNode;
+ ready?: boolean;
+ onConfirm: (status: boolean, error?: string) => Promise | void;
+ sign: (password?: string) => Promise;
+ setPassword?: (pw: Readonly>) => void;
+ onCloseBtn?: () => void;
+ title?: React.ReactNode;
+ info?: React.ReactNode;
+ walletType: WalletType;
+ openHWFlow?: (path: string) => void;
+ getCbor?: () => Promise;
+ setCollateral?: boolean;
+ isPopup?: boolean;
}
-const ConfirmModal = React.forwardRef(
- ({ ready, onConfirm, sign, onCloseBtn, title, info, setPassword }, ref) => {
- const {
- isOpen: isOpenNormal,
- onOpen: onOpenNormal,
- onClose: onCloseNormal,
- } = useDisclosure();
- const {
- isOpen: isOpenHW,
- onOpen: onOpenHW,
- onClose: onCloseHW,
- } = useDisclosure();
- const props = {
- ready,
- onConfirm,
- sign,
- onCloseBtn,
- title,
- info,
- };
- const [hw, setHw] = React.useState('');
+export interface ConfirmModalRef {
+ openModal: () => void;
+ closeModal: () => void;
+}
- React.useImperativeHandle(ref, () => ({
- openModal(accountIndex) {
- if (isHW(accountIndex)) {
- setHw(indexToHw(accountIndex));
- onOpenHW();
- } else {
- onOpenNormal();
- }
- },
- closeModal() {
- onCloseNormal();
- onCloseHW();
- },
- }));
+const ConfirmModal = (
+ {
+ ready,
+ onConfirm,
+ sign,
+ onCloseBtn,
+ title,
+ info,
+ setPassword,
+ walletType,
+ openHWFlow,
+ getCbor,
+ setCollateral,
+ isPopup,
+ }: Readonly,
+ ref,
+) => {
+ const {
+ isOpen: isOpenNormal,
+ onOpen: onOpenNormal,
+ onClose: onCloseNormal,
+ } = useDisclosure();
+ const {
+ isOpen: isOpenHW,
+ onOpen: onOpenHW,
+ onClose: onCloseHW,
+ } = useDisclosure();
+ const props = {
+ ready,
+ onConfirm,
+ sign,
+ onCloseBtn,
+ title,
+ info,
+ walletType,
+ setCollateral,
+ isPopup,
+ };
+ React.useImperativeHandle(ref, () => ({
+ openModal: () => {
+ if (
+ walletType === WalletType.Ledger ||
+ walletType === WalletType.Trezor
+ ) {
+ onOpenHW();
+ } else {
+ onOpenNormal();
+ }
+ },
+ closeModal: () => {
+ onCloseNormal();
+ onCloseHW();
+ },
+ }));
- return (
- <>
+ return (
+ <>
+ {typeof openHWFlow === 'function' &&
+ [WalletType.Ledger, WalletType.Trezor].includes(walletType) ? (
+ ) : (
- >
- );
- }
-);
+ )}
+ >
+ );
+};
-const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
+interface ConfirmModalNormalProps {
+ isOpen?: boolean;
+ onClose: () => void;
+ setPassword?: (pw: Readonly>) => void;
+ props: Props;
+}
+
+const ConfirmModalNormal = ({
+ props,
+ isOpen,
+ onClose,
+ setPassword,
+}: Readonly) => {
const [state, setState] = React.useState({
wrongPassword: false,
password: '',
@@ -94,7 +145,7 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
name: '',
});
const [waitReady, setWaitReady] = React.useState(true);
- const inputRef = React.useRef();
+ const inputRef = React.useRef(null);
React.useEffect(() => {
setState({
@@ -109,13 +160,20 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
if (!state.password || props.ready === false || !waitReady) return;
try {
setWaitReady(false);
- const signedMessage = await props.sign(state.password);
- await props.onConfirm(true, signedMessage);
+ await props.sign(state.password);
+ await props?.onConfirm(true);
onClose?.();
- } catch (e) {
- if (e === ERROR.wrongPassword || e.name === 'AuthenticationError')
- setState((s) => ({ ...s, wrongPassword: true }));
- else await props.onConfirm(false, e);
+ } catch (error) {
+ if (
+ error === ERROR.wrongPassword ||
+ (error instanceof Error && error.name === 'AuthenticationError')
+ )
+ setState(s => ({ ...s, wrongPassword: true }));
+ else
+ await props.onConfirm(
+ false,
+ error instanceof Error ? error.name : (error || '').toString(),
+ );
}
setWaitReady(true);
};
@@ -123,17 +181,16 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
return (
{
if (props.onCloseBtn) {
props.onCloseBtn();
}
- onClose()
+ onClose();
}}
isCentered
initialFocusRef={inputRef}
blockScrollOnMount={false}
- // styleConfig={{maxWidth: '100%'}}
>
@@ -147,14 +204,14 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
ref={inputRef}
focusBorderColor="teal.400"
variant="filled"
- isInvalid={state.wrongPassword === true}
+ isInvalid={state.wrongPassword}
pr="4.5rem"
type={state.show ? 'text' : 'password'}
- onChange={(e) => {
+ onChange={e => {
setPassword?.(e.target);
- setState((s) => ({ ...s, password: e.target.value }));
+ setState(s => ({ ...s, password: e.target.value }));
}}
- onKeyDown={(e) => {
+ onKeyDown={e => {
if (e.key == 'Enter') confirmHandler();
}}
placeholder="Enter password"
@@ -163,13 +220,15 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
- {state.wrongPassword === true && (
+ {state.wrongPassword && (
Password is wrong
)}
@@ -201,22 +260,57 @@ const ConfirmModalNormal = ({ props, isOpen, onClose, setPassword }) => {
);
};
-const ConfirmModalHw = ({ props, isOpen, onClose, hw }) => {
+interface ConfirmModalHwProps {
+ isOpen?: boolean;
+ onClose: () => void;
+ openHWFlow: (path: string) => void;
+ getCbor?: () => Promise;
+ props: Props;
+}
+
+const ConfirmModalHw = ({
+ props,
+ isOpen,
+ getCbor,
+ openHWFlow,
+ onClose,
+}: Readonly) => {
const [waitReady, setWaitReady] = React.useState(true);
const [error, setError] = React.useState('');
+ const capture = useCaptureEvent();
const confirmHandler = async () => {
- if (props.ready === false || !waitReady) return;
- try {
+ if (
+ props.walletType === WalletType.Trezor &&
+ props.isPopup &&
+ typeof getCbor === 'function'
+ ) {
+ const cbor = await getCbor();
+
+ if (cbor === '') {
+ setError('An error occurred');
+ return;
+ }
+
+ if (props.setCollateral) {
+ openHWFlow(`/hwTab/trezorTx/${cbor}/${props.setCollateral}`);
+ } else {
+ openHWFlow(`/hwTab/trezorTx/${cbor}`);
+ }
+ } else {
+ if (props.ready === false || !waitReady) return;
setWaitReady(false);
- const appAda = await initHW({ device: hw.device, id: hw.id });
- const signedMessage = await props.sign(null, { ...hw, appAda });
- await props.onConfirm(true, signedMessage);
- } catch (e) {
- if (e === ERROR.submit) props.onConfirm(false, e);
- else setError('An error occured');
+ try {
+ await props.sign();
+ await props.onConfirm(true);
+ capture(Events.SendTransactionConfirmed);
+ } catch (error_) {
+ console.error(error_);
+ if (error_ === ERROR.submit) props.onConfirm(false, error_);
+ else setError('An error occured');
+ }
+ setWaitReady(true);
}
- setWaitReady(true);
};
React.useEffect(() => {
@@ -227,7 +321,7 @@ const ConfirmModalHw = ({ props, isOpen, onClose, hw }) => {
<>
{
display="flex"
alignItems="center"
justifyContent="center"
- background={hw.device == HW.ledger ? 'blue.400' : 'green.400'}
+ background={
+ props.walletType === WalletType.Ledger
+ ? 'blue.400'
+ : 'green.400'
+ }
rounded="xl"
py={2}
width="70%"
@@ -258,11 +356,9 @@ const ConfirmModalHw = ({ props, isOpen, onClose, hw }) => {
>
- {!waitReady
- ? `Waiting for ${
- hw.device == HW.ledger ? 'Ledger' : 'Trezor'
- }`
- : `Connect ${hw.device == HW.ledger ? 'Ledger' : 'Trezor'}`}
+ {waitReady
+ ? `Connect ${props.walletType}`
+ : `Waiting for ${props.walletType}`}
{error && (
@@ -301,4 +397,4 @@ const ConfirmModalHw = ({ props, isOpen, onClose, hw }) => {
);
};
-export default ConfirmModal;
+export default React.forwardRef(ConfirmModal);
diff --git a/packages/nami/src/ui/app/components/copy.tsx b/packages/nami/src/ui/app/components/copy.tsx
index 32d28070c..55818a36f 100644
--- a/packages/nami/src/ui/app/components/copy.tsx
+++ b/packages/nami/src/ui/app/components/copy.tsx
@@ -1,7 +1,16 @@
-import { Box, Tooltip } from '@chakra-ui/react';
+/* eslint-disable @typescript-eslint/naming-convention */
import React from 'react';
-const Copy = ({ label, copy, onClick, ...props }) => {
+import { Box, Tooltip } from '@chakra-ui/react';
+
+interface Props {
+ label?: React.ReactNode;
+ copy: string;
+ onClick?: () => void;
+ children?: React.ReactNode;
+}
+
+const Copy = ({ label, copy, onClick, ...props }: Readonly) => {
const [copied, setCopied] = React.useState(false);
return (
@@ -11,7 +20,9 @@ const Copy = ({ label, copy, onClick, ...props }) => {
if (onClick) onClick();
navigator.clipboard.writeText(copy);
setCopied(true);
- setTimeout(() => setCopied(false), 800);
+ setTimeout(() => {
+ setCopied(false);
+ }, 800);
}}
>
{props.children}
diff --git a/packages/nami/src/ui/app/components/historyViewer.tsx b/packages/nami/src/ui/app/components/historyViewer.tsx
index 7619b4f7a..c679722db 100644
--- a/packages/nami/src/ui/app/components/historyViewer.tsx
+++ b/packages/nami/src/ui/app/components/historyViewer.tsx
@@ -1,5 +1,5 @@
/* eslint-disable react/no-multi-comp */
-import React from 'react';
+import React, { useMemo } from 'react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { Box, Text, Spinner, Accordion, Button } from '@chakra-ui/react';
@@ -7,6 +7,7 @@ import { File } from 'react-kawaii';
import { Events } from '../../../features/analytics/events';
import { useCaptureEvent } from '../../../features/analytics/hooks';
+import { useCommonOutsideHandles } from '../../../features/common-outside-handles-provider';
import { useOutsideHandles } from '../../../features/outside-handles-provider/useOutsideHandles';
import Transaction from './transaction';
@@ -17,99 +18,99 @@ const BATCH = 5;
const HistoryViewer = () => {
const capture = useCaptureEvent();
- const { cardanoCoin, transactions, environmentName, openExternalLink } =
+ const { transactions, environmentName, openExternalLink } =
useOutsideHandles();
+
+ const { cardanoCoin } = useCommonOutsideHandles();
const [historySlice, setHistorySlice] = React.useState<
- Wallet.Cardano.HydratedTx[] | undefined
+ (Wallet.Cardano.HydratedTx | Wallet.TxInFlight)[] | undefined
>();
const [page, setPage] = React.useState(1);
const [isFinal, setIsFinal] = React.useState(false);
const getTxs = () => {
if (!transactions) return;
- const slice = (historySlice ?? []).concat(
- transactions.slice((page - 1) * BATCH, page * BATCH),
- );
+ const slice = transactions.slice(0, page * BATCH);
if (slice.length < page * BATCH) setIsFinal(true);
setHistorySlice(slice);
};
React.useEffect(() => {
getTxs();
- }, [page]);
+ }, [page, transactions]);
- return (
-
- {historySlice ? (
- historySlice.length <= 0 ? (
-
+ historySlice && historySlice.length <= 0 ? (
+
+
+
+
+ No History
+
+
+ ) : (
+ <>
+ {
+ void capture(Events.ActivityActivityActivityRowClick);
+ }}
>
-
-
-
- No History
-
-
- ) : (
- <>
- {
- void capture(Events.ActivityActivityActivityRowClick);
- }}
+ {historySlice?.map(tx => (
+
+ ))}
+
+ {isFinal ? (
+
- {historySlice.map(tx => (
-
- ))}
-
- {isFinal ? (
-
+ ) : (
+
+
- ) : (
-
-
-
- )}
- >
- )
- ) : (
-
- )}
-
+
+
+
+ )}
+ >
+ ),
+ [historySlice, setPage, openExternalLink, capture],
+ );
+
+ return (
+ {historySlice ? history : }
);
};
diff --git a/packages/nami/src/ui/app/components/laceSecondaryButton.tsx b/packages/nami/src/ui/app/components/laceSecondaryButton.tsx
index a5229b94a..8169151c6 100644
--- a/packages/nami/src/ui/app/components/laceSecondaryButton.tsx
+++ b/packages/nami/src/ui/app/components/laceSecondaryButton.tsx
@@ -1,22 +1,26 @@
import React from 'react';
+
import { Button, Text } from '@chakra-ui/react';
-type Props = {
- children: string;
+interface Props {
+ children: React.ReactNode;
onClick?: () => void;
-};
+}
-const LaceSecondaryButton = ({ children, onClick }: Props) => {
+const LaceSecondaryButton = ({ children, onClick }: Readonly) => {
return (
);
};
diff --git a/packages/nami/src/ui/app/components/privacyPolicy.tsx b/packages/nami/src/ui/app/components/privacyPolicy.tsx
index 5c8df6680..90f64db1d 100644
--- a/packages/nami/src/ui/app/components/privacyPolicy.tsx
+++ b/packages/nami/src/ui/app/components/privacyPolicy.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+
import {
Box,
Text,
@@ -13,15 +14,21 @@ import {
ListItem,
Link,
} from '@chakra-ui/react';
+
import { Scrollbars } from './scrollbar';
-const PrivacyPolicy = React.forwardRef((props, ref) => {
+export interface PrivacyPolicyRef {
+ openModal: () => void;
+ closeModal: () => void;
+}
+
+const PrivacyPolicy = React.forwardRef((_props, ref) => {
const { isOpen, onOpen, onClose } = useDisclosure();
React.useImperativeHandle(ref, () => ({
- openModal() {
+ openModal: () => {
onOpen();
},
- closeModal() {
+ closeModal: () => {
onClose();
},
}));
@@ -588,8 +595,8 @@ const PrivacyPolicy = React.forwardRef((props, ref) => {
IOG may update this Privacy Policy from time to time. Such
changes will be posted on this page. The effective date of such
changes will be notified via email and/or a prominent notice on
- the Product, with an update to the "effective date" at the top of
- this Privacy Policy.
+ the Product, with an update to the "effective date" at the top
+ of this Privacy Policy.
You are advised to review this Privacy Policy periodically for
@@ -644,4 +651,6 @@ const PrivacyPolicy = React.forwardRef((props, ref) => {
);
});
+PrivacyPolicy.displayName = 'PrivacyPolicy';
+
export default PrivacyPolicy;
diff --git a/packages/nami/src/ui/app/components/qrCode.tsx b/packages/nami/src/ui/app/components/qrCode.tsx
index 24bbc2ca9..b6cb6bf9f 100644
--- a/packages/nami/src/ui/app/components/qrCode.tsx
+++ b/packages/nami/src/ui/app/components/qrCode.tsx
@@ -1,7 +1,9 @@
import React from 'react';
+
+import { useColorModeValue } from '@chakra-ui/react';
import QRCodeStyling from 'qr-code-styling';
+
import Ada from '../../../assets/img/ada.png';
-import { useColorModeValue } from '@chakra-ui/react';
const qrCode = new QRCodeStyling({
width: 150,
@@ -18,16 +20,16 @@ const qrCode = new QRCodeStyling({
},
});
-const QrCode = ({ value }) => {
- const ref = React.useRef(null);
+const QrCode = ({ value }: Readonly<{ value?: string }>) => {
+ const ref = React.useRef(null);
const bgColor = useColorModeValue('white', '#2D3748');
const contentColor = useColorModeValue(
{ corner: '#DD6B20', dots: '#319795' },
- { corner: '#FBD38D', dots: '#81E6D9' }
+ { corner: '#FBD38D', dots: '#81E6D9' },
);
React.useEffect(() => {
- qrCode.append(ref.current);
+ ref.current && qrCode.append(ref.current);
}, []);
React.useEffect(() => {
diff --git a/packages/nami/src/ui/app/components/switchToLaceBanner.tsx b/packages/nami/src/ui/app/components/switchToLaceBanner.tsx
index 5a9b4fd7b..2fb47ec2f 100644
--- a/packages/nami/src/ui/app/components/switchToLaceBanner.tsx
+++ b/packages/nami/src/ui/app/components/switchToLaceBanner.tsx
@@ -14,9 +14,11 @@ import { useStoreActions, useStoreState } from '../../store';
import LaceSecondaryButton from './laceSecondaryButton';
export const getLaceVideoBackgroundSrc = () => {
- return typeof chrome !== 'undefined' &&
- chrome.runtime?.getURL('laceVideoBackground.mp4')
- || laceVideoBackground;
+ return (
+ (typeof chrome !== 'undefined' &&
+ chrome.runtime?.getURL('laceVideoBackground.mp4')) ||
+ laceVideoBackground
+ );
};
interface Props {
@@ -154,7 +156,7 @@ export const SwitchToLaceBanner = ({ switchWalletMode }: Props) => {
bgClip="text"
fontWeight="extrabold"
>
- Your Nami wallet evolved!
+ Upgrade your wallet!
{
}}
>
- Enable Lace Mode to unlock access to new and exciting Web
- 3 features
+ Enable Lace mode to access new and exciting Web3 features.
- You can return to the "Nami Mode" at any time
+ You can revert to Nami mode at any time via the Settings
+ page.
@@ -199,7 +201,7 @@ export const SwitchToLaceBanner = ({ switchWalletMode }: Props) => {
bgGradient="linear(180deg, transparent, rgba(255, 255, 255, 0.9) 50%)"
/>