mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-04-29 13:15:32 +08:00
Merge pull request #4080 from hirosystems/release/rainy-monday
Release/rainy monday
This commit is contained in:
@@ -3,7 +3,7 @@ LABEL maintainer="ux@blockstack.com"
|
||||
|
||||
ARG NODE_VERSION=12
|
||||
ENV MINIFY_PRODUCTION_BUILD=true
|
||||
ENV EXT_ENV="prod"
|
||||
ENV WALLET_ENVIRONMENT="production"
|
||||
ENV NODE_VERSION=${NODE_VERSION}
|
||||
WORKDIR /src
|
||||
|
||||
|
||||
36
package.json
36
package.json
@@ -5,17 +5,17 @@
|
||||
"version": "6.1.0",
|
||||
"author": "Hiro Systems PBC",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development concurrently --raw \"node webpack/dev-server.js\" \"redux-devtools --hostname=localhost --port=8000\"",
|
||||
"dev": "cross-env WALLET_ENVIRONMENT=development concurrently --raw \"node webpack/dev-server.js\" \"redux-devtools --hostname=localhost --port=8000\"",
|
||||
"dev:test-app": "webpack serve --config test-app/webpack/webpack.config.dev.js",
|
||||
"build": "cross-env NODE_ENV=production EXT_ENV=prod webpack --config webpack/webpack.config.prod.js",
|
||||
"build:analyze": "cross-env ANALYZE=true NODE_ENV=production EXT_ENV=prod webpack --config webpack/webpack.config.prod.js",
|
||||
"build:dev": "cross-env NODE_ENV=development EXT_ENV=development webpack --config webpack/webpack.config.dev.js",
|
||||
"build:ext:test": "cross-env NODE_ENV=production TEST_ENV=true EXT_ENV=prod webpack --config webpack/webpack.config.prod.js",
|
||||
"build:ext:test:watch": "cross-env NODE_ENV=production TEST_ENV=true EXT_ENV=prod webpack --config webpack/webpack.config.prod.js --watch",
|
||||
"build:test-app": "cross-env NODE_ENV=production EXT_ENV=prod webpack --config ./test-app/webpack/webpack.config.prod.js",
|
||||
"build": "cross-env WALLET_ENVIRONMENT=production webpack --config webpack/webpack.config.prod.js",
|
||||
"build:analyze": "cross-env ANALYZE=true WALLET_ENVIRONMENT=production webpack --config webpack/webpack.config.prod.js",
|
||||
"build:dev": "cross-env WALLET_ENVIRONMENT=development webpack --config webpack/webpack.config.dev.js",
|
||||
"build:ext:test": "cross-env WALLET_ENVIRONMENT=production TEST_ENV=true webpack --config webpack/webpack.config.prod.js",
|
||||
"build:ext:test:watch": "cross-env WALLET_ENVIRONMENT=production TEST_ENV=true webpack --config webpack/webpack.config.prod.js --watch",
|
||||
"build:test-app": "cross-env WALLET_ENVIRONMENT=production webpack --config ./test-app/webpack/webpack.config.prod.js",
|
||||
"build:test": "concurrently 'yarn build:ext:test' 'yarn build:test-app'",
|
||||
"build:test-api": "concurrently 'yarn build:ext:test' 'yarn build:test-app'",
|
||||
"build:test:watch": "cross-env NODE_ENV=test EXT_ENV=watch webpack --config webpack/webpack.config.prod.js",
|
||||
"build:test:watch": "cross-env WALLET_ENVIRONMENT=testing webpack --config webpack/webpack.config.prod.js",
|
||||
"clean": "rm -rf ./dist",
|
||||
"clean:all": "rm -rf ./dist && rm -rf ./coverage && rm -rf ./node_modules",
|
||||
"lint": "concurrently -g 'yarn lint:prettier' 'yarn lint:unused-exports' 'yarn lint:deps' 'yarn lint:remote-wallet-config' 'yarn lint:eslint' 'yarn lint:filename'",
|
||||
@@ -28,15 +28,15 @@
|
||||
"lint:remote-wallet-config": "npx ajv-cli validate -s config/wallet-config.schema.json -d config/wallet-config.json",
|
||||
"lint:deps": "dependency-cruise --config .dependency-cruiser.js \"src/**/*.{ts,tsx}\"",
|
||||
"prod:ext": "yarn build",
|
||||
"prod:analyze": "cross-env NODE_ENV=production ANALYZE=true webpack -p",
|
||||
"prod:analyze": "cross-env WALLET_ENVIRONMENT=production ANALYZE=true webpack -p",
|
||||
"test:integration": "jest --config=./jest.integration.config.js --verbose=true --runInBand --testPathPattern=./tests-legacy/integration/*",
|
||||
"test:integration:ci": "jest --config=./jest.integration.config.js --testPathPattern=./tests-legacy/integration/*",
|
||||
"test:integration-api": "jest --config=./jest.integration.config.js --verbose=true --runInBand --testPathPattern=./tests-legacy/test-api/*",
|
||||
"test:integration-api:ci": "jest --config=./jest.integration.config.js --testPathPattern=./tests-legacy/test-api/*",
|
||||
"test:unit": "vitest run",
|
||||
"test": "NODE_ENV=test jest --verbose=true",
|
||||
"test:coverage": "NODE_ENV=test jest --collect-coverage",
|
||||
"test:watch": "NODE_ENV=test jest --watch",
|
||||
"test": "WALLET_ENVIRONMENT=testing jest --verbose=true",
|
||||
"test:coverage": "WALLET_ENVIRONMENT=testing jest --collect-coverage",
|
||||
"test:watch": "WALLET_ENVIRONMENT=testing jest --watch",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"license": "MIT",
|
||||
@@ -161,10 +161,10 @@
|
||||
"@stacks/ui-utils": "7.5.0",
|
||||
"@stacks/wallet-sdk": "6.5.4",
|
||||
"@styled-system/theme-get": "5.1.2",
|
||||
"@tanstack/query-sync-storage-persister": "4.29.19",
|
||||
"@tanstack/react-query": "4.29.19",
|
||||
"@tanstack/react-query-devtools": "4.29.19",
|
||||
"@tanstack/react-query-persist-client": "4.29.19",
|
||||
"@tanstack/query-sync-storage-persister": "4.32.0",
|
||||
"@tanstack/react-query": "4.32.0",
|
||||
"@tanstack/react-query-devtools": "4.32.0",
|
||||
"@tanstack/react-query-persist-client": "4.32.0",
|
||||
"@tippyjs/react": "4.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.60.1",
|
||||
"@vkontakte/vk-qr": "2.0.13",
|
||||
@@ -231,7 +231,7 @@
|
||||
"@babel/core": "7.22.5",
|
||||
"@babel/preset-react": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.5",
|
||||
"@btckit/types": "0.0.18",
|
||||
"@btckit/types": "0.0.19",
|
||||
"@emotion/babel-plugin": "11.11.0",
|
||||
"@emotion/babel-preset-css-prop": "11.11.0",
|
||||
"@emotion/cache": "11.11.0",
|
||||
@@ -247,7 +247,7 @@
|
||||
"@stacks/stacks-blockchain-api-types": "6.3.4",
|
||||
"@swc-node/jest": "1.5.6",
|
||||
"@swc/core": "1.3.32",
|
||||
"@trivago/prettier-plugin-sort-imports": "4.1.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "4.2.0",
|
||||
"@types/argon2-browser": "1.18.1",
|
||||
"@types/chroma-js": "2.1.4",
|
||||
"@types/chrome": "0.0.236",
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
const deepMerge = require('deepmerge');
|
||||
|
||||
const IS_DEV = process.env.NODE_ENV === 'development';
|
||||
const IS_DEV = process.env.WALLET_ENVIRONMENT === 'development';
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV ?? 'development';
|
||||
const WALLET_ENVIRONMENT = process.env.WALLET_ENVIRONMENT ?? 'development';
|
||||
|
||||
const PREVIEW_RELEASE = process.env.PREVIEW_RELEASE;
|
||||
|
||||
@@ -78,13 +78,13 @@ const manifest = {
|
||||
},
|
||||
host_permissions: ['*://*/*'],
|
||||
content_security_policy: {
|
||||
extension_pages: contentSecurityPolicyEnvironment[NODE_ENV],
|
||||
extension_pages: contentSecurityPolicyEnvironment[WALLET_ENVIRONMENT],
|
||||
},
|
||||
web_accessible_resources: [{ resources: ['inpage.js'], matches: ['*://*/*'] }],
|
||||
action: {
|
||||
default_title: 'Stacks',
|
||||
default_popup: 'popup.html',
|
||||
default_icon: defaultIconEnvironment[NODE_ENV],
|
||||
default_icon: defaultIconEnvironment[WALLET_ENVIRONMENT],
|
||||
},
|
||||
options_ui: {
|
||||
page: 'index.html',
|
||||
@@ -128,7 +128,7 @@ function generateManifest(packageVersion) {
|
||||
manifest,
|
||||
releaseEnvironmentConfig,
|
||||
browserConfig,
|
||||
environmentIcons[NODE_ENV],
|
||||
environmentIcons[WALLET_ENVIRONMENT],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
import { ensureArray, undefinedIfLengthZero } from '@shared/utils';
|
||||
|
||||
import { useRejectIfLedgerWallet } from '@app/common/rpc-helpers';
|
||||
@@ -23,7 +22,7 @@ export function usePsbtRequestSearchParams() {
|
||||
() => ({
|
||||
appName: payload?.appDetails?.name,
|
||||
allowedSighash: payload?.allowedSighash
|
||||
? undefinedIfLengthZero(payload.allowedSighash.map(h => Number(h)) as btc.SignatureHash[])
|
||||
? undefinedIfLengthZero(payload.allowedSighash.map(h => Number(h)) as AllowedSighashTypes[])
|
||||
: undefined,
|
||||
origin,
|
||||
payload,
|
||||
@@ -54,7 +53,7 @@ export function useRpcSignPsbtParams() {
|
||||
requestId,
|
||||
psbtHex,
|
||||
allowedSighash: undefinedIfLengthZero(
|
||||
allowedSighash.map(h => Number(h)) as btc.SignatureHash[]
|
||||
allowedSighash.map(h => Number(h)) as AllowedSighashTypes[]
|
||||
),
|
||||
signAtIndex: undefinedIfLengthZero(ensureArray(signAtIndex).map(h => Number(h))),
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { FullPageLoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
interface HomeLoaderProps {
|
||||
children(data: StacksAccount): React.JSX.Element;
|
||||
interface CurrentStacksAccountLoaderProps {
|
||||
children(data: StacksAccount): React.ReactNode;
|
||||
}
|
||||
export function HomeLoader({ children }: HomeLoaderProps) {
|
||||
|
||||
export function CurrentStacksAccountLoader({ children }: CurrentStacksAccountLoaderProps) {
|
||||
const currentAccount = useCurrentStacksAccount();
|
||||
if (!currentAccount) return <FullPageLoadingSpinner />;
|
||||
if (!currentAccount) return null;
|
||||
return children(currentAccount);
|
||||
}
|
||||
@@ -1,59 +1,31 @@
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Box, Stack, Text } from '@stacks/ui';
|
||||
import { Box, Stack } from '@stacks/ui';
|
||||
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
|
||||
|
||||
import { LEDGER_BITCOIN_ENABLED } from '@shared/environment';
|
||||
|
||||
import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance';
|
||||
import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance';
|
||||
import { ftDecimals } from '@app/common/stacks-utils';
|
||||
import { useWalletType } from '@app/common/use-wallet-type';
|
||||
import { Brc20TokensLoader } from '@app/components/brc20-tokens-loader';
|
||||
import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item';
|
||||
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
|
||||
import { BtcIcon } from '@app/components/icons/btc-icon';
|
||||
import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { Caption } from '@app/components/typography';
|
||||
import { useStacksFungibleTokenAssetBalancesAnchoredWithMetadata } from '@app/query/stacks/balance/stacks-ft-balances.hooks';
|
||||
import { CurrentStacksAccountLoader } from '@app/components/stacks-account-loader';
|
||||
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
|
||||
import { Collectibles } from '../collectibles/collectibles';
|
||||
import { PendingBrc20TransferList } from '../pending-brc-20-transfers/pending-brc-20-transfers';
|
||||
import { BitcoinFungibleTokenAssetList } from './components/bitcoin-fungible-tokens-asset-list';
|
||||
import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list';
|
||||
import { StacksBalanceItem } from './components/stacks-balance-item';
|
||||
|
||||
interface BalancesListContextState {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export function BalancesList(): React.JSX.Element {
|
||||
const { address } = useOutletContext<BalancesListContextState>();
|
||||
const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(address);
|
||||
export function AssetsList() {
|
||||
const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
|
||||
const {
|
||||
stxEffectiveBalance,
|
||||
stxEffectiveUsdBalance,
|
||||
stxLockedBalance,
|
||||
stxUsdLockedBalance,
|
||||
isLoading,
|
||||
} = useStxBalance();
|
||||
|
||||
const { btcAvailableAssetBalance, btcAvailableUsdBalance } = useBtcAssetBalance(btcAddress);
|
||||
const { whenWallet } = useWalletType();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Better handle loading state
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
|
||||
const stxAdditionalBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? (
|
||||
<Text>({ftDecimals(stxLockedBalance.amount, stxLockedBalance.decimals || 0)} locked)</Text>
|
||||
) : undefined;
|
||||
|
||||
const stxAdditionalUsdBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? (
|
||||
<Caption ml="4px">({stxUsdLockedBalance} locked)</Caption>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<Stack pb="extra-loose" spacing="loose" data-testid={HomePageSelectorsLegacy.BalancesList}>
|
||||
{/* Temporary duplication during Ledger Bitcoin feature dev */}
|
||||
@@ -78,15 +50,11 @@ export function BalancesList(): React.JSX.Element {
|
||||
/>
|
||||
) : null,
|
||||
})}
|
||||
<CryptoCurrencyAssetItem
|
||||
assetBalance={stxEffectiveBalance}
|
||||
usdBalance={stxEffectiveUsdBalance}
|
||||
address={address}
|
||||
additionalBalanceInfo={stxAdditionalBalanceInfo}
|
||||
additionalUsdBalanceInfo={stxAdditionalUsdBalanceInfo}
|
||||
icon={<StxAvatar />}
|
||||
/>
|
||||
<StacksFungibleTokenAssetList assetBalances={stacksFtAssetBalances} />
|
||||
|
||||
<CurrentStacksAccountLoader>
|
||||
{account => <StacksBalanceItem account={account} />}
|
||||
</CurrentStacksAccountLoader>
|
||||
|
||||
{whenWallet({
|
||||
software: (
|
||||
<Brc20TokensLoader>
|
||||
@@ -95,8 +63,12 @@ export function BalancesList(): React.JSX.Element {
|
||||
),
|
||||
ledger: null,
|
||||
})}
|
||||
|
||||
<PendingBrc20TransferList />
|
||||
|
||||
<Collectibles />
|
||||
|
||||
<Outlet />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Text } from '@stacks/ui';
|
||||
|
||||
import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance';
|
||||
import { ftDecimals } from '@app/common/stacks-utils';
|
||||
import { CryptoCurrencyAssetItem } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item';
|
||||
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
|
||||
import { Caption } from '@app/components/typography';
|
||||
import { useStacksFungibleTokenAssetBalancesAnchoredWithMetadata } from '@app/query/stacks/balance/stacks-ft-balances.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import { StacksFungibleTokenAssetList } from './stacks-fungible-token-asset-list';
|
||||
|
||||
interface StacksBalanceItemProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
export function StacksBalanceItem({ account }: StacksBalanceItemProps) {
|
||||
const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(
|
||||
account.address
|
||||
);
|
||||
|
||||
const { stxEffectiveBalance, stxEffectiveUsdBalance, stxLockedBalance, stxUsdLockedBalance } =
|
||||
useStxBalance();
|
||||
|
||||
const stxAdditionalBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? (
|
||||
<Text>({ftDecimals(stxLockedBalance.amount, stxLockedBalance.decimals || 0)} locked)</Text>
|
||||
) : undefined;
|
||||
|
||||
const stxAdditionalUsdBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? (
|
||||
<Caption ml="4px">({stxUsdLockedBalance} locked)</Caption>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
<CryptoCurrencyAssetItem
|
||||
assetBalance={stxEffectiveBalance}
|
||||
usdBalance={stxEffectiveUsdBalance}
|
||||
address={account.address}
|
||||
additionalBalanceInfo={stxAdditionalBalanceInfo}
|
||||
additionalUsdBalanceInfo={stxAdditionalUsdBalanceInfo}
|
||||
icon={<StxAvatar />}
|
||||
/>
|
||||
<StacksFungibleTokenAssetList assetBalances={stacksFtAssetBalances} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { useWalletType } from '@app/common/use-wallet-type';
|
||||
import { CurrentStacksAccountLoader } from '@app/components/stacks-account-loader';
|
||||
import { useConfigNftMetadataEnabled } from '@app/query/common/remote-config/remote-config.query';
|
||||
|
||||
import { AddCollectible } from './components/add-collectible';
|
||||
@@ -44,7 +45,11 @@ export function Collectibles() {
|
||||
ledger: null,
|
||||
})}
|
||||
|
||||
{isNftMetadataEnabled ? <StacksCryptoAssets /> : null}
|
||||
{isNftMetadataEnabled && (
|
||||
<CurrentStacksAccountLoader>
|
||||
{account => <StacksCryptoAssets account={account} />}
|
||||
</CurrentStacksAccountLoader>
|
||||
)}
|
||||
|
||||
{whenWallet({
|
||||
software: (
|
||||
|
||||
@@ -3,14 +3,18 @@ import { useEffect } from 'react';
|
||||
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
|
||||
import { parseIfValidPunycode } from '@app/common/utils';
|
||||
import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks';
|
||||
import { useNonFungibleTokensMetadata } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks';
|
||||
import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import { StacksBnsName } from './stacks-bns-name';
|
||||
import { StacksNonFungibleTokens } from './stacks-non-fungible-tokens';
|
||||
|
||||
export function StacksCryptoAssets() {
|
||||
interface StacksCryptoAssetsProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
export function StacksCryptoAssets({ account }: StacksCryptoAssetsProps) {
|
||||
const { data: names = [] } = useCurrentAccountNames();
|
||||
const stacksNftsMetadataResp = useNonFungibleTokensMetadata();
|
||||
const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(account);
|
||||
const analytics = useAnalytics();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { bitcoinNetworkModeToCoreNetworkMode } from '@shared/crypto/bitcoin/bitc
|
||||
import { LedgerRequestKeysContext } from '@app/features/ledger/generic-flows/request-keys/ledger-request-keys.context';
|
||||
import { useLedgerNavigate } from '@app/features/ledger/hooks/use-ledger-navigate';
|
||||
import { useActionCancellableByUser } from '@app/features/ledger/utils/stacks-ledger-utils';
|
||||
import { bitcoinKeySlice } from '@app/store/ledger/bitcoin-key.slice';
|
||||
import { bitcoinKeysSlice } from '@app/store/ledger/bitcoin-key.slice';
|
||||
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
|
||||
|
||||
import { ledgerRequestKeysRoutes } from '../../generic-flows/request-keys/ledger-request-keys-route-generator';
|
||||
@@ -46,7 +46,7 @@ function LedgerRequestBitcoinKeys() {
|
||||
ledgerNavigate.toDeviceBusyStep(`Requesting Bitcoin Taproot address (${index - 4}…5)`);
|
||||
},
|
||||
});
|
||||
dispatch(bitcoinKeySlice.actions.addBitcoinKeys(keys));
|
||||
dispatch(bitcoinKeysSlice.actions.addBitcoinKeys(keys));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
|
||||
import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentAccountTaprootIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks';
|
||||
|
||||
@@ -14,7 +16,7 @@ import { PsbtRequestFee } from './components/psbt-request-fee';
|
||||
import { PsbtRequestRaw } from './components/psbt-request-raw';
|
||||
|
||||
interface PsbtRequestDetailsProps {
|
||||
allowedSighash?: btc.SignatureHash[];
|
||||
allowedSighash?: AllowedSighashTypes[];
|
||||
indexesToSign?: number[];
|
||||
psbtRaw?: RawPsbt;
|
||||
psbtTxInputs: btc.TransactionInput[];
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
getBtcSignerLibNetworkConfigByMode,
|
||||
} from '@shared/crypto/bitcoin/bitcoin.network';
|
||||
import { getAddressFromOutScript } from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
import { ensureArray, isDefined, isUndefined } from '@shared/utils';
|
||||
|
||||
import { useOrdinalsAwareUtxoQueries } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
@@ -44,7 +45,7 @@ function getInputValue(index: number, input: btc.TransactionInput) {
|
||||
}
|
||||
|
||||
interface UseParsedInputsArgs {
|
||||
allowedSighash?: btc.SignatureHash[];
|
||||
allowedSighash?: AllowedSighashTypes[];
|
||||
inputs: btc.TransactionInput[];
|
||||
indexesToSign?: number[];
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
||||
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
|
||||
import { subtractMoney } from '@app/common/money/calculate-money';
|
||||
import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
import { useCurrentAccountTaprootIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks';
|
||||
@@ -13,7 +15,7 @@ import { usePsbtInscriptions } from './use-psbt-inscriptions';
|
||||
import { usePsbtTotals } from './use-psbt-totals';
|
||||
|
||||
interface UseParsedPsbtArgs {
|
||||
allowedSighash?: btc.SignatureHash[];
|
||||
allowedSighash?: AllowedSighashTypes[];
|
||||
inputs: btc.TransactionInput[];
|
||||
indexesToSign?: number[];
|
||||
outputs: btc.TransactionOutput[];
|
||||
|
||||
@@ -4,6 +4,7 @@ import { hexToBytes } from '@noble/hashes/utils';
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { logger } from '@shared/logger';
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
import { isString, isUndefined } from '@shared/utils';
|
||||
|
||||
import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
@@ -12,7 +13,7 @@ import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/b
|
||||
export type RawPsbt = ReturnType<typeof btc.RawPSBTV0.decode>;
|
||||
|
||||
interface SignPsbtArgs {
|
||||
allowedSighash?: btc.SignatureHash[];
|
||||
allowedSighash?: AllowedSighashTypes[];
|
||||
inputs: btc.TransactionInput[];
|
||||
indexesToSign?: number[];
|
||||
tx: btc.Transaction;
|
||||
@@ -39,7 +40,7 @@ export function usePsbtSigner() {
|
||||
// If type taproot, and the tapInternalKey is missing, assume it should
|
||||
// be the account publicKey
|
||||
if (taprootSigner && witnessOutputScript?.type === 'tr' && !input.tapInternalKey) {
|
||||
input.tapInternalKey = taprootSigner.payment.tapInternalKey;
|
||||
tx.updateInput(idx, { ...input, tapInternalKey: taprootSigner.payment.tapInternalKey });
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
|
||||
import { useRouteHeader } from '@app/common/hooks/use-route-header';
|
||||
import { PopupHeader } from '@app/features/current-account/popup-header';
|
||||
@@ -36,7 +37,7 @@ function getPsbtTxOutputs(psbtTx: btc.Transaction) {
|
||||
}
|
||||
|
||||
interface PsbtSignerProps {
|
||||
allowedSighash?: btc.SignatureHash[];
|
||||
allowedSighash?: AllowedSighashTypes[];
|
||||
indexesToSign?: number[];
|
||||
name?: string;
|
||||
origin: string;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SuggestedFirstStepStatus, SuggestedFirstSteps } from '@shared/models/on
|
||||
|
||||
import { useGetAnchoredAccountBalanceListQuery } from '@app/query/stacks/balance/stx-balance.query';
|
||||
import { useAllAccountsNonFungibleTokenHoldingsTotal } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.hooks';
|
||||
import { useGetNonFungibleTokenHoldingsQuery } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.query';
|
||||
import useGetNonFungibleTokenHoldingsQuery from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.query';
|
||||
import {
|
||||
useCurrentStacksAccount,
|
||||
useStacksAccounts,
|
||||
@@ -34,10 +34,10 @@ export function useSuggestedFirstSteps() {
|
||||
const stepsStatus = useSuggestedFirstStepsStatus();
|
||||
|
||||
const { data: nonFungibleTokenHoldings } = useGetNonFungibleTokenHoldingsQuery(
|
||||
currentAccount?.address
|
||||
currentAccount?.address!
|
||||
);
|
||||
|
||||
const firstFiveAccounts = accounts?.slice(0, 5);
|
||||
const firstFiveAccounts = accounts?.slice(0, 5) ?? [];
|
||||
const accountsAvailableStxBalance = useAllAccountsAvailableStxBalance(firstFiveAccounts);
|
||||
const accountsNonFungibleTokenHoldings =
|
||||
useAllAccountsNonFungibleTokenHoldingsTotal(firstFiveAccounts);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { Tabs } from '@app/components/tabs';
|
||||
|
||||
interface HomeTabsProps extends StackProps {
|
||||
children: React.JSX.Element;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
// TODO #4013: Abstract this to generic RouteTab once choose-fee-tab updated
|
||||
export function HomeTabs({ children }: HomeTabsProps) {
|
||||
|
||||
@@ -10,22 +10,16 @@ import { Header } from '@app/components/header';
|
||||
import { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
|
||||
import { SuggestedFirstSteps } from '@app/features/suggested-first-steps/suggested-first-steps';
|
||||
import { HomeActions } from '@app/pages/home/components/home-actions';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
|
||||
import { CurrentAccount } from './components/account-area';
|
||||
import { HomeTabs } from './components/home-tabs';
|
||||
import { HomeLayout } from './components/home.layout';
|
||||
import { HomeLoader } from './home.loader';
|
||||
|
||||
export function Home() {
|
||||
return <HomeLoader>{account => <HomeContainer account={account} />}</HomeLoader>;
|
||||
}
|
||||
|
||||
interface HomeContainerProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
function HomeContainer({ account }: HomeContainerProps) {
|
||||
const { decodedAuthRequest } = useOnboardingState();
|
||||
|
||||
const stacksAccount = useCurrentStacksAccount();
|
||||
const navigate = useNavigate();
|
||||
useTrackFirstDeposit();
|
||||
|
||||
@@ -47,7 +41,7 @@ function HomeContainer({ account }: HomeContainerProps) {
|
||||
actions={<HomeActions />}
|
||||
>
|
||||
<HomeTabs>
|
||||
<Outlet context={{ address: account.address }} />
|
||||
<Outlet context={{ address: stacksAccount?.address }} />
|
||||
</HomeTabs>
|
||||
</HomeLayout>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { RpcErrorCode } from '@btckit/types';
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import { bytesToHex } from '@stacks/common';
|
||||
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
import { makeRpcErrorResponse, makeRpcSuccessResponse } from '@shared/rpc/rpc-methods';
|
||||
|
||||
import { useRpcSignPsbtParams } from '@app/common/psbt/use-psbt-request-params';
|
||||
import { usePsbtSigner } from '@app/features/psbt-signer/hooks/use-psbt-signer';
|
||||
|
||||
export function useRpcSignPsbt() {
|
||||
const navigate = useNavigate();
|
||||
const { origin, tabId, requestId, psbtHex, allowedSighash, signAtIndex } = useRpcSignPsbtParams();
|
||||
const { signPsbt, getPsbtAsTransaction } = usePsbtSigner();
|
||||
|
||||
@@ -21,7 +25,13 @@ export function useRpcSignPsbt() {
|
||||
onSignPsbt(inputs: btc.TransactionInput[]) {
|
||||
const tx = getPsbtAsTransaction(psbtHex);
|
||||
|
||||
signPsbt({ allowedSighash, indexesToSign: signAtIndex, inputs, tx });
|
||||
try {
|
||||
signPsbt({ allowedSighash, indexesToSign: signAtIndex, inputs, tx });
|
||||
} catch (e) {
|
||||
return navigate(RouteUrls.RequestError, {
|
||||
state: { message: e instanceof Error ? e.message : '', title: 'Failed to sign' },
|
||||
});
|
||||
}
|
||||
|
||||
const psbt = tx.toPSBT();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ function ContractCallDetailsSuspense() {
|
||||
px="base-loose"
|
||||
py="extra-loose"
|
||||
spacing="loose"
|
||||
width="100%"
|
||||
>
|
||||
<Title as="h2" fontWeight="500">
|
||||
Function and arguments
|
||||
|
||||
@@ -13,6 +13,7 @@ export enum QueryPrefixes {
|
||||
InscriptionFromTxid = 'inscription-from-txid',
|
||||
InscriptionsFromApiInfiniteQuery = 'inscriptions-from-api-infinite-query',
|
||||
GetNftMetadata = 'get-nft-metadata',
|
||||
GetNftHoldings = 'get-nft-holdings',
|
||||
|
||||
StampCollection = 'stamp-collection',
|
||||
StampsByAddress = 'stamps-by-address',
|
||||
|
||||
@@ -2,42 +2,21 @@ import { useMemo } from 'react';
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import {
|
||||
useGetNonFungibleTokenHoldingsListQuery,
|
||||
useGetNonFungibleTokenHoldingsQuery,
|
||||
} from './non-fungible-token-holdings.query';
|
||||
import { useGetNonFungibleTokenHoldingsListQuery } from './non-fungible-token-holdings.query';
|
||||
|
||||
export function useAllAccountsNonFungibleTokenHoldingsTotal(accounts?: StacksAccount[]) {
|
||||
const accountsNftHoldings = useGetNonFungibleTokenHoldingsListQuery(accounts);
|
||||
export function useAllAccountsNonFungibleTokenHoldingsTotal(accounts: StacksAccount[]) {
|
||||
const accountsNftHoldings = useGetNonFungibleTokenHoldingsListQuery(
|
||||
accounts.map(account => account.address)
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
accountsNftHoldings.reduce((acc, nftHoldings) => {
|
||||
return acc.plus(nftHoldings.data?.total || 0);
|
||||
}, new BigNumber(0)),
|
||||
accountsNftHoldings.reduce(
|
||||
(acc, nftHoldings) => acc.plus(nftHoldings.data?.total || 0),
|
||||
new BigNumber(0)
|
||||
),
|
||||
[accountsNftHoldings]
|
||||
);
|
||||
}
|
||||
|
||||
interface NonFungibleTokenHoldingListResult {
|
||||
asset_identifier: string;
|
||||
value: {
|
||||
hex: string;
|
||||
repr: string;
|
||||
};
|
||||
block_height: number;
|
||||
tx_id: string;
|
||||
}
|
||||
|
||||
export function useAccountNonFungibleTokenHoldings() {
|
||||
const currentAccount = useCurrentStacksAccount();
|
||||
|
||||
const { data: nonFungibleTokenHoldings } = useGetNonFungibleTokenHoldingsQuery(
|
||||
currentAccount?.address
|
||||
);
|
||||
|
||||
return (nonFungibleTokenHoldings?.results as NonFungibleTokenHoldingListResult[]) ?? [];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { NonFungibleTokenHoldingsList } from '@stacks/blockchain-api-client';
|
||||
import { useQueries, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { AppUseQueryConfig } from '@app/query/query-config';
|
||||
import { logger } from '@shared/logger';
|
||||
import { Paginated } from '@shared/models/api-types';
|
||||
|
||||
import { QueryPrefixes } from '@app/query/query-prefixes';
|
||||
import { StacksClient } from '@app/query/stacks/stacks-client';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
import { useStacksClientUnanchored } from '@app/store/common/api-clients.hooks';
|
||||
import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
|
||||
|
||||
@@ -11,54 +12,64 @@ import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
|
||||
const staleTime = 15 * 60 * 1000; // 15 min
|
||||
|
||||
const queryOptions = {
|
||||
cacheTime: staleTime,
|
||||
staleTime,
|
||||
} as const;
|
||||
interface NonFungibleTokenHoldingListResult {
|
||||
asset_identifier: string;
|
||||
value: {
|
||||
hex: string;
|
||||
repr: string;
|
||||
};
|
||||
block_height: number;
|
||||
tx_id: string;
|
||||
}
|
||||
|
||||
const queryOptions = { cacheTime: staleTime, staleTime, refetchhOnFocus: false } as const;
|
||||
|
||||
type FetchNonFungibleTokenHoldingsResp = Paginated<NonFungibleTokenHoldingListResult[]>;
|
||||
|
||||
function fetchNonFungibleTokenHoldings(client: StacksClient, limiter: RateLimiter) {
|
||||
return async (address?: string) => {
|
||||
return async (address: string) => {
|
||||
if (!address) return;
|
||||
await limiter.removeTokens(1);
|
||||
return client.nonFungibleTokensApi.getNftHoldings({
|
||||
principal: address,
|
||||
limit: 50,
|
||||
}) as Promise<NonFungibleTokenHoldingsList>;
|
||||
}) as unknown as Promise<FetchNonFungibleTokenHoldingsResp>;
|
||||
};
|
||||
}
|
||||
|
||||
type FetchNonFungibleTokenHoldingsResp = Awaited<
|
||||
ReturnType<ReturnType<typeof fetchNonFungibleTokenHoldings>>
|
||||
>;
|
||||
function makeNonFungibleTokenHoldingsQuery(
|
||||
address: string,
|
||||
network: string,
|
||||
client: StacksClient,
|
||||
limiter: RateLimiter
|
||||
) {
|
||||
if (address === '') logger.warn('No address passed to ' + QueryPrefixes.GetNftHoldings);
|
||||
return {
|
||||
enabled: !!address,
|
||||
queryKey: [QueryPrefixes.GetNftHoldings, address, network],
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(address),
|
||||
...queryOptions,
|
||||
};
|
||||
}
|
||||
|
||||
export function useGetNonFungibleTokenHoldingsQuery<
|
||||
T extends unknown = FetchNonFungibleTokenHoldingsResp
|
||||
>(address?: string, options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
export default function useGetNonFungibleTokenHoldingsQuery(address: string) {
|
||||
const client = useStacksClientUnanchored();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['get-nft-holdings', address, network.chain.stacks.url],
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(address),
|
||||
...queryOptions,
|
||||
...options,
|
||||
});
|
||||
return useQuery(
|
||||
makeNonFungibleTokenHoldingsQuery(address, network.chain.stacks.url, client, limiter)
|
||||
);
|
||||
}
|
||||
|
||||
export function useGetNonFungibleTokenHoldingsListQuery<
|
||||
T extends unknown = FetchNonFungibleTokenHoldingsResp
|
||||
>(accounts?: StacksAccount[], options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
export function useGetNonFungibleTokenHoldingsListQuery(addresses: string[]) {
|
||||
const client = useStacksClientUnanchored();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
|
||||
return useQueries({
|
||||
queries: (accounts ?? []).map(account => ({
|
||||
queryKey: ['get-nft-holdings', account.address, network.chain.stacks.url],
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(account.address),
|
||||
...queryOptions,
|
||||
...options,
|
||||
})),
|
||||
queries: addresses.map(address =>
|
||||
makeNonFungibleTokenHoldingsQuery(address, network.chain.stacks.url, client, limiter)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import { isNftAsset } from '../token-metadata.utils';
|
||||
import { useGetNonFungibleTokenMetadataListQuery } from './non-fungible-token-metadata.query';
|
||||
|
||||
export function useNonFungibleTokensMetadata() {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery();
|
||||
export function useStacksNonFungibleTokensMetadata(account: StacksAccount) {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery(account);
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
|
||||
@@ -3,12 +3,13 @@ import { UseQueryResult, useQueries } from '@tanstack/react-query';
|
||||
|
||||
import { pullContractIdFromIdentity } from '@app/common/utils';
|
||||
import { QueryPrefixes } from '@app/query/query-prefixes';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
import { useTokenMetadataClient } from '@app/store/common/api-clients.hooks';
|
||||
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
import { TokenMetadataClient } from '../../token-metadata-client';
|
||||
import { NftAssetResponse } from '../token-metadata.utils';
|
||||
import { useAccountNonFungibleTokenHoldings } from './non-fungible-token-holdings.hooks';
|
||||
import useGetNonFungibleTokenHoldingsQuery from './non-fungible-token-holdings.query';
|
||||
|
||||
const queryOptions = {
|
||||
refetchOnWindowFocus: true,
|
||||
@@ -28,13 +29,15 @@ function fetchNonFungibleTokenMetadata(client: TokenMetadataClient, limiter: Rat
|
||||
};
|
||||
}
|
||||
|
||||
export function useGetNonFungibleTokenMetadataListQuery(): UseQueryResult<NftAssetResponse>[] {
|
||||
export function useGetNonFungibleTokenMetadataListQuery(
|
||||
account: StacksAccount
|
||||
): UseQueryResult<NftAssetResponse>[] {
|
||||
const client = useTokenMetadataClient();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
const nftHoldings = useAccountNonFungibleTokenHoldings();
|
||||
const nftHoldings = useGetNonFungibleTokenHoldingsQuery(account.address);
|
||||
|
||||
return useQueries({
|
||||
queries: nftHoldings.map(nft => {
|
||||
queries: (nftHoldings.data?.results ?? []).map(nft => {
|
||||
const principal = pullContractIdFromIdentity(nft.asset_identifier);
|
||||
const tokenId = getTokenId(nft.value.hex);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { RouteUrls } from '@shared/route-urls';
|
||||
import { BroadcastErrorDrawer } from '@app/components/broadcast-error-drawer/broadcast-error-drawer';
|
||||
import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { ActivityList } from '@app/features/activity-list/activity-list';
|
||||
import { BalancesList } from '@app/features/balances-list/balances-list';
|
||||
import { AssetsList } from '@app/features/asset-list/asset-list';
|
||||
import { Container } from '@app/features/container/container';
|
||||
import { EditNonceDrawer } from '@app/features/edit-nonce-drawer/edit-nonce-drawer';
|
||||
import { IncreaseBtcFeeDrawer } from '@app/features/increase-fee-drawer/increase-btc-fee-drawer';
|
||||
@@ -186,7 +186,7 @@ function useAppRoutes() {
|
||||
</AccountGate>
|
||||
}
|
||||
>
|
||||
<Route index element={<BalancesList />} />
|
||||
<Route index element={<AssetsList />} />
|
||||
<Route path={RouteUrls.Activity} element={<ActivityList />} />
|
||||
|
||||
{requestBitcoinKeysRoutes}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@shared/crypto/bitcoin/bitcoin.utils';
|
||||
import { getTaprootAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2tr-address-gen';
|
||||
import { getNativeSegwitAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2wpkh-address-gen';
|
||||
import { AllowedSighashTypes } from '@shared/rpc/methods/sign-psbt';
|
||||
|
||||
interface Signer<Payment> {
|
||||
network: BitcoinNetworkModes;
|
||||
@@ -20,7 +21,7 @@ interface Signer<Payment> {
|
||||
address: string;
|
||||
publicKey: Uint8Array;
|
||||
sign(tx: btc.Transaction): void;
|
||||
signIndex(tx: btc.Transaction, index: number, allowedSighash?: btc.SignatureHash[]): void;
|
||||
signIndex(tx: btc.Transaction, index: number, allowedSighash?: AllowedSighashTypes[]): void;
|
||||
}
|
||||
|
||||
interface MakeBitcoinSignerArgs {
|
||||
@@ -29,7 +30,7 @@ interface MakeBitcoinSignerArgs {
|
||||
derivationPath: string;
|
||||
paymentFn(keychain: HDKey, network: BitcoinNetworkModes): any;
|
||||
signFn(tx: btc.Transaction): void;
|
||||
signAtIndexFn(tx: btc.Transaction, index: number, allowedSighash?: btc.SignatureHash[]): void;
|
||||
signAtIndexFn(tx: btc.Transaction, index: number, allowedSighash?: AllowedSighashTypes[]): void;
|
||||
}
|
||||
function makeBitcoinSigner<T extends MakeBitcoinSignerArgs>(args: T) {
|
||||
const { derivationPath, keychain, network, paymentFn, signFn, signAtIndexFn } = args;
|
||||
@@ -89,7 +90,8 @@ export function bitcoinAddressIndexSignerFactory<T extends BitcoinAddressIndexSi
|
||||
|
||||
tx.sign(addressIndexKeychain.privateKey);
|
||||
},
|
||||
signAtIndexFn(tx: btc.Transaction, index: number, allowedSighash?: btc.SignatureHash[]) {
|
||||
// TODO: Revisit allowedSighash type if/when fixed in btc-signer
|
||||
signAtIndexFn(tx: btc.Transaction, index: number, allowedSighash?: number[]) {
|
||||
if (!addressIndexKeychain.privateKey)
|
||||
throw new Error('Unable to sign transaction, no private key found');
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'redux-persist';
|
||||
import { PersistPartial } from 'redux-persist/es/persistReducer';
|
||||
|
||||
import { IS_DEV_ENV } from '@shared/environment';
|
||||
import { persistConfig } from '@shared/storage/redux-pesist';
|
||||
|
||||
import { analyticsSlice } from './analytics/analytics.slice';
|
||||
@@ -23,7 +22,7 @@ import { appPermissionsSlice } from './app-permissions/app-permissions.slice';
|
||||
import { stxChainSlice } from './chains/stx-chain.slice';
|
||||
import { inMemoryKeySlice } from './in-memory-key/in-memory-key.slice';
|
||||
import { keySlice } from './keys/key.slice';
|
||||
import { bitcoinKeySlice } from './ledger/bitcoin-key.slice';
|
||||
import { bitcoinKeysSlice } from './ledger/bitcoin-key.slice';
|
||||
import { networksSlice } from './networks/networks.slice';
|
||||
import { onboardingSlice } from './onboarding/onboarding.slice';
|
||||
import { ordinalsSlice } from './ordinals/ordinals.slice';
|
||||
@@ -38,7 +37,7 @@ export interface RootState {
|
||||
stx: ReturnType<typeof stxChainSlice.reducer>;
|
||||
};
|
||||
ledger: {
|
||||
bitcoin: ReturnType<typeof bitcoinKeySlice.reducer>;
|
||||
bitcoin: ReturnType<typeof bitcoinKeysSlice.reducer>;
|
||||
};
|
||||
ordinals: ReturnType<typeof ordinalsSlice.reducer>;
|
||||
inMemoryKeys: ReturnType<typeof inMemoryKeySlice.reducer>;
|
||||
@@ -56,7 +55,7 @@ const appReducer = combineReducers({
|
||||
stx: stxChainSlice.reducer,
|
||||
}),
|
||||
ledger: combineReducers({
|
||||
bitcoin: bitcoinKeySlice.reducer,
|
||||
bitcoin: bitcoinKeysSlice.reducer,
|
||||
}),
|
||||
ordinals: ordinalsSlice.reducer,
|
||||
inMemoryKeys: inMemoryKeySlice.reducer,
|
||||
@@ -84,16 +83,17 @@ export const store = configureStore({
|
||||
}),
|
||||
broadcastActionTypeToOtherFramesMiddleware,
|
||||
],
|
||||
enhancers: IS_DEV_ENV
|
||||
? [
|
||||
devToolsEnhancer({
|
||||
hostname: 'localhost',
|
||||
port: 8000,
|
||||
realtime: true,
|
||||
suppressConnectErrors: false,
|
||||
}),
|
||||
]
|
||||
: undefined,
|
||||
enhancers:
|
||||
process.env.WALLET_ENVIRONMENT === 'development'
|
||||
? [
|
||||
devToolsEnhancer({
|
||||
hostname: 'localhost',
|
||||
port: 8000,
|
||||
realtime: true,
|
||||
suppressConnectErrors: false,
|
||||
}),
|
||||
]
|
||||
: undefined,
|
||||
});
|
||||
|
||||
export const persistor = persistStore(store);
|
||||
|
||||
@@ -40,5 +40,10 @@ export const keySlice = createSlice({
|
||||
signOut(state) {
|
||||
keyAdapter.removeOne(state, defaultKeyId);
|
||||
},
|
||||
|
||||
debugKillStacks(state) {
|
||||
if (state.entities.default?.type !== 'ledger') return;
|
||||
state.entities.default.publicKeys = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,8 +11,8 @@ interface PersistedBitcoinKeys extends BitcoinLedgerAccountDetails {
|
||||
|
||||
const bitcoinKeyAdapter = createEntityAdapter<PersistedBitcoinKeys>();
|
||||
|
||||
export const bitcoinKeySlice = createSlice({
|
||||
name: 'bitcoinKeys',
|
||||
export const bitcoinKeysSlice = createSlice({
|
||||
name: 'bitcoinsKeys',
|
||||
initialState: bitcoinKeyAdapter.getInitialState(),
|
||||
reducers: {
|
||||
addBitcoinKeys(state, { payload }: PayloadAction<BitcoinLedgerAccountDetails[]>) {
|
||||
|
||||
@@ -128,9 +128,12 @@ document.addEventListener(DomEventName.psbtRequest, ((event: PsbtRequestEvent) =
|
||||
});
|
||||
}) as EventListener);
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
function addHiroWalletToPage() {
|
||||
const inpage = document.createElement('script');
|
||||
inpage.src = chrome.runtime.getURL('inpage.js');
|
||||
inpage.id = 'stacks-wallet-provider';
|
||||
inpage.id = 'hiro-wallet-provider';
|
||||
document.body.appendChild(inpage);
|
||||
});
|
||||
}
|
||||
|
||||
// Don't block thread to add Hiro Wallet to page
|
||||
requestAnimationFrame(() => addHiroWalletToPage());
|
||||
|
||||
6
src/shared/models/api-types.ts
Normal file
6
src/shared/models/api-types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface Paginated<T> {
|
||||
limit: number;
|
||||
offset: number;
|
||||
total: number;
|
||||
results: T;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ declare module '*.svg' {
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare const EXT_ENV: string;
|
||||
declare const VERSION: string;
|
||||
|
||||
interface QrOptions {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { DefineRpcMethod, RpcRequest, RpcResponse } from '@btckit/types';
|
||||
import { AllowAdditionaProperties } from '@btckit/types/dist/types/utils';
|
||||
import { AllowAdditionalProperties } from '@btckit/types/dist/types/utils';
|
||||
|
||||
interface BitcoinContractResponseParams extends AllowAdditionaProperties {
|
||||
interface BitcoinContractResponseParams extends AllowAdditionalProperties {
|
||||
bitcoinContractOffer: string;
|
||||
counterpartyWalletURL: string;
|
||||
counterpartyWalletName: string;
|
||||
counterpartyWalletIcon: string;
|
||||
}
|
||||
|
||||
interface BitcoinContractResponseBody extends AllowAdditionaProperties {
|
||||
interface BitcoinContractResponseBody extends AllowAdditionalProperties {
|
||||
contractId: string;
|
||||
action: string;
|
||||
txId?: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DefineRpcMethod, RpcRequest, RpcResponse } from '@btckit/types';
|
||||
import { DefineRpcMethod, RpcRequest, RpcResponse, SignatureHash } from '@btckit/types';
|
||||
import * as btc from '@scure/btc-signer';
|
||||
import * as yup from 'yup';
|
||||
|
||||
@@ -12,9 +12,21 @@ import {
|
||||
validateRpcParams,
|
||||
} from './validation.utils';
|
||||
|
||||
// TODO: Revisit allowedSighash type if/when fixed in btc-signer
|
||||
export type AllowedSighashTypes = SignatureHash | btc.SignatureHash;
|
||||
|
||||
const rpcSignPsbtParamsSchema = yup.object().shape({
|
||||
account: accountSchema,
|
||||
allowedSighash: yup.array().of(yup.mixed().oneOf(Object.values(btc.SignatureHash))),
|
||||
allowedSighash: yup
|
||||
.array()
|
||||
.of(
|
||||
yup
|
||||
.mixed()
|
||||
.oneOf([
|
||||
...Object.values(SignatureHash).filter(Number.isInteger),
|
||||
...Object.values(btc.SignatureHash).filter(Number.isInteger),
|
||||
])
|
||||
),
|
||||
hex: yup.string().required(),
|
||||
network: yup.string().oneOf(Object.values(WalletDefaultNetworkConfigurationIds)),
|
||||
signAtIndex: yup.mixed<number | number[]>().test(testIsNumberOrArrayOfNumbers),
|
||||
|
||||
@@ -139,6 +139,7 @@ function buildTestTaprootPsbtRequestWithIndex(pubKey: Uint8Array): PsbtRequestOp
|
||||
tx.addInput({
|
||||
index: 0,
|
||||
txid: '4f4cc7cb40b04978bd7704798dc1adf55b58196cef616b0fac8181965abc4726',
|
||||
// tapInternalKey: payment.tapInternalKey,
|
||||
witnessUtxo: {
|
||||
amount: BigInt(1000),
|
||||
script: payment.script,
|
||||
|
||||
@@ -37,13 +37,12 @@ const BRANCH = getBranch();
|
||||
|
||||
const SRC_ROOT_PATH = path.join(__dirname, '../', 'src');
|
||||
const DIST_ROOT_PATH = path.join(__dirname, '../', 'dist');
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
const WALLET_ENVIRONMENT = process.env.WALLET_ENVIRONMENT || 'development';
|
||||
const WEB_BROWSER = process.env.WEB_BROWSER ? process.env.WEB_BROWSER : 'chrome';
|
||||
const IS_DEV = NODE_ENV === 'development';
|
||||
const IS_DEV = WALLET_ENVIRONMENT === 'development';
|
||||
const IS_PROD = !IS_DEV;
|
||||
|
||||
const ANALYZE_BUNDLE = process.env.ANALYZE === 'true';
|
||||
const EXT_ENV = process.env.EXT_ENV || 'web';
|
||||
|
||||
const speedMeasurePlugin = new SpeedMeasurePlugin({
|
||||
disable: !ANALYZE_BUNDLE,
|
||||
@@ -90,7 +89,7 @@ const config = {
|
||||
path: DIST_ROOT_PATH,
|
||||
chunkFilename: !IS_DEV ? '[name].[contenthash:8].chunk.js' : IS_DEV && '[name].chunk.js',
|
||||
filename: () => {
|
||||
if (EXT_ENV === 'prod' || IS_DEV) {
|
||||
if (WALLET_ENVIRONMENT === 'production' || IS_DEV) {
|
||||
return '[name].js';
|
||||
}
|
||||
return '[name].[contenthash:8].js';
|
||||
@@ -172,7 +171,7 @@ const config = {
|
||||
],
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
NODE_ENV: JSON.stringify(NODE_ENV),
|
||||
WALLET_ENVIRONMENT: JSON.stringify(WALLET_ENVIRONMENT),
|
||||
WEB_BROWSER: JSON.stringify(WEB_BROWSER),
|
||||
VERSION: JSON.stringify(VERSION),
|
||||
COMMIT_SHA: JSON.stringify(COMMIT_SHA),
|
||||
|
||||
@@ -7,7 +7,7 @@ config.optimization = {
|
||||
...config.optimization,
|
||||
flagIncludedChunks: false,
|
||||
concatenateModules: false,
|
||||
minimize: process.env.NODE_ENV !== 'test',
|
||||
minimize: process.env.WALLET_ENVIRONMENT !== 'testing',
|
||||
moduleIds: 'deterministic',
|
||||
splitChunks: {
|
||||
hidePathInfo: false,
|
||||
|
||||
@@ -3,7 +3,7 @@ const WebpackDevServer = require('webpack-dev-server');
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
const WALLET_ENVIRONMENT = process.env.WALLET_ENVIRONMENT;
|
||||
|
||||
const HOST = 'localhost';
|
||||
const PORT = process.env.PORT || '8080';
|
||||
@@ -53,7 +53,7 @@ const server = new WebpackDevServer(
|
||||
compiler
|
||||
);
|
||||
|
||||
if (NODE_ENV === 'development' && module.hot) {
|
||||
if (WALLET_ENVIRONMENT === 'development' && module.hot) {
|
||||
module.hot.accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||
const SRC_ROOT_PATH = path.join(__dirname, '../', 'src');
|
||||
const DIST_ROOT_PATH = path.join(__dirname, '../', 'dist');
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
const WALLET_ENVIRONMENT = process.env.WALLET_ENVIRONMENT || 'development';
|
||||
const ANALYZE_BUNDLE = process.env.ANALYZE === 'true';
|
||||
const IS_PUBLISHING = !!process.env.IS_PUBLISHING;
|
||||
const BRANCH = process.env.GITHUB_REF;
|
||||
|
||||
const IS_DEV = NODE_ENV === 'development';
|
||||
const IS_DEV = WALLET_ENVIRONMENT === 'development';
|
||||
const IS_PROD = !IS_DEV;
|
||||
const MAIN_BRANCH = 'refs/heads/main';
|
||||
|
||||
|
||||
72
yarn.lock
72
yarn.lock
@@ -926,10 +926,10 @@
|
||||
sha.js "^2.4.11"
|
||||
smart-buffer "^4.1.0"
|
||||
|
||||
"@btckit/types@0.0.18":
|
||||
version "0.0.18"
|
||||
resolved "https://registry.yarnpkg.com/@btckit/types/-/types-0.0.18.tgz#48bd0b564a9c14bf0c670b0834131e5054c20d69"
|
||||
integrity sha512-2b/Ny4rZ+TbGW59xiV/PvXRP1cnF40874t12OzeVKa/BghxBFazEayDHce9YtPTkxQ4ilQkmeNrS67FWK4OsGA==
|
||||
"@btckit/types@0.0.19":
|
||||
version "0.0.19"
|
||||
resolved "https://registry.yarnpkg.com/@btckit/types/-/types-0.0.19.tgz#8454326b669d3b029094d3d20026fdc6e6c72319"
|
||||
integrity sha512-APoOfYSg9SRR4CMXL606IDtpgh+ZD3kS/+iY0BkUALD6HvXo2pVw20L5YYIc+HrgMcF6WN0TH7TXdVs+Vu+kww==
|
||||
|
||||
"@coinbase/cbpay-js@1.0.2":
|
||||
version "1.0.2"
|
||||
@@ -5519,47 +5519,47 @@
|
||||
dependencies:
|
||||
remove-accents "0.4.2"
|
||||
|
||||
"@tanstack/query-core@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.29.19.tgz#49ccbd0606633d1e55baf3b91ab7cc7aef411b1d"
|
||||
integrity sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw==
|
||||
"@tanstack/query-core@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.32.0.tgz#e0f4a830283612430450c13badd353766423f523"
|
||||
integrity sha512-ei4IYwL2kmlKSlCw9WgvV7PpXi0MiswVwfQRxawhJA690zWO3dU49igaQ/UMTl+Jy9jj9dK5IKAYvbX7kUvviQ==
|
||||
|
||||
"@tanstack/query-persist-client-core@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-4.29.19.tgz#c0cd03eca74d33e5aa40c95cadd5585620ff2320"
|
||||
integrity sha512-rr6p3xwEZCz3cEDZsj3QGePf6PG44WxRUGQVcm2JFPZOq9TkG/0i5+hQ3STiHm1Fj6qwCH8xIi62L8kG0zRj/Q==
|
||||
"@tanstack/query-persist-client-core@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-4.32.0.tgz#8ab8670555b777fcc7cfde0581b42d28b6164933"
|
||||
integrity sha512-TUIArpiZJqLsYEPmg2LsZD+uJknrAHXWSaoCROFniDS0TqExe4KkjQ2gt+XTBwzxlOEN52R+l98k+oS/u94ogg==
|
||||
dependencies:
|
||||
"@tanstack/query-core" "4.29.19"
|
||||
"@tanstack/query-core" "4.32.0"
|
||||
|
||||
"@tanstack/query-sync-storage-persister@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-4.29.19.tgz#292d33cdc9b38b11127f2f07b1935a73039cbba4"
|
||||
integrity sha512-B3wDl3D3YBFTlY2yeXecEh4NEG/8Hr8pqsxnWJijRwBqvOKlqD4bUgk5kl5nbn1mShD2vnQ+yvH900/11X29Zw==
|
||||
"@tanstack/query-sync-storage-persister@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-4.32.0.tgz#7c33266d9e0c45eb0ec309467066c20940d57052"
|
||||
integrity sha512-DJuHgyHmmzjamC1hjs5BB1fScqMXuk/rOYrcmSLOd3U/tceAK0PqZOJzzd2Zt0bwtJQ8hatKqks7MqiLAvAPQw==
|
||||
dependencies:
|
||||
"@tanstack/query-persist-client-core" "4.29.19"
|
||||
"@tanstack/query-persist-client-core" "4.32.0"
|
||||
|
||||
"@tanstack/react-query-devtools@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.29.19.tgz#b6f337c91313388d3f04c890449005d7bb355322"
|
||||
integrity sha512-rL2xqTPr+7gJvVGwyq8E8CWqqw950N4lZ6ffJeNX0qqymKHxHW1FM6nZaYt7Aufs/bXH0m1L9Sj3kDGQbp0rwg==
|
||||
"@tanstack/react-query-devtools@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.32.0.tgz#7d0ba1bcf7a7470c4f405229e212923bcc0f3323"
|
||||
integrity sha512-rOmWqzKzRmQrQULV5Ova2FGEEPT76FZA3hz8T+LFkvp3ehw9ugSZ1BosgRJ7AFCeir+5pcNvFwILy4pDK8HpRw==
|
||||
dependencies:
|
||||
"@tanstack/match-sorter-utils" "^8.7.0"
|
||||
superjson "^1.10.0"
|
||||
use-sync-external-store "^1.2.0"
|
||||
|
||||
"@tanstack/react-query-persist-client@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-4.29.19.tgz#27b24ec5c8983894d6f23450e2f922d07f47706f"
|
||||
integrity sha512-LfcasTosdnI9K66HTP0Rk72Ypza3tCgrcF9bc2qFlKsBleYOYo8bz7/GiiOHj1SQH4GRQlKB+P4+/it+qlJg4g==
|
||||
"@tanstack/react-query-persist-client@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-4.32.0.tgz#6840a73042ca7708e793e6ec0acb1f49bc275d9a"
|
||||
integrity sha512-2H1Fuw02uoW2U4kAexV3rmSjyOWunaWAoMgRybULu/TbEGh00c6p4yon/nQM81TAbf4M66Aa7nU0vqipY0VJXQ==
|
||||
dependencies:
|
||||
"@tanstack/query-persist-client-core" "4.29.19"
|
||||
"@tanstack/query-persist-client-core" "4.32.0"
|
||||
|
||||
"@tanstack/react-query@4.29.19":
|
||||
version "4.29.19"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.29.19.tgz#6ba187f2d0ea36ae83ff1f67068f53c88ce7b228"
|
||||
integrity sha512-XiTIOHHQ5Cw1WUlHaD4fmVUMhoWjuNJlAeJGq7eM4BraI5z7y8WkZO+NR8PSuRnQGblpuVdjClQbDFtwxTtTUw==
|
||||
"@tanstack/react-query@4.32.0":
|
||||
version "4.32.0"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.32.0.tgz#701b45b149cfd4b54a68705f9100973db3ba5d5d"
|
||||
integrity sha512-B8WUMcByYAH9500ENejDCATOmEZhqjtS9wsfiQ3BNa+s+yAynY8SESI8WWHhSqUmjd0pmCSFRP6BOUGSda3QXA==
|
||||
dependencies:
|
||||
"@tanstack/query-core" "4.29.19"
|
||||
"@tanstack/query-core" "4.32.0"
|
||||
use-sync-external-store "^1.2.0"
|
||||
|
||||
"@tippyjs/react@4.2.6", "@tippyjs/react@^4.2.4":
|
||||
@@ -5579,10 +5579,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@trivago/prettier-plugin-sort-imports@4.1.1":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.1.tgz#71c3c1ae770c3738b6fc85710714844477574ffd"
|
||||
integrity sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw==
|
||||
"@trivago/prettier-plugin-sort-imports@4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.2.0.tgz#b240366f9e2bda8e14edb18b14ea084e0ec25968"
|
||||
integrity sha512-YBepjbt+ZNBVmN3ev1amQH3lWCmHyt5qTbLCp/syXJRu/Kw2koXh44qayB1gMRxcL/gV8egmjN5xWSrYyfUtyw==
|
||||
dependencies:
|
||||
"@babel/generator" "7.17.7"
|
||||
"@babel/parser" "^7.20.5"
|
||||
|
||||
Reference in New Issue
Block a user