From a0618c15779a8d3dc912704ffb7fe5de45e50c00 Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 09:49:22 +1100 Subject: [PATCH 01/15] fix: copy patches dir in public-api Dockerfile for pnpm install pnpm requires the patches directory during install (not just scripts) because patched dependencies are referenced in the lockfile. The --ignore-scripts flag alone doesn't prevent pnpm from reading patch files during dependency resolution. Co-Authored-By: Claude Opus 4.6 --- packages/public-api/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index 20de68568c5..6bef1e59745 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -47,6 +47,9 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ +# Copy patches directory (required by pnpm for patched dependencies in lockfile) +COPY patches/ ./patches/ + # Install dependencies with Railway cache mount (skip build/postinstall scripts that require git/cypress/etc) RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts From 473f2d32c4a26b365b68892b6551fa9b92ea269f Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 10:36:29 +1100 Subject: [PATCH 02/15] fix: add .railwayignore to reduce snapshot size for public-api deploys Railway's "Failed to snapshot repository" error is caused by the repo being too large (~74MB tracked files). This excludes frontend source, images, tests, and other files not needed for the public-api Dockerfile build from Railway's pre-build snapshot. Co-Authored-By: Claude Opus 4.6 --- .railwayignore | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .railwayignore diff --git a/.railwayignore b/.railwayignore new file mode 100644 index 00000000000..2396bc7ab31 --- /dev/null +++ b/.railwayignore @@ -0,0 +1,48 @@ +# Railway snapshot ignore - reduce repo snapshot size for public-api builds +# The Dockerfile.dockerignore handles Docker build context separately; +# this file reduces what Railway snapshots from GitHub before the build starts. + +# Frontend source (not needed for public-api server build) +src/ +cypress/ +e2e/ + +# Large generated/static assets already copied explicitly in Dockerfile +public/generated/generatedAssetData.json.gz +public/generated/generatedAssetData.json.br +public/generated/relatedAssetIndex.json + +# Images and static assets +src/assets/ +*.png +*.jpg +*.jpeg +*.svg +*.gif +*.ico +*.webp + +# Development/IDE files +.vscode/ +.idea/ +.claude/ +.agents/ +.beads/ +.playwright-mcp/ +coverage/ +*.log +.DS_Store + +# Yarn state (not needed for pnpm builds) +.yarn/ + +# Documentation +*.md +!packages/public-api/*.md +docs/ + +# Test files +**/*.test.ts +**/*.test.tsx +**/*.spec.ts +**/*.spec.tsx From dc3c2b0c89ebd024e1e229910c2ec852a624cfaf Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 11:15:20 +1100 Subject: [PATCH 03/15] Revert "fix: add .railwayignore to reduce snapshot size for public-api deploys" This reverts commit 473f2d32c4a26b365b68892b6551fa9b92ea269f. --- .railwayignore | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 .railwayignore diff --git a/.railwayignore b/.railwayignore deleted file mode 100644 index 2396bc7ab31..00000000000 --- a/.railwayignore +++ /dev/null @@ -1,48 +0,0 @@ -# Railway snapshot ignore - reduce repo snapshot size for public-api builds -# The Dockerfile.dockerignore handles Docker build context separately; -# this file reduces what Railway snapshots from GitHub before the build starts. - -# Frontend source (not needed for public-api server build) -src/ -cypress/ -e2e/ - -# Large generated/static assets already copied explicitly in Dockerfile -public/generated/generatedAssetData.json.gz -public/generated/generatedAssetData.json.br -public/generated/relatedAssetIndex.json - -# Images and static assets -src/assets/ -*.png -*.jpg -*.jpeg -*.svg -*.gif -*.ico -*.webp - -# Development/IDE files -.vscode/ -.idea/ -.claude/ -.agents/ -.beads/ -.playwright-mcp/ -coverage/ -*.log -.DS_Store - -# Yarn state (not needed for pnpm builds) -.yarn/ - -# Documentation -*.md -!packages/public-api/*.md -docs/ - -# Test files -**/*.test.ts -**/*.test.tsx -**/*.spec.ts -**/*.spec.tsx From eb5562c7f498fa0e3bd697de44c9ef4e8c5819ed Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 11:15:20 +1100 Subject: [PATCH 04/15] Revert "fix: copy patches dir in public-api Dockerfile for pnpm install" This reverts commit a0618c15779a8d3dc912704ffb7fe5de45e50c00. --- packages/public-api/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index 6bef1e59745..20de68568c5 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -47,9 +47,6 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ -# Copy patches directory (required by pnpm for patched dependencies in lockfile) -COPY patches/ ./patches/ - # Install dependencies with Railway cache mount (skip build/postinstall scripts that require git/cypress/etc) RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts From b95adf590cb279499a350cdf1281c9d3f90cbd8b Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 12:21:51 +1100 Subject: [PATCH 05/15] fix: unrug Railway CI - copy patches dir and add .railwayignore (#12099) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: unrug public-api Railway CI - copy patches dir and add .railwayignore Two fixes for Railway deployment failures: 1. Copy patches/ directory in Dockerfile before pnpm install - pnpm requires patch files during install even with --ignore-scripts because patched dependencies are referenced in the lockfile. 2. Add .railwayignore to reduce repo snapshot size - Railway's "Failed to snapshot repository" error was caused by the repo being ~74MB of tracked files. Excludes frontend source, images, tests, and other files not needed for the public-api Docker build. Co-Authored-By: Claude Opus 4.6 * fix: add patches dir to swap-widget Dockerfile and update .railwayignore The swap-widget Dockerfile had the same missing patches/ dir bug as public-api — pnpm install fails with ENOENT for patched dependencies. Also add swap-widget *.md exception to .railwayignore. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .railwayignore | 49 +++++++++++++++++++++++++++++++++ packages/public-api/Dockerfile | 3 ++ packages/swap-widget/Dockerfile | 3 ++ 3 files changed, 55 insertions(+) create mode 100644 .railwayignore diff --git a/.railwayignore b/.railwayignore new file mode 100644 index 00000000000..bb091a643f1 --- /dev/null +++ b/.railwayignore @@ -0,0 +1,49 @@ +# Railway snapshot ignore - reduce repo snapshot size for public-api builds +# The Dockerfile.dockerignore handles Docker build context separately; +# this file reduces what Railway snapshots from GitHub before the build starts. + +# Frontend source (not needed for public-api server build) +src/ +cypress/ +e2e/ + +# Large generated/static assets already copied explicitly in Dockerfile +public/generated/generatedAssetData.json.gz +public/generated/generatedAssetData.json.br +public/generated/relatedAssetIndex.json + +# Images and static assets +src/assets/ +*.png +*.jpg +*.jpeg +*.svg +*.gif +*.ico +*.webp + +# Development/IDE files +.vscode/ +.idea/ +.claude/ +.agents/ +.beads/ +.playwright-mcp/ +coverage/ +*.log +.DS_Store + +# Yarn state (not needed for pnpm builds) +.yarn/ + +# Documentation +*.md +!packages/public-api/*.md +!packages/swap-widget/*.md +docs/ + +# Test files +**/*.test.ts +**/*.test.tsx +**/*.spec.ts +**/*.spec.tsx diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index 20de68568c5..6bef1e59745 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -47,6 +47,9 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ +# Copy patches directory (required by pnpm for patched dependencies in lockfile) +COPY patches/ ./patches/ + # Install dependencies with Railway cache mount (skip build/postinstall scripts that require git/cypress/etc) RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts diff --git a/packages/swap-widget/Dockerfile b/packages/swap-widget/Dockerfile index 5e784f3b619..d7943d4733f 100644 --- a/packages/swap-widget/Dockerfile +++ b/packages/swap-widget/Dockerfile @@ -47,6 +47,9 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ +# Copy patches directory (required by pnpm for patched dependencies in lockfile) +COPY patches/ ./patches/ + # Install dependencies (skip build/postinstall scripts that require git/cypress/etc) RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts From 3c56acfac58421780ca51e290f884a1ff37187c3 Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Thu, 5 Mar 2026 14:44:41 +1100 Subject: [PATCH 06/15] fix: use copy package-import-method in Docker to avoid pnpm ENOENT (#12100) * fix: use copy package-import-method in Docker to avoid pnpm ENOENT pnpm's default hard-link strategy fails intermittently on Docker's overlay filesystem with "ENOENT: rename _tmp -> secp256k1". Setting package-import-method=copy via env var (Docker-only, doesn't affect local dev .npmrc) fixes the atomic rename race condition. Co-Authored-By: Claude Opus 4.6 * fix: add retry loop for pnpm install in Docker builds The ENOENT rename failure persists even with package-import-method=copy because pnpm's hoisting/linking phase still does atomic renames that race on Docker's overlay filesystem. Since it's intermittent and all packages are cached in the store after the first attempt, a retry with clean node_modules is fast and reliable. Co-Authored-By: Claude Opus 4.6 * fix: restrict swap-widget tsconfig types to prevent auto-discovery Without an explicit `types` field, TypeScript auto-discovers all @types/* packages hoisted to root node_modules. The deprecated stub @types/ethereumjs-util (from hdwallet-ledger devDeps) has no .d.ts files, causing TS2688 during Docker builds. Restricting to ["vite/client"] matches the pattern used by the root tsconfig. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- packages/public-api/Dockerfile | 12 +++++++++++- packages/swap-widget/Dockerfile | 12 +++++++++++- packages/swap-widget/tsconfig.json | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index 6bef1e59745..b0050a9f496 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -50,8 +50,18 @@ COPY packages/utils/package.json ./packages/utils/ # Copy patches directory (required by pnpm for patched dependencies in lockfile) COPY patches/ ./patches/ +# Use copy instead of hard-link to avoid ENOENT rename failures on Docker's overlay filesystem +ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy + # Install dependencies with Railway cache mount (skip build/postinstall scripts that require git/cypress/etc) -RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts +# Retry loop: pnpm's hoisting/linking phase hits intermittent ENOENT rename failures on +# Docker overlay filesystems. Packages are cached in the store after the first attempt, +# so retries are fast. Clean node_modules between attempts to avoid partial state. +RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && \ + for i in 1 2 3; do \ + pnpm install --frozen-lockfile --ignore-scripts && break || \ + { echo "pnpm install attempt $i failed, retrying..."; rm -rf node_modules; }; \ + done # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ diff --git a/packages/swap-widget/Dockerfile b/packages/swap-widget/Dockerfile index d7943d4733f..a0fec07f567 100644 --- a/packages/swap-widget/Dockerfile +++ b/packages/swap-widget/Dockerfile @@ -50,8 +50,18 @@ COPY packages/utils/package.json ./packages/utils/ # Copy patches directory (required by pnpm for patched dependencies in lockfile) COPY patches/ ./patches/ +# Use copy instead of hard-link to avoid ENOENT rename failures on Docker's overlay filesystem +ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy + # Install dependencies (skip build/postinstall scripts that require git/cypress/etc) -RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && pnpm install --frozen-lockfile --ignore-scripts +# Retry loop: pnpm's hoisting/linking phase hits intermittent ENOENT rename failures on +# Docker overlay filesystems. Packages are cached in the store after the first attempt, +# so retries are fast. Clean node_modules between attempts to avoid partial state. +RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && \ + for i in 1 2 3; do \ + pnpm install --frozen-lockfile --ignore-scripts && break || \ + { echo "pnpm install attempt $i failed, retrying..."; rm -rf node_modules; }; \ + done # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ diff --git a/packages/swap-widget/tsconfig.json b/packages/swap-widget/tsconfig.json index f50b75c5f0c..245f29c5944 100644 --- a/packages/swap-widget/tsconfig.json +++ b/packages/swap-widget/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, + "types": ["vite/client"], "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, From b4b0ade17d8b847f254eada2015ca0690dfe1cbe Mon Sep 17 00:00:00 2001 From: gomes-bot Date: Thu, 5 Mar 2026 12:03:40 +0100 Subject: [PATCH 07/15] feat: speed up stuck btc transactions via rbf (#11885) --- .../src/utxo/UtxoBaseAdapter.ts | 1 + src/assets/translations/en/main.json | 37 +- .../Header/ActionCenter/ActionCenter.tsx | 36 +- .../components/ActionStatusIcon.tsx | 6 + .../components/ActionStatusTag.tsx | 6 + .../GenericTransactionActionCard.tsx | 129 ++- .../GenericTransactionNotification.tsx | 2 + .../ActionCenter/components/SpeedUpModal.tsx | 844 ++++++++++++++++++ .../components/speedUpUtils.test.ts | 234 +++++ .../ActionCenter/components/speedUpUtils.ts | 186 ++++ src/components/Modals/QrCode/Form.tsx | 7 +- src/components/Modals/Send/Form.tsx | 7 +- .../Modals/Send/hooks/useCompleteSendFlow.tsx | 75 +- .../Send/hooks/useFormSend/useFormSend.tsx | 21 +- src/components/Modals/Send/utils.ts | 45 +- .../TransactionCommon.tsx | 42 +- .../useSendActionSubscriber.tsx | 32 +- src/state/slices/actionSlice/types.ts | 13 + 18 files changed, 1685 insertions(+), 38 deletions(-) create mode 100644 src/components/Layout/Header/ActionCenter/components/SpeedUpModal.tsx create mode 100644 src/components/Layout/Header/ActionCenter/components/speedUpUtils.test.ts create mode 100644 src/components/Layout/Header/ActionCenter/components/speedUpUtils.ts diff --git a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts index b1ebd972895..e6dc58540bf 100644 --- a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts +++ b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts @@ -377,6 +377,7 @@ export abstract class UtxoBaseAdapter implements IChainAd vout: input.vout, txid: input.txid, hex: data.hex, + // BIP-125: Opt-in RBF for Bitcoin transactions (sequence < 0xffffffff signals replaceability) ...(this.chainId === KnownChainIds.BitcoinMainnet && { sequence: 0xfffffffd }), // For Zcash, we need to pass the blockHeight and txid of each input transaction // so Ledger can add them to the PSBT and determine the correct consensus branch ID. diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index d408aef0c8f..c128ad3c49a 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -69,6 +69,7 @@ "rename": "Rename", "forget": "Forget?", "pending": "Pending", + "current": "Current", "preview": "Preview", "incomplete": "Incomplete", "available": "Available", @@ -1383,6 +1384,32 @@ }, "status": { "pendingBody": "Sending %{amount} %{symbol}" + }, + "speedUp": { + "title": "Speed Up Transaction", + "description": "Increase the fee to speed up your pending Bitcoin transaction.", + "currentFee": "Current Fee", + "currentFeeRate": "Current Fee Rate", + "previousFee": "Previous Fee", + "newFee": "New Fee", + "additionalFee": "Additional Fee", + "newFeeRate": "New Fee Rate", + "feeDifference": "Additional Fee", + "confirm": "Confirm Speed Up", + "cancel": "Cancel Transaction Instead", + "cancelDescription": "Send the funds back to yourself with a higher fee, effectively canceling the original transaction.", + "confirmCancel": "Confirm Cancellation", + "insufficientFunds": "Insufficient funds to bump fee", + "selectFeeRate": "Select a higher fee rate", + "pendingTransaction": "Pending Transaction", + "speedingUp": "Speeding up transaction...", + "canceling": "Canceling transaction...", + "fetchError": "Failed to fetch transaction data", + "feesTooLow": "Current fees are already at network rates", + "alreadySpentOrReplaced": "This transaction can no longer be sped up because it was already replaced or confirmed.", + "alreadyConfirmed": "This transaction is already confirmed and can no longer be sped up.", + "speedUpFailed": "Failed to speed up transaction", + "broadcastNotObserved": "Replacement transaction broadcast was not observed by the network yet. Please try again." } }, "receive": { @@ -2182,7 +2209,12 @@ "today": "Today", "yesterday": "Yesterday", "thisWeek": "This Week", - "thisMonth": "This Month" + "thisMonth": "This Month", + "speedUp": "Speed Up", + "cancelTransaction": "Cancel Transaction", + "replaced": "Replaced", + "replacedBy": "Replaced by %{txHash}", + "replaces": "Replaces %{txHash}" }, "plugins": { "foxPage": { @@ -3118,6 +3150,8 @@ "actionCenter": { "title": "Notifications", "viewTransaction": "View Transaction", + "originalTransaction": "Original Transaction", + "replacementTransaction": "Replacement Transaction", "viewOrder": "View Order", "cancelOrder": "Cancel Order", "pair": "Pair", @@ -3134,6 +3168,7 @@ "confirmed": "Confirmed", "failed": "Failed", "pending": "Pending", + "replaced": "Replaced", "claimAvailable": "Claim Available", "claimed": "Claimed", "unknown": "Unknown", diff --git a/src/components/Layout/Header/ActionCenter/ActionCenter.tsx b/src/components/Layout/Header/ActionCenter/ActionCenter.tsx index b494a6d60d6..8235a9bd304 100644 --- a/src/components/Layout/Header/ActionCenter/ActionCenter.tsx +++ b/src/components/Layout/Header/ActionCenter/ActionCenter.tsx @@ -11,7 +11,7 @@ import { Icon, IconButton, } from '@chakra-ui/react' -import { memo, useMemo, useState } from 'react' +import { memo, useCallback, useMemo, useState } from 'react' import { TbBellFilled } from 'react-icons/tb' import { useTranslate } from 'react-polyglot' import { Virtuoso } from 'react-virtuoso' @@ -25,6 +25,7 @@ import { GenericTransactionActionCard } from './components/GenericTransactionAct import { LimitOrderActionCard } from './components/LimitOrderActionCard' import { RewardDistributionActionCard } from './components/RewardDistributionActionCard' import { RfoxClaimActionCard } from './components/RfoxClaimActionCard' +import { SpeedUpModal } from './components/SpeedUpModal' import { SwapActionCard } from './components/SwapActionCard' import { TcyClaimActionCard } from './components/TcyClaimActionCard' @@ -41,6 +42,7 @@ import { selectWalletActionsSorted, selectWalletPendingActions, } from '@/state/slices/actionSlice/selectors' +import type { GenericTransactionAction } from '@/state/slices/actionSlice/types' import { ActionType, GenericTransactionDisplayType } from '@/state/slices/actionSlice/types' import { selectWalletType } from '@/state/slices/localWalletSlice/selectors' import { swapSlice } from '@/state/slices/swapSlice/swapSlice' @@ -77,6 +79,16 @@ export const ActionCenter = memo(() => { const translate = useTranslate() const [orderToCancel, setOrderToCancel] = useState(undefined) + const [speedUpAction, setSpeedUpAction] = useState( + undefined, + ) + const handleOpenSpeedUp = useCallback( + (action: GenericTransactionAction) => { + setSpeedUpAction(action) + closeDrawer() + }, + [closeDrawer], + ) const actions = useAppSelector(state => (isConnected ? selectWalletActionsSorted(state) : [])) @@ -133,7 +145,13 @@ export const ActionCenter = memo(() => { return } - return + return ( + + ) } case ActionType.RfoxClaim: { return @@ -157,7 +175,7 @@ export const ActionCenter = memo(() => { return actionsCards } - }, [actions, ordersByActionId, swapsById, setOrderToCancel]) + }, [actions, handleOpenSpeedUp, ordersByActionId, swapsById]) const actionCenterButton = useMemo(() => { if (pendingActions.length) { @@ -252,6 +270,18 @@ export const ActionCenter = memo(() => { + {speedUpAction && ( + setSpeedUpAction(undefined)} + /> + )} ) }) diff --git a/src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx b/src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx index 0646b0880d9..1c7ab095e58 100644 --- a/src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx +++ b/src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx @@ -42,6 +42,12 @@ export const ActionStatusIcon = ({ status }: { status?: ActionStatus }) => { ) + case ActionStatus.Replaced: + return ( + + + + ) case ActionStatus.Failed: case ActionStatus.Cancelled: case ActionStatus.Expired: diff --git a/src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx b/src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx index ae9d69b0932..64e2e5cf858 100644 --- a/src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx +++ b/src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx @@ -45,6 +45,12 @@ export const ActionStatusTag = ({ status }: ActionStatusTagProps) => { {translate('actionCenter.status.confirmed')} ) + case ActionStatus.Replaced: + return ( + + {translate('actionCenter.status.replaced')} + + ) case ActionStatus.Pending: return ( diff --git a/src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx b/src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx index 768d9597b96..7342720fb74 100644 --- a/src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx +++ b/src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx @@ -1,4 +1,5 @@ -import { Button, ButtonGroup, Link, Stack, useDisclosure } from '@chakra-ui/react' +import { Button, ButtonGroup, HStack, Link, Stack, useDisclosure } from '@chakra-ui/react' +import { btcChainId } from '@shapeshiftoss/caip' import dayjs from 'dayjs' import duration from 'dayjs/plugin/duration' import relativeTime from 'dayjs/plugin/relativeTime' @@ -12,10 +13,15 @@ import { ActionStatusTag } from './ActionStatusTag' import { AssetIconWithBadge } from '@/components/AssetIconWithBadge' import { useActionCenterContext } from '@/components/Layout/Header/ActionCenter/ActionCenterContext' +import { RawText } from '@/components/Text' import { getTxLink } from '@/lib/getTxLink' import { middleEllipsis } from '@/lib/utils' import type { GenericTransactionAction } from '@/state/slices/actionSlice/types' -import { ActionStatus, GenericTransactionDisplayType } from '@/state/slices/actionSlice/types' +import { + ActionStatus, + ActionType, + GenericTransactionDisplayType, +} from '@/state/slices/actionSlice/types' import { selectAssetById, selectFeeAssetByChainId } from '@/state/slices/assetsSlice/selectors' import { useAppSelector } from '@/state/store' @@ -24,9 +30,13 @@ dayjs.extend(relativeTime) type GenericTransactionActionCardProps = { action: GenericTransactionAction + onOpenSpeedUp?: (action: GenericTransactionAction) => void } -export const GenericTransactionActionCard = ({ action }: GenericTransactionActionCardProps) => { +export const GenericTransactionActionCard = ({ + action, + onOpenSpeedUp, +}: GenericTransactionActionCardProps) => { const translate = useTranslate() const navigate = useNavigate() const { closeDrawer } = useActionCenterContext() @@ -78,6 +88,30 @@ export const GenericTransactionActionCard = ({ action }: GenericTransactionActio }) }, [action.transactionMetadata.txHash, action.transactionMetadata.chainId, feeAsset]) + const replacementTxLink = useMemo(() => { + if (!feeAsset || !action.transactionMetadata.replacedByTxHash) return + + return getTxLink({ + txId: action.transactionMetadata.replacedByTxHash, + chainId: action.transactionMetadata.chainId, + defaultExplorerBaseUrl: feeAsset.explorerTxLink, + address: undefined, + maybeSafeTx: undefined, + }) + }, [action.transactionMetadata.chainId, action.transactionMetadata.replacedByTxHash, feeAsset]) + + const originalTxLink = useMemo(() => { + if (!feeAsset || !action.transactionMetadata.replacesTxHash) return + + return getTxLink({ + txId: action.transactionMetadata.replacesTxHash, + chainId: action.transactionMetadata.chainId, + defaultExplorerBaseUrl: feeAsset.explorerTxLink, + address: undefined, + maybeSafeTx: undefined, + }) + }, [action.transactionMetadata.chainId, action.transactionMetadata.replacesTxHash, feeAsset]) + const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: false }) const icon = useMemo(() => { @@ -100,19 +134,31 @@ export const GenericTransactionActionCard = ({ action }: GenericTransactionActio return dayjs.duration(remaining).humanize() }, [cooldownExpiryTimestamp]) - const description = useMemo( - () => - translate(action.transactionMetadata.message, { - ...action.transactionMetadata, - amount: action.transactionMetadata.amountCryptoPrecision, - symbol: asset?.symbol, - newAddress: middleEllipsis(action.transactionMetadata.newAddress ?? ''), - duration: cooldownDuration, - }), - [action.transactionMetadata, asset?.symbol, cooldownDuration, translate], - ) + const description = useMemo(() => { + const { + btcUtxoRbfTxMetadata: _btcUtxoRbfTxMetadata, + accountIdsToRefetch: _accountIdsToRefetch, + confirmedQuote: _confirmedQuote, + ...serializableMetadata + } = action.transactionMetadata + + return translate(action.transactionMetadata.message, { + ...serializableMetadata, + amount: action.transactionMetadata.amountCryptoPrecision, + symbol: asset?.symbol, + newAddress: middleEllipsis(action.transactionMetadata.newAddress ?? ''), + duration: cooldownDuration, + }) + }, [action.transactionMetadata, asset?.symbol, cooldownDuration, translate]) const isCollapsable = !!txLink || (isYieldClaim && action.status === ActionStatus.ClaimAvailable) + const isSpeedUpEligible = + action.type === ActionType.Send && + action.transactionMetadata.displayType === GenericTransactionDisplayType.SEND && + action.transactionMetadata.chainId === btcChainId && + action.status === ActionStatus.Pending && + !action.transactionMetadata.replacedByTxHash && + action.transactionMetadata.isRbfEnabled === true const details = useMemo(() => { if (isYieldClaim && action.status === ActionStatus.ClaimAvailable) { @@ -131,10 +177,63 @@ export const GenericTransactionActionCard = ({ action }: GenericTransactionActio + {isSpeedUpEligible && ( + + )} + {replacementTxLink && action.transactionMetadata.replacedByTxHash ? ( + + + {translate('actionCenter.replacementTransaction')} + + + {middleEllipsis(action.transactionMetadata.replacedByTxHash)} + + + ) : null} + {originalTxLink && action.transactionMetadata.replacesTxHash ? ( + + + {translate('actionCenter.originalTransaction')} + + + {middleEllipsis(action.transactionMetadata.replacesTxHash)} + + + ) : null} ) - }, [action.status, handleClaimClick, isYieldClaim, translate, txLink]) + }, [ + action, + handleClaimClick, + isYieldClaim, + isSpeedUpEligible, + onOpenSpeedUp, + originalTxLink, + replacementTxLink, + translate, + txLink, + ]) return ( void +} + +const BTC_DUST_THRESHOLD = 546 +const FETCH_RETRIES = 1 +const RETRY_DELAY_MS = 300 +const BROADCAST_VERIFY_RETRIES = 10 +const BROADCAST_VERIFY_DELAY_MS = 1000 +const MIN_REPLACEMENT_RELAY_FEE_RATE_SATS_PER_VB = 1 + +type ReconstructedInput = { + txid: string + vout: number + amount: string + addressNList: number[] + alternateAddressNList?: number[] + hex: string +} + +type ReconstructedOutput = { + address?: string + amount: string + isChange: boolean +} + +export const SpeedUpModal = ({ + txHash, + accountId, + assetId = BTC_ASSET_ID, + amountCryptoPrecision, + accountIdsToRefetch, + btcUtxoRbfTxMetadata, + isOpen, + onClose, +}: SpeedUpModalProps) => { + const translate = useTranslate() + const dispatch = useAppDispatch() + const toast = useToast() + const { + state: { wallet }, + } = useWallet() + + const pubkey = fromAccountId(accountId).account + + const btcAsset = useAppSelector(state => selectAssetById(state, BTC_ASSET_ID)) + const actionsById = useAppSelector(actionSlice.selectors.selectActionsById) + const btcMarketData = useAppSelector(state => + selectMarketDataByAssetIdUserCurrency(state, BTC_ASSET_ID), + ) + const accountMetadata = useAppSelector(state => + selectPortfolioAccountMetadataByAccountId(state, { accountId }), + ) + const btcPrecision = btcAsset?.precision ?? 8 + + const [selectedFeeRate, setSelectedFeeRate] = useState('0') + const [error, setError] = useState(null) + const [isLoading, setIsLoading] = useState(true) + const [originalFeeRate, setOriginalFeeRate] = useState('0') + const [originalVsize, setOriginalVsize] = useState('0') + const [originalFeeSats, setOriginalFeeSats] = useState('0') + + const [reconstructedInputs, setReconstructedInputs] = useState([]) + const [reconstructedOutputs, setReconstructedOutputs] = useState([]) + const [isAlreadyConfirmed, setIsAlreadyConfirmed] = useState(false) + const fetchedTxRef = useRef(null) + const intendedSendSats = useMemo(() => { + if (!amountCryptoPrecision) return undefined + if (!btcPrecision) return undefined + return bn(amountCryptoPrecision).times(bn(10).pow(btcPrecision)).integerValue().toFixed(0) + }, [amountCryptoPrecision, btcPrecision]) + + const sleep = useCallback((ms: number) => { + return new Promise(resolve => setTimeout(resolve, ms)) + }, []) + + const withRetry = useCallback( + async (fetcher: () => Promise): Promise => { + let lastError: unknown + for (let i = 0; i <= FETCH_RETRIES; i++) { + try { + return await fetcher() + } catch (e) { + lastError = e + if (i === FETCH_RETRIES) break + await sleep(RETRY_DELAY_MS * (i + 1)) + } + } + throw lastError + }, + [sleep], + ) + + useEffect(() => { + if (!isOpen) { + fetchedTxRef.current = null + return + } + if (!accountMetadata) return + if (fetchedTxRef.current === txHash) return + let cancelled = false + + const fetchTxData = async () => { + setIsLoading(true) + setError(null) + setIsAlreadyConfirmed(false) + + try { + const adapter = assertGetUtxoChainAdapter(btcChainId) + const httpProvider = adapter.httpProvider + + const [originalTx, utxos] = await Promise.all([ + withRetry(() => httpProvider.getTransaction({ txid: txHash })), + withRetry(() => httpProvider.getUtxos({ pubkey })), + ]) + + if (cancelled) return + const confirmed = Boolean((originalTx.confirmations ?? 0) > 0) + + const feeSats = getTxFeeSats(originalTx) + const txSize = getTxVsize(originalTx) + const feeRate = getDisplayFeeRateSatPerVbPrecise({ + tx: { + ...originalTx, + fee: feeSats.toFixed(0), + }, + }) + + setOriginalFeeRate(feeRate.toFixed(2)) + setOriginalVsize(txSize.toFixed(0)) + setOriginalFeeSats(feeSats.toFixed(0)) + setSelectedFeeRate(feeRate.toFixed(2)) + + const { bip44Params } = accountMetadata + const accountType = accountMetadata.accountType as UtxoAccountType + const account = await adapter.getAccount(pubkey) + const accountAddressIndexByAddress = new Map() + account.chainSpecific.addresses?.forEach((entry, index) => { + if (entry.pubkey) accountAddressIndexByAddress.set(entry.pubkey, index) + }) + + const utxoByAddress = new Map() + for (const utxo of utxos) { + if (utxo.address) { + utxoByAddress.set(utxo.address, { path: utxo.path }) + } + } + + const ownAddresses = new Set(utxos.map(u => u.address).filter(Boolean)) + const intendedPaymentIndices = + intendedSendSats !== undefined + ? originalTx.vout + .map((vout, index) => ({ index, value: String(vout.value ?? '0') })) + .filter(({ value }) => value === intendedSendSats) + .map(({ index }) => index) + : [] + const hasUniqueIntendedPaymentIndex = intendedPaymentIndices.length === 1 + const outputs: ReconstructedOutput[] = originalTx.vout.map((vout, index) => { + const address = vout.addresses?.[0] + const isChange = hasUniqueIntendedPaymentIndex + ? index !== intendedPaymentIndices[0] + : Boolean(address && ownAddresses.has(address)) + return { + address, + amount: vout.value, + isChange, + } + }) + + if (cancelled) return + setReconstructedOutputs(outputs) + setReconstructedInputs([]) + + if (confirmed) { + setIsAlreadyConfirmed(true) + return + } + + const reconstructInputs = async () => { + try { + const inputs: ReconstructedInput[] = await Promise.all( + originalTx.vin + .filter((vin): vin is typeof vin & { txid: string } => Boolean(vin.txid)) + .map(async (vin, index) => { + const metadataInput = btcUtxoRbfTxMetadata?.inputs[index] + const prevTx = await httpProvider.getTransaction({ txid: vin.txid }) + const vinAddress = vin.addresses?.[0] + const voutIndex = resolveVinVoutIndex({ + vinVout: vin.vout, + vinValue: vin.value, + vinAddress, + prevTxVouts: prevTx.vout, + }) + if (voutIndex === undefined) { + throw new Error(`Unable to resolve vin.vout for ${vin.txid}`) + } + const prevOutputAmount = prevTx.vout[voutIndex]?.value + const inputAmount = vin.value ?? prevOutputAmount ?? '0' + + const utxoInfo = vinAddress ? utxoByAddress.get(vinAddress) : undefined + const accountAddressIndex = vinAddress + ? accountAddressIndexByAddress.get(vinAddress) + : undefined + const receivePath = + accountAddressIndex !== undefined + ? toAddressNList( + adapter.getBip44Params({ + accountNumber: bip44Params.accountNumber, + accountType, + isChange: false, + addressIndex: accountAddressIndex, + }), + ) + : undefined + const changePath = + accountAddressIndex !== undefined + ? toAddressNList( + adapter.getBip44Params({ + accountNumber: bip44Params.accountNumber, + accountType, + isChange: true, + addressIndex: accountAddressIndex, + }), + ) + : undefined + + const metadataAddressNList = metadataInput?.addressNList + let addressNList = metadataAddressNList?.length + ? metadataAddressNList + : utxoInfo?.path + ? bip32ToAddressNList(utxoInfo.path) + : receivePath ?? toAddressNList(bip44Params) + let alternateAddressNList = + metadataAddressNList?.length || + utxoInfo?.path || + accountAddressIndex === undefined + ? undefined + : changePath + const walletWithBtcGetAddress = wallet as unknown as { + btcGetAddress?: (input: { + addressNList: number[] + coin: string + scriptType: BTCSignTx['inputs'][number]['scriptType'] + showDisplay: boolean + }) => Promise + } + + if ( + !utxoInfo?.path && + walletWithBtcGetAddress?.btcGetAddress && + vinAddress && + receivePath && + changePath + ) { + const [derivedReceiveAddress, derivedChangeAddress] = await Promise.all([ + walletWithBtcGetAddress.btcGetAddress({ + addressNList: receivePath, + coin: 'Bitcoin', + scriptType: accountTypeToScriptType[accountType], + showDisplay: false, + }), + walletWithBtcGetAddress.btcGetAddress({ + addressNList: changePath, + coin: 'Bitcoin', + scriptType: accountTypeToScriptType[accountType], + showDisplay: false, + }), + ]) + + if (derivedReceiveAddress === vinAddress) { + addressNList = receivePath + alternateAddressNList = changePath + } else if (derivedChangeAddress === vinAddress) { + addressNList = changePath + alternateAddressNList = receivePath + } + } + + return { + txid: vin.txid, + vout: voutIndex, + amount: inputAmount, + addressNList, + alternateAddressNList, + hex: prevTx.hex, + } + }), + ) + + if (cancelled) return + setReconstructedInputs(inputs) + } catch (inputError) { + console.error('Failed to reconstruct replacement tx inputs:', inputError) + if (cancelled) return + setError(translate('modals.send.speedUp.fetchError')) + } + } + await reconstructInputs() + fetchedTxRef.current = txHash + } catch (e) { + console.error('Failed to fetch transaction data:', e) + if (cancelled) return + setOriginalFeeRate(prev => (bn(prev).gt(0) ? prev : '1')) + setSelectedFeeRate(prev => (bn(prev).gt(0) ? prev : '1')) + setError(translate('modals.send.speedUp.fetchError')) + toast({ + title: translate('common.error'), + description: translate('modals.send.speedUp.fetchError'), + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-right', + }) + } finally { + if (!cancelled) { + setIsLoading(false) + } + } + } + + fetchTxData() + + return () => { + cancelled = true + } + }, [ + accountMetadata, + btcUtxoRbfTxMetadata, + isOpen, + onClose, + pubkey, + toast, + translate, + txHash, + wallet, + withRetry, + intendedSendSats, + ]) + + const quickMultiplierMarks = useMemo(() => { + const current = Number(originalFeeRate) + return [2, 3, 5] + .map(multiplier => ({ + value: current * multiplier, + label: `${multiplier}x`, + })) + .filter(mark => Number.isFinite(mark.value)) + }, [originalFeeRate]) + + const sliderMarks = useMemo(() => { + const current = Number(originalFeeRate) + const multiplierMarks = [2, 3, 5].map(multiplier => ({ + value: current * multiplier, + label: `${multiplier}x`, + })) + const marks = [{ value: current, label: translate('common.current') }, ...multiplierMarks] + return marks + .filter(mark => Number.isFinite(mark.value)) + .sort((a, b) => a.value - b.value) + .filter((mark, index, arr) => index === 0 || mark.value !== arr[index - 1].value) + }, [originalFeeRate, translate]) + + const sliderMin = useMemo(() => Number(originalFeeRate), [originalFeeRate]) + const sliderMax = useMemo(() => { + const maxMark = sliderMarks[sliderMarks.length - 1]?.value ?? sliderMin + return Math.max(sliderMin + 1, maxMark) + }, [sliderMarks, sliderMin]) + + const newFeeRate = useMemo(() => { + return selectedFeeRate || originalFeeRate + }, [selectedFeeRate, originalFeeRate]) + + const effectiveNewFeeSats = useMemo(() => { + if (!originalVsize || originalVsize === '0') return bn(0) + const computedNewFee = bn(originalVsize).times(newFeeRate).integerValue() + const minimumAdditionalRelayFee = bn(originalVsize) + .times(MIN_REPLACEMENT_RELAY_FEE_RATE_SATS_PER_VB) + .integerValue() + const minimumReplacementFee = bn(originalFeeSats) + .plus(minimumAdditionalRelayFee.gt(0) ? minimumAdditionalRelayFee : 1) + .integerValue() + return computedNewFee.lte(minimumReplacementFee) ? minimumReplacementFee : computedNewFee + }, [newFeeRate, originalFeeSats, originalVsize]) + + const previousFeeCrypto = useMemo(() => { + if (!originalFeeSats || originalFeeSats === '0') return bn(0) + return bn( + BigAmount.fromBaseUnit({ + value: originalFeeSats, + precision: btcPrecision, + }).toPrecision(), + ) + }, [btcPrecision, originalFeeSats]) + + const newFeeCrypto = useMemo(() => { + if (effectiveNewFeeSats.lte(0)) return bn(0) + return bn( + BigAmount.fromBaseUnit({ + value: effectiveNewFeeSats.toFixed(0), + precision: btcPrecision, + }).toPrecision(), + ) + }, [btcPrecision, effectiveNewFeeSats]) + + const additionalFeeCrypto = useMemo(() => { + return newFeeCrypto.minus(previousFeeCrypto) + }, [newFeeCrypto, previousFeeCrypto]) + + const previousFeeFiat = useMemo( + () => previousFeeCrypto.times(btcMarketData?.price ?? 0), + [btcMarketData?.price, previousFeeCrypto], + ) + const newFeeFiat = useMemo( + () => newFeeCrypto.times(btcMarketData?.price ?? 0), + [btcMarketData?.price, newFeeCrypto], + ) + const additionalFeeFiat = useMemo( + () => additionalFeeCrypto.times(btcMarketData?.price ?? 0), + [additionalFeeCrypto, btcMarketData?.price], + ) + + const hasInsufficientFunds = useMemo(() => { + if (reconstructedInputs.length === 0 || reconstructedOutputs.length === 0) return false + const totalInputValue = reconstructedInputs.reduce( + (sum, input) => sum.plus(input.amount), + bn(0), + ) + const totalPaymentValue = reconstructedOutputs + .filter(o => !o.isChange) + .reduce((sum, o) => sum.plus(o.amount), bn(0)) + const newFee = effectiveNewFeeSats + return totalInputValue.minus(totalPaymentValue).minus(newFee).lt(0) + }, [effectiveNewFeeSats, reconstructedInputs, reconstructedOutputs]) + + const speedUpMutation = useMutation({ + onMutate: () => setError(null), + mutationFn: async () => { + if (!wallet || !accountMetadata || reconstructedInputs.length === 0) return + + const adapter = assertGetUtxoChainAdapter(btcChainId) + const accountType = accountMetadata.accountType as UtxoAccountType + const latestTx = await adapter.httpProvider.getTransaction({ txid: txHash }).catch(() => null) + if (!latestTx || (latestTx.confirmations ?? 0) > 0) { + throw new Error(translate('modals.send.speedUp.alreadySpentOrReplaced')) + } + + const totalInputValue = reconstructedInputs.reduce( + (sum, input) => sum.plus(input.amount), + bn(0), + ) + const paymentOutputs = reconstructedOutputs.filter(o => !o.isChange) + const totalPaymentValue = paymentOutputs.reduce((sum, o) => sum.plus(o.amount), bn(0)) + + const newFee = effectiveNewFeeSats + const newChange = totalInputValue.minus(totalPaymentValue).minus(newFee) + + const account = await adapter.getAccount(pubkey) + const changeBip44Params = adapter.getBip44Params({ + accountNumber: accountMetadata.bip44Params.accountNumber, + accountType, + isChange: true, + addressIndex: account.chainSpecific.nextChangeAddressIndex, + }) + + const outputs: BTCSignTx['outputs'] = paymentOutputs + .filter((o): o is typeof o & { address: string } => Boolean(o.address)) + .map(o => ({ + addressType: BTCOutputAddressType.Spend, + address: o.address, + amount: o.amount, + })) as BTCSignTx['outputs'] + + if (newChange.gte(BTC_DUST_THRESHOLD)) { + ;(outputs as unknown as unknown[]).push({ + addressType: BTCOutputAddressType.Change, + addressNList: toAddressNList(changeBip44Params), + scriptType: accountTypeToOutputScriptType[accountType], + amount: newChange.toFixed(0), + isChange: true, + }) + } + + const ambiguousInputIndices = reconstructedInputs.reduce((acc, input, index) => { + if (input.alternateAddressNList) acc.push(index) + return acc + }, []) + + const maxAttempts = Math.min(1 << ambiguousInputIndices.length, 16) + let replacementTxHash: string | undefined + let replacementBtcUtxoRbfTxMetadata: BtcUtxoRbfTxMetadata | undefined + let lastError: unknown + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + const txInputs = reconstructedInputs.map((input, index) => { + const ambiguousIndex = ambiguousInputIndices.indexOf(index) + const useAlternate = + ambiguousIndex >= 0 && input.alternateAddressNList + ? Boolean((attempt >> ambiguousIndex) & 1) + : false + + return { + addressNList: + useAlternate && input.alternateAddressNList + ? input.alternateAddressNList + : input.addressNList, + scriptType: accountTypeToScriptType[accountType], + amount: input.amount, + vout: input.vout, + txid: input.txid, + hex: input.hex, + sequence: 0xfffffffd, + } + }) as BTCSignTx['inputs'] + replacementBtcUtxoRbfTxMetadata = { + inputs: txInputs.map(input => ({ addressNList: input.addressNList })), + } + + const txToSign: BTCSignTx = { + coin: 'Bitcoin', + inputs: txInputs, + outputs, + version: 1, + locktime: 0, + } + try { + const signedTx = await adapter.signTransaction({ txToSign, wallet }) + const signedTxId = getTxIdFromHex(signedTx) + + const broadcastResult = await adapter.broadcastTransaction({ hex: signedTx }) + const broadcastTxId = String(broadcastResult).trim() + + const candidateTxIds = [ + ...new Set([broadcastTxId, signedTxId].filter(isLikelyBitcoinTxId)), + ] + if (!candidateTxIds.length) { + throw new Error(`Unexpected broadcast txid response: ${broadcastResult}`) + } + + let resolvedTxId: string | undefined + for (const candidateTxId of candidateTxIds) { + for (let i = 0; i <= BROADCAST_VERIFY_RETRIES; i++) { + const observedTx = await adapter.httpProvider + .getTransaction({ txid: candidateTxId }) + .catch(() => null) + if (observedTx) { + resolvedTxId = candidateTxId + break + } + if (i < BROADCAST_VERIFY_RETRIES) await sleep(BROADCAST_VERIFY_DELAY_MS) + } + if (resolvedTxId) break + } + + if (!resolvedTxId) throw new Error(translate('modals.send.speedUp.broadcastNotObserved')) + + replacementTxHash = resolvedTxId + break + } catch (signError) { + lastError = signError + } + } + + if (!replacementTxHash) throw lastError ?? new Error('Failed to sign replacement transaction') + + dispatch( + actionSlice.actions.upsertAction({ + id: replacementTxHash, + type: ActionType.Send, + transactionMetadata: { + displayType: GenericTransactionDisplayType.SEND, + txHash: replacementTxHash, + chainId: btcChainId, + accountId, + accountIdsToRefetch, + assetId, + amountCryptoPrecision, + message: 'modals.send.status.pendingBody', + replacesTxHash: txHash, + isRbfEnabled: true, + btcUtxoRbfTxMetadata: replacementBtcUtxoRbfTxMetadata, + }, + status: ActionStatus.Pending, + createdAt: Date.now(), + updatedAt: Date.now(), + }), + ) + + const replacedAction = actionsById[txHash] + if ( + replacedAction && + isGenericTransactionAction(replacedAction) && + replacedAction.status === ActionStatus.Pending + ) { + dispatch( + actionSlice.actions.upsertAction({ + ...replacedAction, + status: ActionStatus.Replaced, + updatedAt: Date.now(), + transactionMetadata: { + ...replacedAction.transactionMetadata, + message: 'transactionHistory.replaced', + replacedByTxHash: replacementTxHash, + }, + }), + ) + } + + onClose() + }, + onError: e => { + console.error('Speed up failed:', e) + const message = + e instanceof Error ? e.message : translate('modals.send.speedUp.speedUpFailed') + + setError(message) + toast({ + title: translate('common.error'), + description: message, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-right', + }) + }, + }) + + if (!btcAsset) return null + + return ( + + + + {translate('modals.send.speedUp.title')} + + + {isLoading ? ( + + + + ) : ( + + + + {translate('modals.send.speedUp.currentFeeRate')} + + + {originalFeeRate} sat/B + + + + + {translate('modals.send.speedUp.selectFeeRate')} + + + setSelectedFeeRate(String(value))} + > + {sliderMarks.map(mark => ( + + {mark.label} + + ))} + + + + + + + + {bn(newFeeRate).toFixed(2)} sat/B + + + {quickMultiplierMarks.map(mark => ( + setSelectedFeeRate(String(mark.value))} + > + {mark.label} + + ))} + + + + + {translate('modals.send.speedUp.previousFee')} + + + + + + + + + {translate('modals.send.speedUp.newFee')} + + + + + + + + + {translate('modals.send.speedUp.additionalFee')} + + + + + + + {hasInsufficientFunds && ( + + {translate('modals.send.speedUp.insufficientFunds')} + + )} + {isAlreadyConfirmed && ( + + {translate('modals.send.speedUp.alreadyConfirmed')} + + )} + + )} + + + + + + + ) +} diff --git a/src/components/Layout/Header/ActionCenter/components/speedUpUtils.test.ts b/src/components/Layout/Header/ActionCenter/components/speedUpUtils.test.ts new file mode 100644 index 00000000000..3f5cf4308d6 --- /dev/null +++ b/src/components/Layout/Header/ActionCenter/components/speedUpUtils.test.ts @@ -0,0 +1,234 @@ +import { Transaction } from '@shapeshiftoss/bitcoinjs-lib' +import { describe, expect, it, vi } from 'vitest' + +import { + getDisplayFeeRateSatPerVb, + getDisplayFeeRateSatPerVbPrecise, + getTxFeeRateSatPerVb, + getTxFeeRateSatPerVbPrecise, + getTxFeeSats, + getTxIdFromHex, + getTxVsize, + isLikelyBitcoinTxId, + resolveVinVoutIndex, + toSats, +} from './speedUpUtils' + +describe('speedUpUtils', () => { + describe('toSats', () => { + it('parses satoshi integer strings', () => { + expect(toSats('512').toString()).toBe('512') + }) + + it('parses btc decimal strings to sats', () => { + expect(toSats('0.00000512').toString()).toBe('512') + }) + + it('parses btc scientific notation to sats', () => { + expect(toSats('1e-8').toString()).toBe('1') + }) + }) + + describe('getTxVsize', () => { + it('uses explicit vsize first', () => { + const tx = { + vsize: 246, + vin: [], + vout: [], + } + expect(getTxVsize(tx).toString()).toBe('246') + }) + + it('falls back to weight/4', () => { + const tx = { + weight: 981, + vin: [], + vout: [], + } + expect(getTxVsize(tx).toString()).toBe('246') + }) + + it('rounds up weight/4 deterministically for odd weights', () => { + const tx = { + weight: 983, + vin: [], + vout: [], + } + expect(getTxVsize(tx).toString()).toBe('246') + }) + + it('uses bitcoinjs virtual size from hex when vsize and weight are missing', () => { + const fromHexSpy = vi.spyOn(Transaction, 'fromHex').mockReturnValue({ + virtualSize: () => 246, + } as Transaction) + + const tx = { + hex: 'deadbeef', + vin: [], + vout: [], + } + + expect(getTxVsize(tx).toString()).toBe('246') + expect(fromHexSpy).toHaveBeenCalledWith('deadbeef') + fromHexSpy.mockRestore() + }) + }) + + describe('fee calculations', () => { + it('uses tx.fee when present', () => { + const tx = { + fee: '512', + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + + expect(getTxFeeSats(tx).toString()).toBe('512') + }) + + it('falls back to vin-vout fee derivation when fee is missing', () => { + const tx = { + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + + expect(getTxFeeSats(tx).toString()).toBe('512') + }) + + it('derives current fee rate for the reported tx example as 2 sat/vB', () => { + const tx = { + fee: '512', + weight: 981, + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + + expect(getTxFeeRateSatPerVb(tx).toString()).toBe('2') + }) + + it('derives precise fee rate for the reported tx example as 2.09 sat/vB', () => { + const tx = { + fee: '512', + weight: 981, + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + + expect(getTxFeeRateSatPerVbPrecise(tx).toFixed(2)).toBe('2.09') + }) + }) + + describe('getDisplayFeeRateSatPerVb', () => { + it('prefers tx fee rate over network average', () => { + const tx = { + fee: '512', + weight: 981, + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + expect( + getDisplayFeeRateSatPerVb({ + tx, + networkAverageFeeRateSatPerVb: '1', + }).toString(), + ).toBe('2') + }) + + it('falls back to network average when tx fee rate is unavailable', () => { + const tx = { + fee: '0', + vin: [], + vout: [], + } + expect( + getDisplayFeeRateSatPerVb({ + tx, + networkAverageFeeRateSatPerVb: '11', + }).toString(), + ).toBe('11') + }) + }) + + describe('getDisplayFeeRateSatPerVbPrecise', () => { + it('prefers precise tx fee rate over network average', () => { + const tx = { + fee: '512', + weight: 981, + vin: [{ value: '3422' }, { value: '3089' }, { value: '1001' }], + vout: [{ value: '7000' }], + } + expect( + getDisplayFeeRateSatPerVbPrecise({ + tx, + networkAverageFeeRateSatPerVb: '1', + }).toFixed(2), + ).toBe('2.09') + }) + }) + + describe('resolveVinVoutIndex', () => { + it('uses provided vin.vout directly when present', () => { + const index = resolveVinVoutIndex({ + vinVout: 1, + vinValue: '5000', + vinAddress: 'bc1qabc', + prevTxVouts: [{ value: '1111' }, { value: '5000' }], + }) + + expect(index).toBe(1) + }) + + it('ignores invalid vin.vout values and falls back to matching heuristics', () => { + const index = resolveVinVoutIndex({ + vinVout: 'not-a-number', + vinValue: '5000', + vinAddress: 'bc1qxyz', + prevTxVouts: [ + { value: '5000', addresses: ['bc1qabc'] }, + { value: '5000', addresses: ['bc1qxyz'] }, + ], + }) + + expect(index).toBe(1) + }) + + it('resolves by unique address+value match when vin.vout is missing', () => { + const index = resolveVinVoutIndex({ + vinValue: '5000', + vinAddress: 'bc1qxyz', + prevTxVouts: [ + { value: '5000', addresses: ['bc1qabc'] }, + { value: '5000', addresses: ['bc1qxyz'] }, + ], + }) + + expect(index).toBe(1) + }) + + it('returns undefined when there is no unique match', () => { + const index = resolveVinVoutIndex({ + vinValue: '5000', + vinAddress: 'bc1qnomatch', + prevTxVouts: [{ value: '5000' }, { value: '5000' }], + }) + + expect(index).toBeUndefined() + }) + }) + + describe('txid helpers', () => { + it('extracts txid from signed tx hex', () => { + const txid = getTxIdFromHex( + '0100000000010105abd41ac558c186429b77a2344106bdd978955fc407e3363239864cb479b9ad0000000000fdffffff02900100000000000016001408450440a15ea38314c52d5c9ae6201857d7cf7a677a000000000000160014bf44db911ae5acc9cffcc1bbb9622ddda4a1112b024730440220261bd026ab75ed19ee9b537204c38953593d37b1f1819fdcedfc9e494ae8503902204f38ac8cbf3145e83bc0578866fd4508fcc97bb57d70b6688253558639a8a4a50121029dc27a53da073b1fea5601cf370d02d3b33cf572156c3a6df9d5c03c5dbcdcd700000000', + ) + + expect(txid).toBe('04a551347291f60a37a632243ed4d4b5320e1269148a02bb55fdc678d7c84a0f') + }) + + it('validates bitcoin txid format', () => { + expect( + isLikelyBitcoinTxId('35f171f3008a45a9071c4f1a8a75c95debc4ad438dc8453b93f25ea43d7ab1e4'), + ).toBe(true) + expect(isLikelyBitcoinTxId('not-a-txid')).toBe(false) + }) + }) +}) diff --git a/src/components/Layout/Header/ActionCenter/components/speedUpUtils.ts b/src/components/Layout/Header/ActionCenter/components/speedUpUtils.ts new file mode 100644 index 00000000000..42d0417711d --- /dev/null +++ b/src/components/Layout/Header/ActionCenter/components/speedUpUtils.ts @@ -0,0 +1,186 @@ +import { Transaction } from '@shapeshiftoss/bitcoinjs-lib' +import BigNumber from 'bignumber.js' + +import { bn, bnOrZero } from '@/lib/bignumber/bignumber' + +type TxValue = string | number | null | undefined + +type VinLike = { + value?: TxValue + addresses?: string[] +} + +type VoutLike = { + value?: TxValue + addresses?: string[] +} + +export type SpeedUpTxLike = { + fee?: TxValue + vsize?: TxValue + weight?: TxValue + hex?: string + vin: VinLike[] + vout: VoutLike[] +} + +export const toSats = (value?: TxValue) => { + if (value === undefined || value === null) return bn(0) + const parsed = String(value) + const isPrecisionValue = /[.eE]/.test(parsed) + return isPrecisionValue ? bn(parsed).times('100000000') : bn(parsed) +} + +export const getTxVsize = (tx: SpeedUpTxLike) => { + const vsize = bnOrZero(tx.vsize) + if (vsize.gt(0)) return vsize + + const weight = bnOrZero(tx.weight) + if (weight.gt(0)) return weight.div(4).integerValue(BigNumber.ROUND_CEIL) + + if (tx.hex) { + try { + return bn(Transaction.fromHex(tx.hex).virtualSize()) + } catch { + return bn(tx.hex.length / 2) + } + } + + return bn(0) +} + +export const getTxVirtualBytes = (tx: SpeedUpTxLike) => { + const vsize = bnOrZero(tx.vsize) + if (vsize.gt(0)) return vsize + + const weight = bnOrZero(tx.weight) + if (weight.gt(0)) return weight.div(4) + + if (tx.hex) { + try { + return bn(Transaction.fromHex(tx.hex).weight()).div(4) + } catch { + return bn(tx.hex.length / 2) + } + } + + return bn(0) +} + +export const getTxFeeSats = (tx: SpeedUpTxLike) => { + const feeSats = toSats(tx.fee) + if (feeSats.gt(0)) return feeSats + + const totalInputSats = tx.vin.reduce((sum, vin) => sum.plus(toSats(vin.value)), bn(0)) + const totalOutputSats = tx.vout.reduce((sum, vout) => sum.plus(toSats(vout.value)), bn(0)) + + if (totalInputSats.lte(0) || totalOutputSats.lte(0)) return bn(0) + + return totalInputSats.minus(totalOutputSats) +} + +export const getTxFeeRateSatPerVbPrecise = (tx: SpeedUpTxLike) => { + const virtualBytes = getTxVirtualBytes(tx) + if (virtualBytes.lte(0)) return bn(0) + + const feeSats = getTxFeeSats(tx) + if (feeSats.lte(0)) return bn(0) + + return feeSats.div(virtualBytes) +} + +export const getTxFeeRateSatPerVb = (tx: SpeedUpTxLike) => { + return getTxFeeRateSatPerVbPrecise(tx).integerValue() +} + +export const getDisplayFeeRateSatPerVb = ({ + tx, + networkAverageFeeRateSatPerVb, +}: { + tx: SpeedUpTxLike + networkAverageFeeRateSatPerVb?: string | number +}) => { + const txFeeRate = getTxFeeRateSatPerVb(tx) + if (txFeeRate.gt(0)) return txFeeRate + + const networkFeeRate = bnOrZero(networkAverageFeeRateSatPerVb).integerValue() + if (networkFeeRate.gt(0)) return networkFeeRate + + return bn(1) +} + +export const getDisplayFeeRateSatPerVbPrecise = ({ + tx, + networkAverageFeeRateSatPerVb, +}: { + tx: SpeedUpTxLike + networkAverageFeeRateSatPerVb?: string | number +}) => { + const txFeeRate = getTxFeeRateSatPerVbPrecise(tx) + if (txFeeRate.gt(0)) return txFeeRate + + const networkFeeRate = bnOrZero(networkAverageFeeRateSatPerVb) + if (networkFeeRate.gt(0)) return networkFeeRate + + return bn(1) +} + +export const resolveVinVoutIndex = ({ + vinVout, + vinValue, + vinAddress, + prevTxVouts, +}: { + vinVout?: string | number | null + vinValue?: TxValue + vinAddress?: string + prevTxVouts: VoutLike[] +}) => { + const vinValueSats = + vinValue !== undefined && vinValue !== null ? toSats(vinValue).toFixed(0) : undefined + + if (vinVout !== undefined && vinVout !== null) { + const parsedIndex = Number(vinVout) + if (Number.isInteger(parsedIndex) && parsedIndex >= 0 && parsedIndex < prevTxVouts.length) { + return parsedIndex + } + } + + const valueMatches = prevTxVouts + .map((vout, index) => ({ + index, + valueSats: + vout.value !== undefined && vout.value !== null ? toSats(vout.value).toFixed(0) : undefined, + addresses: vout.addresses ?? [], + })) + .filter(vout => (vinValueSats !== undefined ? vout.valueSats === vinValueSats : true)) + + const addressAndValueMatches = valueMatches.filter(vout => + vinAddress ? vout.addresses.includes(vinAddress) : true, + ) + if (addressAndValueMatches.length === 1) return addressAndValueMatches[0].index + + if (valueMatches.length === 1) return valueMatches[0].index + + const addressMatches = prevTxVouts + .map((vout, index) => ({ index, addresses: vout.addresses ?? [] })) + .filter(vout => (vinAddress ? vout.addresses.includes(vinAddress) : false)) + if (addressMatches.length === 1) return addressMatches[0].index + + return undefined +} + +export const getTxIdFromHex = (hex?: string) => { + if (!hex) return undefined + + try { + return Transaction.fromHex(hex).getId() + } catch { + return undefined + } +} + +export const isLikelyBitcoinTxId = (value: unknown): value is string => { + if (typeof value !== 'string') return false + return /^[a-fA-F0-9]{64}$/.test(value.trim()) +} diff --git a/src/components/Modals/QrCode/Form.tsx b/src/components/Modals/QrCode/Form.tsx index e969cbd4beb..4886637235a 100644 --- a/src/components/Modals/QrCode/Form.tsx +++ b/src/components/Modals/QrCode/Form.tsx @@ -115,15 +115,16 @@ export const Form: React.FC = ({ assetId: initialAssetId, accou const handleSubmit = useCallback( async (data: SendInput) => { - const txHash = await handleFormSend(data, false) - if (!txHash) return + const sendResult = await handleFormSend(data, false) + if (!sendResult?.txHash) return completeSendFlow({ - txHash, + txHash: sendResult.txHash, to: data.to, accountId: data.accountId, assetId: data.assetId, amountCryptoPrecision: data.amountCryptoPrecision, + btcUtxoRbfTxMetadata: sendResult.btcUtxoRbfTxMetadata, }) }, [handleFormSend, completeSendFlow], diff --git a/src/components/Modals/Send/Form.tsx b/src/components/Modals/Send/Form.tsx index 65b3d03627f..850bc578001 100644 --- a/src/components/Modals/Send/Form.tsx +++ b/src/components/Modals/Send/Form.tsx @@ -128,17 +128,18 @@ export const Form: React.FC = ({ initialAssetId, input = '', acco methods.setValue(SendFormFields.ChangeAddress, changeAddress) } - const txHash = await handleFormSend(data, false) - if (!txHash) return + const sendResult = await handleFormSend(data, false) + if (!sendResult?.txHash) return mixpanel?.track(MixPanelEvent.SendBroadcast) completeSendFlow({ - txHash, + txHash: sendResult.txHash, to: data.to, accountId: data.accountId, assetId: data.assetId, amountCryptoPrecision: data.amountCryptoPrecision, + btcUtxoRbfTxMetadata: sendResult.btcUtxoRbfTxMetadata, }) }, [wallet, methods, handleFormSend, mixpanel, completeSendFlow], diff --git a/src/components/Modals/Send/hooks/useCompleteSendFlow.tsx b/src/components/Modals/Send/hooks/useCompleteSendFlow.tsx index bec835962a8..26f68efc90c 100644 --- a/src/components/Modals/Send/hooks/useCompleteSendFlow.tsx +++ b/src/components/Modals/Send/hooks/useCompleteSendFlow.tsx @@ -1,15 +1,18 @@ import type { AccountId, AssetId } from '@shapeshiftoss/caip' -import { CHAIN_NAMESPACE, fromAccountId, toAccountId } from '@shapeshiftoss/caip' +import { btcChainId, CHAIN_NAMESPACE, fromAccountId, toAccountId } from '@shapeshiftoss/caip' import { useCallback } from 'react' import { useActionCenterContext } from '@/components/Layout/Header/ActionCenter/ActionCenterContext' import { GenericTransactionNotification } from '@/components/Layout/Header/ActionCenter/components/Notifications/GenericTransactionNotification' import { useNotificationToast } from '@/hooks/useNotificationToast' +import { assertGetUtxoChainAdapter } from '@/lib/utils/utxo' import { actionSlice } from '@/state/slices/actionSlice/actionSlice' +import type { BtcUtxoRbfTxMetadata } from '@/state/slices/actionSlice/types' import { ActionStatus, ActionType, GenericTransactionDisplayType, + isGenericTransactionAction, } from '@/state/slices/actionSlice/types' import { selectInternalAccountIdByAddress } from '@/state/slices/addressBookSlice/selectors' import { store, useAppDispatch } from '@/state/store' @@ -24,6 +27,7 @@ type CompleteSendFlowArgs = { accountId: AccountId assetId: AssetId amountCryptoPrecision: string + btcUtxoRbfTxMetadata?: BtcUtxoRbfTxMetadata } export const useCompleteSendFlow = ({ handleClose }: UseCompleteSendFlowArgs) => { @@ -32,7 +36,14 @@ export const useCompleteSendFlow = ({ handleClose }: UseCompleteSendFlowArgs) => const toast = useNotificationToast({ duration: isDrawerOpen ? 5000 : null }) return useCallback( - ({ txHash, to, accountId, assetId, amountCryptoPrecision }: CompleteSendFlowArgs) => { + ({ + txHash, + to, + accountId, + assetId, + amountCryptoPrecision, + btcUtxoRbfTxMetadata, + }: CompleteSendFlowArgs) => { const { chainId, chainNamespace } = fromAccountId(accountId) const internalReceiveAccountId = @@ -62,6 +73,8 @@ export const useCompleteSendFlow = ({ handleClose }: UseCompleteSendFlowArgs) => assetId, amountCryptoPrecision, message: 'modals.send.status.pendingBody', + isRbfEnabled: chainId === btcChainId, + btcUtxoRbfTxMetadata, }, status: ActionStatus.Pending, createdAt: Date.now(), @@ -69,6 +82,64 @@ export const useCompleteSendFlow = ({ handleClose }: UseCompleteSendFlowArgs) => }), ) + if (chainId === btcChainId) { + void (async () => { + const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + try { + const adapter = assertGetUtxoChainAdapter(btcChainId) + const fetchTransactionWithRetry = async ( + txid: string, + ): Promise>> => { + for (let i = 0; i < 6; i++) { + try { + return await adapter.httpProvider.getTransaction({ txid }) + } catch (e) { + if (i === 5) throw e + await sleep(1000) + } + } + + throw new Error('Failed to fetch transaction after retries') + } + const tx = await fetchTransactionWithRetry(txHash) + const isRbfEnabled = tx.vin.some( + vin => typeof vin.sequence === 'number' && vin.sequence < 0xfffffffe, + ) + + const existingAction = actionSlice.selectors.selectActionsById(store.getState())[txHash] + if (!existingAction || !isGenericTransactionAction(existingAction)) return + + dispatch( + actionSlice.actions.upsertAction({ + ...existingAction, + transactionMetadata: { + ...existingAction.transactionMetadata, + isRbfEnabled, + btcUtxoRbfTxMetadata: + btcUtxoRbfTxMetadata ?? existingAction.transactionMetadata.btcUtxoRbfTxMetadata, + }, + updatedAt: Date.now(), + }), + ) + } catch (e) { + console.error('Failed to resolve BTC RBF capability:', e) + const existingAction = actionSlice.selectors.selectActionsById(store.getState())[txHash] + if (!existingAction || !isGenericTransactionAction(existingAction)) return + + dispatch( + actionSlice.actions.upsertAction({ + ...existingAction, + transactionMetadata: { + ...existingAction.transactionMetadata, + isRbfEnabled: false, + }, + updatedAt: Date.now(), + }), + ) + } + })() + } + toast({ id: txHash, duration: isDrawerOpen ? 5000 : null, diff --git a/src/components/Modals/Send/hooks/useFormSend/useFormSend.tsx b/src/components/Modals/Send/hooks/useFormSend/useFormSend.tsx index bd3917dea64..f999c308fc3 100644 --- a/src/components/Modals/Send/hooks/useFormSend/useFormSend.tsx +++ b/src/components/Modals/Send/hooks/useFormSend/useFormSend.tsx @@ -5,14 +5,20 @@ import { useCallback } from 'react' import { useTranslate } from 'react-polyglot' import type { SendInput } from '../../Form' -import { handleSend } from '../../utils' +import { handleSendWithMetadata } from '../../utils' import { InlineCopyButton } from '@/components/InlineCopyButton' import { RawText } from '@/components/Text' import { useWallet } from '@/hooks/useWallet/useWallet' +import type { BtcUtxoRbfTxMetadata } from '@/state/slices/actionSlice/types' import { selectAssetById } from '@/state/slices/selectors' import { store } from '@/state/store' +type HandleFormSendResult = { + txHash: string + btcUtxoRbfTxMetadata?: BtcUtxoRbfTxMetadata +} + export const useFormSend = () => { const toast = useToast() const translate = useTranslate() @@ -21,13 +27,16 @@ export const useFormSend = () => { } = useWallet() const handleFormSend = useCallback( - async (sendInput: SendInput, toastOnBroadcast: boolean): Promise => { + async ( + sendInput: SendInput, + toastOnBroadcast: boolean, + ): Promise => { try { const asset = selectAssetById(store.getState(), sendInput.assetId) if (!asset) throw new Error(`No asset found for assetId ${sendInput.assetId}`) if (!wallet) throw new Error('No wallet connected') - const broadcastTXID = await handleSend({ wallet, sendInput }) + const { txHash, btcUtxoRbfTxMetadata } = await handleSendWithMetadata({ wallet, sendInput }) setTimeout(() => { if (!toastOnBroadcast) return @@ -43,7 +52,7 @@ export const useFormSend = () => { })} {asset.explorerTxLink && ( - + {translate('modals.status.viewExplorer')} )} @@ -56,7 +65,7 @@ export const useFormSend = () => { }) }, 5000) - return broadcastTXID + return { txHash, btcUtxoRbfTxMetadata } } catch (e) { const asset = selectAssetById(store.getState(), sendInput.assetId) console.error(e) @@ -83,7 +92,7 @@ export const useFormSend = () => { position: 'top-right', }) - return '' + return undefined } }, [toast, translate, wallet], diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index 3a04495312c..2b2ef38e06b 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -1,5 +1,6 @@ import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' import { + btcChainId, CHAIN_NAMESPACE, fromAccountId, fromChainId, @@ -38,6 +39,7 @@ import { assertGetStarknetChainAdapter } from '@/lib/utils/starknet' import { assertGetSuiChainAdapter } from '@/lib/utils/sui' import { assertGetTonChainAdapter } from '@/lib/utils/ton' import { assertGetUtxoChainAdapter, isUtxoChainId } from '@/lib/utils/utxo' +import type { BtcUtxoRbfTxMetadata } from '@/state/slices/actionSlice/types' import { selectAssetById, selectPortfolioAccountMetadataByAccountId, @@ -208,6 +210,44 @@ export const handleSend = async ({ sendInput: SendInput wallet: HDWallet }): Promise => { + const { txHash } = await handleSendWithMetadata({ sendInput, wallet }) + return txHash +} + +type HandleSendWithMetadataResult = { + txHash: string + btcUtxoRbfTxMetadata?: BtcUtxoRbfTxMetadata +} + +const getBtcUtxoRbfTxMetadata = (txToSign: unknown): BtcUtxoRbfTxMetadata | undefined => { + const maybeBtcTx = txToSign as { + inputs?: { + addressNList?: number[] + }[] + } + + const inputs = maybeBtcTx.inputs + ?.filter( + ( + input, + ): input is { + addressNList: number[] + } => Array.isArray(input.addressNList), + ) + .map(input => ({ addressNList: input.addressNList })) + + if (!inputs?.length) return undefined + + return { inputs } +} + +export const handleSendWithMetadata = async ({ + sendInput, + wallet, +}: { + sendInput: SendInput + wallet: HDWallet +}): Promise => { const { asset, chainId, accountMetadata, adapter } = prepareSendAdapter(sendInput) const supportedEvmChainIds = getSupportedEvmChainIds() const isMetaMaskDesktop = checkIsMetaMaskDesktop(wallet) @@ -512,7 +552,10 @@ export const handleSend = async ({ throw new Error('Broadcast failed') } - return broadcastTXID + return { + txHash: broadcastTXID, + btcUtxoRbfTxMetadata: chainId === btcChainId ? getBtcUtxoRbfTxMetadata(txToSign) : undefined, + } } const prepareSendAdapter = (sendInput: SendInput) => { diff --git a/src/components/TransactionHistoryRows/TransactionCommon.tsx b/src/components/TransactionHistoryRows/TransactionCommon.tsx index 545588b82af..5a3387e79b0 100644 --- a/src/components/TransactionHistoryRows/TransactionCommon.tsx +++ b/src/components/TransactionHistoryRows/TransactionCommon.tsx @@ -1,5 +1,8 @@ -import { TransferType } from '@shapeshiftoss/unchained-client' -import { useMemo } from 'react' +import { Button } from '@chakra-ui/react' +import { btcChainId, toAccountId } from '@shapeshiftoss/caip' +import { TransferType, TxStatus } from '@shapeshiftoss/unchained-client' +import { useCallback, useMemo, useState } from 'react' +import { useTranslate } from 'react-polyglot' import { TransactionDate } from './TransactionDate' import { Amount } from './TransactionDetails/Amount' @@ -13,6 +16,7 @@ import { TransactionGenericRow } from './TransactionGenericRow' import type { TransactionRowProps } from './TransactionRow' import { getTransfersByType } from './utils' +import { SpeedUpModal } from '@/components/Layout/Header/ActionCenter/components/SpeedUpModal' import { RawText } from '@/components/Text' export const TransactionCommon = ({ @@ -22,6 +26,7 @@ export const TransactionCommon = ({ toggleOpen, parentWidth, }: TransactionRowProps) => { + const translate = useTranslate() const transfersByType = useMemo( () => getTransfersByType(txDetails.transfers, [TransferType.Send, TransferType.Receive]), [txDetails.transfers], @@ -31,6 +36,26 @@ export const TransactionCommon = ({ return transfersByType && transfersByType.Send && transfersByType.Send.length > 0 }, [transfersByType]) + const isSpeedUpEligible = useMemo(() => { + if (txDetails.tx.status !== TxStatus.Pending) return false + if (txDetails.tx.chainId !== btcChainId) return false + if (!hasSend) return false + return true + }, [txDetails.tx.status, txDetails.tx.chainId, hasSend]) + + const accountId = useMemo(() => { + if (!isSpeedUpEligible) return undefined + return toAccountId({ chainId: txDetails.tx.chainId, account: txDetails.tx.pubkey }) + }, [isSpeedUpEligible, txDetails.tx.chainId, txDetails.tx.pubkey]) + + const [isSpeedUpModalOpen, setIsSpeedUpModalOpen] = useState(false) + const handleSpeedUpClick = useCallback((e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() + setIsSpeedUpModalOpen(true) + }, []) + const handleCloseSpeedUpModal = useCallback(() => setIsSpeedUpModalOpen(false), []) + return ( <> {'--'} )} + {isSpeedUpEligible && ( + + )} + {isSpeedUpModalOpen && accountId && ( + + )} ) } diff --git a/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx b/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx index 635a44d364a..d35367d8196 100644 --- a/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx +++ b/src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx @@ -19,7 +19,7 @@ import { getTonTransactionStatus, isTonChainAdapter } from '@/lib/utils/ton' import { getTronTransactionStatus } from '@/lib/utils/tron' import { actionSlice } from '@/state/slices/actionSlice/actionSlice' import { selectPendingWalletSendActions } from '@/state/slices/actionSlice/selectors' -import { ActionStatus } from '@/state/slices/actionSlice/types' +import { ActionStatus, isGenericTransactionAction } from '@/state/slices/actionSlice/types' import { portfolioApi } from '@/state/slices/portfolioSlice/portfolioSlice' import { selectTxs } from '@/state/slices/selectors' import { txHistory } from '@/state/slices/txHistorySlice/txHistorySlice' @@ -44,6 +44,8 @@ export const useSendActionSubscriber = () => { const pollingIntervalsRef = useRef>(new Map()) + const allActions = useAppSelector(actionSlice.selectors.selectActionsById) + const completeAction = useCallback( (action: ReturnType[number]) => { const { txHash, accountId, accountIdsToRefetch } = action.transactionMetadata @@ -60,6 +62,32 @@ export const useSendActionSubscriber = () => { }), ) + if (action.transactionMetadata.replacesTxHash) { + const originalAction = Object.values(allActions).find( + candidate => + isGenericTransactionAction(candidate) && + candidate.transactionMetadata.txHash === action.transactionMetadata.replacesTxHash, + ) + if ( + originalAction && + originalAction.status === ActionStatus.Pending && + isGenericTransactionAction(originalAction) + ) { + dispatch( + actionSlice.actions.upsertAction({ + ...originalAction, + status: ActionStatus.Replaced, + updatedAt: Date.now(), + transactionMetadata: { + ...originalAction.transactionMetadata, + message: 'transactionHistory.replaced', + replacedByTxHash: txHash, + }, + }), + ) + } + } + const chainId = fromAccountId(accountId).chainId const isSecondClassChain = SECOND_CLASS_CHAINS.includes(chainId as KnownChainIds) @@ -102,7 +130,7 @@ export const useSendActionSubscriber = () => { }, }) }, - [dispatch, toast, isDrawerOpen, openActionCenter], + [allActions, dispatch, toast, isDrawerOpen, openActionCenter], ) const failAction = useCallback( diff --git a/src/state/slices/actionSlice/types.ts b/src/state/slices/actionSlice/types.ts index d8362ea0769..a96be384916 100644 --- a/src/state/slices/actionSlice/types.ts +++ b/src/state/slices/actionSlice/types.ts @@ -37,6 +37,7 @@ export enum ActionStatus { Pending = 'Pending', Initiated = 'Initiated', Complete = 'Complete', + Replaced = 'Replaced', Failed = 'Failed', ClaimAvailable = 'ClaimAvailable', Claimed = 'Claimed', @@ -124,6 +125,14 @@ export enum GenericTransactionQueryId { TCY = 'TCY', } +export type BtcUtxoRbfTxMetadataInput = { + addressNList: number[] +} + +export type BtcUtxoRbfTxMetadata = { + inputs: BtcUtxoRbfTxMetadataInput[] +} + type ActionGenericTransactionMetadata = { displayType: GenericTransactionDisplayType queryId?: GenericTransactionQueryId @@ -147,6 +156,10 @@ type ActionGenericTransactionMetadata = { confirmedQuote?: LpConfirmedWithdrawalQuote | LpConfirmedDepositQuote assetAmountsAndSymbols?: string poolName?: string + replacedByTxHash?: string + replacesTxHash?: string + isRbfEnabled?: boolean + btcUtxoRbfTxMetadata?: BtcUtxoRbfTxMetadata } export type BaseAction = { From 702ba59bcec13831adf9e37dcdf47a5c922c9e59 Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 5 Mar 2026 19:27:16 -0700 Subject: [PATCH 08/15] fix: railway build with pnpm (#12107) --- .npmrc | 4 -- .railwayignore | 48 +++++++-------------- packages/public-api/Dockerfile | 26 +++++------ packages/public-api/Dockerfile.dockerignore | 31 ------------- packages/swap-widget/Dockerfile | 23 +++++----- pnpm-workspace.yaml | 4 ++ 6 files changed, 44 insertions(+), 92 deletions(-) delete mode 100644 .npmrc delete mode 100644 packages/public-api/Dockerfile.dockerignore diff --git a/.npmrc b/.npmrc deleted file mode 100644 index c0aaed42049..00000000000 --- a/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -node-linker=hoisted -shamefully-hoist=true -strict-peer-dependencies=true -auto-install-peers=true diff --git a/.railwayignore b/.railwayignore index bb091a643f1..ba939cdb378 100644 --- a/.railwayignore +++ b/.railwayignore @@ -1,49 +1,31 @@ -# Railway snapshot ignore - reduce repo snapshot size for public-api builds +# Railway snapshot ignore - reduce repo snapshot size for Railway builds # The Dockerfile.dockerignore handles Docker build context separately; # this file reduces what Railway snapshots from GitHub before the build starts. -# Frontend source (not needed for public-api server build) +# Frontend source (not needed for server builds) src/ -cypress/ e2e/ -# Large generated/static assets already copied explicitly in Dockerfile -public/generated/generatedAssetData.json.gz -public/generated/generatedAssetData.json.br -public/generated/relatedAssetIndex.json +# Static assets +public/ +!public/generated/generatedAssetData.json -# Images and static assets -src/assets/ -*.png -*.jpg -*.jpeg -*.svg -*.gif -*.ico -*.webp - -# Development/IDE files -.vscode/ -.idea/ +# Development tooling +.github/ +.cursor/ .claude/ .agents/ -.beads/ -.playwright-mcp/ -coverage/ -*.log -.DS_Store - -# Yarn state (not needed for pnpm builds) -.yarn/ +prds/ +scripts/ +headers/ +__mocks__/ # Documentation +docs/ *.md !packages/public-api/*.md !packages/swap-widget/*.md -docs/ # Test files -**/*.test.ts -**/*.test.tsx -**/*.spec.ts -**/*.spec.tsx +**/*.test.* +**/*.spec.* diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index b0050a9f496..3230f470514 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -9,7 +9,8 @@ WORKDIR /app RUN apk add --no-cache python3 make g++ openjdk17-jre # Copy workspace files -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY patches/ ./patches/ # Copy all workspace package.json files for resolution (pnpm requires all workspaces present) COPY packages/caip/package.json ./packages/caip/ @@ -47,28 +48,27 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ -# Copy patches directory (required by pnpm for patched dependencies in lockfile) -COPY patches/ ./patches/ - -# Use copy instead of hard-link to avoid ENOENT rename failures on Docker's overlay filesystem +# Copy import method avoids hard-link failures from the pnpm store on Docker overlay FS ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy -# Install dependencies with Railway cache mount (skip build/postinstall scripts that require git/cypress/etc) -# Retry loop: pnpm's hoisting/linking phase hits intermittent ENOENT rename failures on -# Docker overlay filesystems. Packages are cached in the store after the first attempt, -# so retries are fast. Clean node_modules between attempts to avoid partial state. -RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && \ +# Install dependencies, skipping lifecycle scripts (controlled by onlyBuiltDependencies in package.json) +# Retry loop: pnpm's hoisted linker intermittently fails with ENOENT on Docker overlay FS (known issue). +RUN --mount=type=cache,id=s/ff1bcf56-d6d3-403a-b41e-2c92b7233f01-/root/.local/share/pnpm/store,target=/root/.local/share/pnpm/store \ + corepack enable && \ for i in 1 2 3; do \ pnpm install --frozen-lockfile --ignore-scripts && break || \ - { echo "pnpm install attempt $i failed, retrying..."; rm -rf node_modules; }; \ + rm -rf node_modules; \ done +# Run postinstall for openapi-generator-cli to download the JAR (skipped by --ignore-scripts above) +RUN --mount=type=cache,id=s/ff1bcf56-d6d3-403a-b41e-2c92b7233f01-/root/.openapi-generator-cli,target=/root/.openapi-generator-cli \ + pnpm rebuild @openapitools/openapi-generator-cli + # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ COPY packages/unchained-client/generator/ ./packages/unchained-client/generator/ -# Generate unchained-client code (cached when openapitools.json unchanged) -# Cache mount for OpenAPI Generator JAR to avoid re-downloading on each build +# Generate unchained-client code (required by tsc --build for workspace packages) RUN pnpm --filter @shapeshiftoss/unchained-client generate # Copy remaining source files diff --git a/packages/public-api/Dockerfile.dockerignore b/packages/public-api/Dockerfile.dockerignore deleted file mode 100644 index 6f14c4455f9..00000000000 --- a/packages/public-api/Dockerfile.dockerignore +++ /dev/null @@ -1,31 +0,0 @@ -# Dependencies (pnpm install handles these fresh) -node_modules/ -packages/*/node_modules/ - -# Git -.git/ - -# Build artifacts -build/ -packages/*/dist/ - -# Development files -.env* -!.env.example -*.log -.DS_Store -coverage/ - -# IDE -.idea/ -.vscode/ - -# Test files not needed for build -cypress/ -**/*.test.ts -**/*.test.tsx -**/*.spec.ts - -# Other large/unnecessary directories -src/ -.playwright-mcp/ diff --git a/packages/swap-widget/Dockerfile b/packages/swap-widget/Dockerfile index a0fec07f567..f7ffd258c8f 100644 --- a/packages/swap-widget/Dockerfile +++ b/packages/swap-widget/Dockerfile @@ -9,7 +9,8 @@ WORKDIR /app RUN apk add --no-cache python3 make g++ openjdk17-jre # Copy workspace files -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY patches/ ./patches/ # Copy all workspace package.json files for resolution (pnpm requires all workspaces present) COPY packages/caip/package.json ./packages/caip/ @@ -47,22 +48,22 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ -# Copy patches directory (required by pnpm for patched dependencies in lockfile) -COPY patches/ ./patches/ - -# Use copy instead of hard-link to avoid ENOENT rename failures on Docker's overlay filesystem +# Copy import method avoids hard-link failures from the pnpm store on Docker overlay FS ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy -# Install dependencies (skip build/postinstall scripts that require git/cypress/etc) -# Retry loop: pnpm's hoisting/linking phase hits intermittent ENOENT rename failures on -# Docker overlay filesystems. Packages are cached in the store after the first attempt, -# so retries are fast. Clean node_modules between attempts to avoid partial state. -RUN corepack enable && corepack prepare pnpm@10.30.3 --activate && \ +# Install dependencies, skipping lifecycle scripts (controlled by onlyBuiltDependencies in package.json) +# Retry loop: pnpm's hoisted linker intermittently fails with ENOENT on Docker overlay FS (known issue). +RUN --mount=type=cache,id=s/4cafb297-3349-46e2-bfef-8210504e0583-/root/.local/share/pnpm/store,target=/root/.local/share/pnpm/store \ + corepack enable && \ for i in 1 2 3; do \ pnpm install --frozen-lockfile --ignore-scripts && break || \ - { echo "pnpm install attempt $i failed, retrying..."; rm -rf node_modules; }; \ + rm -rf node_modules; \ done +# Run postinstall for openapi-generator-cli to download the JAR (skipped by --ignore-scripts above) +RUN --mount=type=cache,id=s/4cafb297-3349-46e2-bfef-8210504e0583-/root/.openapi-generator-cli,target=/root/.openapi-generator-cli \ + pnpm rebuild @openapitools/openapi-generator-cli + # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ COPY packages/unchained-client/generator/ ./packages/unchained-client/generator/ diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 18ec407efca..06ccf60e070 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,6 @@ +nodeLinker: hoisted +shamefullyHoist: true +strictPeerDependencies: true +autoInstallPeers: true packages: - 'packages/*' From ff51d457ba7f5fb00fdaf74a50c93f0b7e3ff9db Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:27:48 +0100 Subject: [PATCH 09/15] feat: thorchain solana lp integration (#12037) --- .../poolAssetHelpers/poolAssetHelpers.ts | 10 +++- .../src/thorchain-utils/solana/constants.ts | 3 ++ src/components/Modals/Send/utils.ts | 47 ++++++++++++++----- src/lib/utils/thorchain/index.ts | 8 +++- .../AddLiquidity/AddLiquidityInput.tsx | 1 + src/pages/ThorChainLP/components/LpType.tsx | 16 +++++-- 6 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 packages/swapper/src/thorchain-utils/solana/constants.ts diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts index 0175c89e135..f31433872f3 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts @@ -5,10 +5,16 @@ import generatedTradableAssetMap from '../../generated/generatedTradableAssetMap const thorPoolIdAssetIdSymbolMap = generatedTradableAssetMap as Record -export const assetIdToThorPoolAssetIdMap = invert(thorPoolIdAssetIdSymbolMap) +export const assetIdToThorPoolAssetIdMap: Record = invert( + thorPoolIdAssetIdSymbolMap, +) + +const assetIdToThorPoolAssetIdMapLower: Record = Object.fromEntries( + Object.entries(assetIdToThorPoolAssetIdMap).map(([k, v]) => [k.toLowerCase(), v]), +) export const thorPoolAssetIdToAssetId = (id: string): AssetId | undefined => thorPoolIdAssetIdSymbolMap[id.toUpperCase()] export const assetIdToThorPoolAssetId = ({ assetId }: { assetId: AssetId }): string | undefined => - assetIdToThorPoolAssetIdMap[assetId] + assetIdToThorPoolAssetIdMapLower[assetId.toLowerCase()] diff --git a/packages/swapper/src/thorchain-utils/solana/constants.ts b/packages/swapper/src/thorchain-utils/solana/constants.ts new file mode 100644 index 00000000000..793406146df --- /dev/null +++ b/packages/swapper/src/thorchain-utils/solana/constants.ts @@ -0,0 +1,3 @@ +// Solana Memo Program v2 +// https://spl.solana.com/memo +export const MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index 2b2ef38e06b..3abb01d58a6 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -16,12 +16,13 @@ import type { GetFeeDataInput, } from '@shapeshiftoss/chain-adapters' import { utxoChainIds } from '@shapeshiftoss/chain-adapters' -import type { HDWallet } from '@shapeshiftoss/hdwallet-core' +import type { HDWallet, SolanaTxInstruction } from '@shapeshiftoss/hdwallet-core' import { isGridPlus, supportsETH, supportsSolana } from '@shapeshiftoss/hdwallet-core/wallet' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import { isTrezor } from '@shapeshiftoss/hdwallet-trezor' import type { CosmosSdkChainId, EvmChainId, KnownChainIds, UtxoChainId } from '@shapeshiftoss/types' import { contractAddressOrUndefined } from '@shapeshiftoss/utils' +import { PublicKey, TransactionInstruction } from '@solana/web3.js' import type { SendInput } from './Form' @@ -59,6 +60,8 @@ export type EstimateFeesInput = { contractAddress: string | undefined } +const SOLANA_MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' + export const estimateFees = async ({ amountCryptoPrecision, assetId, @@ -112,8 +115,17 @@ export const estimateFees = async ({ case CHAIN_NAMESPACE.Solana: { const adapter = assertGetSolanaChainAdapter(asset.chainId) + const memoInstruction: TransactionInstruction | undefined = memo + ? new TransactionInstruction({ + keys: [], + programId: new PublicKey(SOLANA_MEMO_PROGRAM_ID), + data: Buffer.from(memo, 'utf8'), + }) + : undefined + // For SPL transfers, build complete instruction set including compute budget - // For SOL transfers (pure sends i.e not e.g a Jup swap), pass no instructions to get 0 count (avoids blind signing) + // For SOL transfers with memo (e.g. THORChain), pass memo instruction for accurate fee estimation + // For pure SOL transfers, pass no instructions to get 0 count (avoids blind signing) const instructions = contractAddress ? await adapter.buildEstimationInstructions({ from: account, @@ -121,6 +133,8 @@ export const estimateFees = async ({ tokenId: contractAddress, value, }) + : memoInstruction + ? [memoInstruction] : undefined const getFeeDataInput: GetFeeDataInput = { @@ -376,29 +390,36 @@ export const handleSendWithMetadata = async ({ const solanaAdapter = assertGetSolanaChainAdapter(chainId) const { account } = fromAccountId(sendInput.accountId) - const instructions = await solanaAdapter.buildEstimationInstructions({ + + const memoInstruction: SolanaTxInstruction | undefined = memo + ? { keys: [], programId: SOLANA_MEMO_PROGRAM_ID, data: Buffer.from(memo, 'utf8') } + : undefined + + const estimationInstructions = await solanaAdapter.buildEstimationInstructions({ from: account, to, tokenId: contractAddress, value, }) + const shouldAddComputeBudget = estimationInstructions.length > 1 || Boolean(memoInstruction) + const input: BuildSendTxInput = { to, value, wallet, accountNumber: bip44Params.accountNumber, pubKey: skipDeviceDerivation ? fromAccountId(sendInput.accountId).account : undefined, - chainSpecific: - instructions.length <= 1 - ? { - tokenId: contractAddress, - } - : { - tokenId: contractAddress, - computeUnitLimit: fees.chainSpecific.computeUnits, - computeUnitPrice: fees.chainSpecific.priorityFee, - }, + chainSpecific: shouldAddComputeBudget + ? { + tokenId: contractAddress, + computeUnitLimit: fees.chainSpecific.computeUnits, + computeUnitPrice: fees.chainSpecific.priorityFee, + instructions: memoInstruction ? [memoInstruction] : undefined, + } + : { + tokenId: contractAddress, + }, } return solanaAdapter.buildSendTransaction(input) diff --git a/src/lib/utils/thorchain/index.ts b/src/lib/utils/thorchain/index.ts index 1f770627430..c3c497e8e0a 100644 --- a/src/lib/utils/thorchain/index.ts +++ b/src/lib/utils/thorchain/index.ts @@ -5,6 +5,7 @@ import { fromAccountId, fromAssetId, rujiAssetId, + solanaChainId, tcyAssetId, thorchainChainId, tronChainId, @@ -308,7 +309,12 @@ export const getThorchainTransactionType = (chainId: ChainId) => { if (supportedEvmChainIds.includes(chainId as KnownChainIds)) { return 'EvmCustomTx' } - if (isUtxoChainId(chainId) || chainId === cosmosChainId || chainId === tronChainId) { + if ( + isUtxoChainId(chainId) || + chainId === cosmosChainId || + chainId === tronChainId || + chainId === solanaChainId + ) { return 'Send' } diff --git a/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx index 4f841700b14..40f9a6635a4 100644 --- a/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx @@ -1673,6 +1673,7 @@ export const AddLiquidityInput: React.FC = ({ {maybeOpportunityNotSupportedExplainer} {maybeAlert} = ({ assetIds }) => { ) } -const TypeRadio: React.FC = props => { - const { getInputProps, getRadioProps } = useRadio(props) +const TypeRadio: React.FC = props => { + const { 'data-testid': dataTestId, ...radioProps } = props + const { getInputProps, getRadioProps } = useRadio(radioProps) const input = getInputProps() const checkbox = getRadioProps() return ( - + + + {radioOptions} ) From a0e5c28187013855eabdae85e6576d5b74753537 Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Mon, 9 Mar 2026 11:54:25 +0100 Subject: [PATCH 10/15] fix: narrow YieldExplainers action prop type to remove unused claim variant (#12073) --- src/pages/Yields/components/YieldExplainers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Yields/components/YieldExplainers.tsx b/src/pages/Yields/components/YieldExplainers.tsx index 0777dfae5a5..c23c396670c 100644 --- a/src/pages/Yields/components/YieldExplainers.tsx +++ b/src/pages/Yields/components/YieldExplainers.tsx @@ -73,7 +73,7 @@ const getYieldExplainers = (selectedYield: AugmentedYieldDto): ExplainerItem[] = type YieldExplainersProps = { selectedYield: AugmentedYieldDto sellAssetSymbol?: string - action: 'enter' | 'exit' | 'claim' + action: 'enter' | 'exit' } export const YieldExplainers = memo( From 455bdca79fb14ec5392402fdaf43c4b00183c39e Mon Sep 17 00:00:00 2001 From: NeOMakinG <14963751+NeOMakinG@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:09:43 +0100 Subject: [PATCH 11/15] fix: always refresh account balances after swap completion (#12106) --- .../useSwapActionSubscriber.tsx | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx b/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx index dfcca3b63e3..369283f643b 100644 --- a/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx +++ b/src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx @@ -322,29 +322,27 @@ export const useSwapActionSubscriber = () => { const { getAccount } = portfolioApi.endpoints - if (isSellSecondClassChain) { + // Always refresh sell account balance after swap completion + // This ensures balances are up-to-date even if WebSocket subscriptions miss the update + dispatch( + getAccount.initiate( + { accountId: swap.sellAccountId, upsertOnFetch: true }, + { forceRefetch: true }, + ), + ) + + // Always refresh buy account balance after swap completion (if different from sell) + // This fixes cross-chain swaps where the destination chain's balance wasn't updating + // See: https://github.com/shapeshift/web/issues/12092 + if (swap.buyAccountId && swap.buyAccountId !== swap.sellAccountId) { dispatch( getAccount.initiate( - { accountId: swap.sellAccountId, upsertOnFetch: true }, + { accountId: swap.buyAccountId, upsertOnFetch: true }, { forceRefetch: true }, ), ) } - if (swap.buyAccountId && swap.buyAccountId !== swap.sellAccountId) { - const buyChainId = fromAccountId(swap.buyAccountId).chainId - const isBuySecondClassChain = SECOND_CLASS_CHAINS.includes(buyChainId as KnownChainIds) - - if (isBuySecondClassChain) { - dispatch( - getAccount.initiate( - { accountId: swap.buyAccountId, upsertOnFetch: true }, - { forceRefetch: true }, - ), - ) - } - } - if ( !hasSeenRatingModal && mobileFeaturesCompatibility[MobileFeature.RatingModal].isCompatible && From 32716ef0463ea1d516ae917e8fab402833e011f3 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:42:13 +0100 Subject: [PATCH 12/15] chore: unify claude and codex instruction entrypoints (#12119) --- .claude/guidelines/beads-rules.md | 22 +++ .claude/guidelines/global-rules.md | 59 +++++++ .claude/guidelines/pr-rules.md | 6 + .claude/guidelines/project-rules.md | 153 ++++++++++++++++++ .claude/programming-guidelines.md | 15 ++ .claude/test-scenarios/README.md | 6 + .codex/config.toml | 1 + .codex/skills | 1 + AGENTS.md | 41 ++++- CLAUDE.md | 232 +--------------------------- 10 files changed, 307 insertions(+), 229 deletions(-) create mode 100644 .claude/guidelines/beads-rules.md create mode 100644 .claude/guidelines/global-rules.md create mode 100644 .claude/guidelines/pr-rules.md create mode 100644 .claude/guidelines/project-rules.md create mode 100644 .claude/programming-guidelines.md create mode 100644 .codex/config.toml create mode 120000 .codex/skills mode change 120000 => 100644 AGENTS.md diff --git a/.claude/guidelines/beads-rules.md b/.claude/guidelines/beads-rules.md new file mode 100644 index 00000000000..27e1bd3830d --- /dev/null +++ b/.claude/guidelines/beads-rules.md @@ -0,0 +1,22 @@ +## Beads Rules + +Use `bd` as the default workflow for complex multi-step work. + +### Priority +- For complex tasks, always prefer beads tracking before implementation. +- Keep bead status current as work progresses (`in_progress`, then `closed`). +- Keep beads sync in your normal completion flow. + +### Commands + +```bash +bd ready # Find available work +bd show # View issue details +bd update --status in_progress # Claim work +bd close # Complete work +bd sync # Sync with git +``` + +### Session Completion Integration +- Include beads updates in session completion. +- Before final push, ensure bead state is consistent with delivered work. diff --git a/.claude/guidelines/global-rules.md b/.claude/guidelines/global-rules.md new file mode 100644 index 00000000000..ba05ee59d4e --- /dev/null +++ b/.claude/guidelines/global-rules.md @@ -0,0 +1,59 @@ +## Global Programming Rules + +### Code Quality & Style +- Look for opportunities to use existing code rather than creating new code +- Follow existing code conventions in each file/project +- Use existing libraries and utilities already present in the codebase +- Never assume a library is available - always check imports/package.json first +- Prefer composition over inheritance +- Write self-documenting code with clear variable and function names +- Keep functions small and focused on a single responsibility +- Avoid deep nesting - use early returns instead +- Prefer procedural and easy to understand code +- Avoid useEffect where practical - use it only when necessary and following best practices +- Choose the most straightforward approach that accomplishes the task +- Avoid "any" types - use specific type annotations instead +- For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern: `userSelected ?? smartDefault ?? fallback` +- When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore + +### Security & Best Practices +- Never expose, log, or commit secrets, API keys, or credentials +- Validate all inputs, especially user inputs +- Use parameterized queries to prevent SQL injection +- Sanitize data before displaying to prevent XSS +- Follow principle of least privilege +- Use HTTPS and secure communication protocols + +### Error Handling +- Handle errors gracefully with meaningful messages +- Don't silently catch and ignore exceptions +- Log errors appropriately for debugging +- Provide fallback behavior when possible +- Use proper HTTP status codes in APIs + +### Performance +- Avoid premature optimization, but be mindful of performance +- Use appropriate data structures for the task +- Minimize database queries and API calls +- Implement proper caching strategies +- Optimize images and assets for web delivery + +### Testing +- Write tests for critical business logic +- Test edge cases and error conditions +- Use descriptive test names that explain behavior +- Keep tests isolated and independent +- Mock external dependencies appropriately + +### Documentation & Communication +- Never add code comments unless explicitly requested +- When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality. +- Write clear commit messages explaining the "why" +- Use meaningful names for branches, variables, and functions +- Keep README files updated with setup and usage instructions + +### Rule Management +- When user says "add that to the project rules": take previous guidance, form a rule, and add it to `.claude/guidelines/project-rules.md` +- When user says "add that to the global rules": take previous guidance, form a rule, and add it to `.claude/guidelines/global-rules.md` + +--- diff --git a/.claude/guidelines/pr-rules.md b/.claude/guidelines/pr-rules.md new file mode 100644 index 00000000000..1c9dfda8e36 --- /dev/null +++ b/.claude/guidelines/pr-rules.md @@ -0,0 +1,6 @@ +## PR-Specific Rules + +### xstate PRs +- When a PR includes xstate state machines, the PR description MUST include a Mermaid `stateDiagram-v2` visualization of the machine's states and transitions +- Generate the diagram from the machine definition - show states, events, guards, and error/retry flows +- This serves as living documentation for both product and engineering reviewers diff --git a/.claude/guidelines/project-rules.md b/.claude/guidelines/project-rules.md new file mode 100644 index 00000000000..a892be42de6 --- /dev/null +++ b/.claude/guidelines/project-rules.md @@ -0,0 +1,153 @@ +## Project-Specific Rules: ShapeShift + +### Project Overview +- **Project**: Decentralized crypto exchange platform +- **Main branch**: `develop` (not main/master) +- **Package manager**: pnpm +- **State management**: Redux Toolkit with redux-persist +- **Architecture**: Plugin-based for blockchain support + +### Code Quality & Standards +- Always run `pnpm run lint --fix` and `pnpm run type-check` after making changes +- Keep changes surgical where possible - minimize changes to make code reviews easier +- Make targeted, focused modifications rather than broad refactors unless specifically requested +- Never create documentation files unless explicitly requested +- Prefer editing existing files over creating new ones +- Memoize aggressively - wrap component variables in `useMemo` and callbacks in `useCallback` where possible +- Avoid `let` variable assignments - prefer `const` with inline IIFE switch statements or extract to functions for conditional logic +- For static JSX icon elements (e.g., ``) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo + +### CLI Tool Preferences +- Prefer `rg` (ripgrep) over `grep` for searching - it's faster and respects .gitignore +- Use `jq` for querying and manipulating JSON files instead of reading entire files into context + - Example: `jq '.yieldXYZ | keys' src/assets/translations/en/main.json` to list keys + - Example: `jq '.someKey.nested' file.json` to extract specific values +- This is especially important for large JSON files (like generated data or translation files) to avoid context bloat and improve performance + +### Git & Version Control +- Never commit changes unless explicitly requested +- When creating commits, follow the Git Safety Protocol (see session notes) +- **Before pushing**: always run `pnpm run lint --fix`, and if there are lint fixes, commit them before pushing. Never push without verifying lint passes first. +- Main branch is `develop` - use this for PRs +- Branch naming: Use descriptive names (e.g., `feat_gridplus`, `fix_wallet_connect`) +- Prefer pushing new feature/fix branches to `origin` when permissions allow. +- If a PR branch already exists on `fork`, continue pushing to that existing `fork` branch and do not recreate it on `origin`. +- When opening PRs (via `gh`, Aviator `av`, or any CLI tool), ALWAYS use the `.github/PULL_REQUEST_TEMPLATE.md` template as the base for the PR body +- **Editing PR descriptions**: `gh pr edit --body` fails on this repo due to a deprecated Projects Classic GraphQL error. Use the REST API instead: `gh api repos/shapeshift/web/pulls/ -X PATCH -F "body=@/path/to/body.md"` (write the body to a temp file first) + +### UI/UX Standards +- Account for light/dark mode using `useColorModeValue` hook +- Account for responsive mobile designs in all UI components +- When applying styles, use the existing standards and conventions of the codebase +- Use Chakra UI components and conventions + +### Internationalization (i18n) +- All copy/text must use translation keys - never hardcode strings +- Add English copy to `src/assets/translations/en/main.json` (find appropriate section) +- Ignore other language translation files - only update English +- Use the translation hook: `useTranslate()` from `react-polyglot` +- **Both steps required**: Translations must be (1) added to `en/main.json` AND (2) consumed via `translate('key')` - missing either step results in untranslated strings showing raw keys + +### Feature Flags +- Feature flags are stored in Redux state under `preferences.featureFlags` +- Feature flags are NOT persisted between sessions (blacklisted in redux-persist) +- Default values always come from environment variables prefixed with `VITE_FEATURE_` + +**To add a new feature flag:** +1. Add to `FeatureFlags` type in `src/state/slices/preferencesSlice/preferencesSlice.ts` +2. Add environment variable validation in `src/config.ts` (e.g., `VITE_FEATURE_MY_FLAG: bool({ default: false })`) +3. Add to initial state in `preferencesSlice.ts` (e.g., `MyFlag: getConfig().VITE_FEATURE_MY_FLAG`) +4. Add to test mock in `src/test/mocks/store.ts` +5. Set appropriate values in `.env`, `.env.development`, and `.env.production` + +**Usage:** +- Use `useFeatureFlag('FlagName')` hook to access feature flag values in components +- The `/flags` route provides a hidden UI for toggling feature flags at runtime (click settings modal header 5 times) +- Use `.env.development` for dev-only features and `.env.production` for prod settings + +### Redux State Management +- Uses Redux Toolkit with createSlice +- State is persisted with redux-persist (localforage) +- Migrations are required when changing persisted state structure (see `src/state/migrations/`) +- When adding new slices: + 1. Create slice in `src/state/slices//` + 2. Add to `src/state/reducer.ts` (both `slices` and `sliceReducers`) + 3. Add to `src/state/store.ts` `clearState()` function + 4. Add persist config if needed + 5. Export selectors from slice using `selectors` property + +### Contracts (Enforceable Integration Specs) + +Contracts live in `.claude/contracts/` (project) and `~/.claude/contracts/` (global, fallback). +They define the authoritative checklist of integration points for a feature type. + +**When building:** If a contract exists for the feature type being implemented, load it +and use it as your todo list. Every item must be addressed. + +**When reviewing:** If a contract exists for the feature type being reviewed, load it +and perform gap analysis against the PR diff. Flag missing items with severity prefixes. + +**Discovery:** Check `.claude/contracts/` first, then `~/.claude/contracts/`. + +Current contracts: +- `second-class-evm-chain.md` - All integration points for adding a new second-class EVM chain +- `swapper-integration.md` - Registration, testing, and completion checklist for new swappers + +### Type Definitions +- Prefer `type` over `interface` for type definitions +- Use strict typing - avoid `any` +- Use `Nominal` types for domain identifiers (e.g., `WalletId`, `AccountId`) +- Import types from `@shapeshiftoss/caip` for chain/account/asset IDs + +### Common Patterns + +**Selectors:** +- Use `createDeepEqualOutputSelector` from `@/state/selector-utils` for deep equality checks +- Use `createCachedSelector` from `re-reselect` for parameterized selectors +- Export selectors from slice using inline `selectors` property + +**Components:** +- Use `useAppSelector` for Redux state +- Use `useAppDispatch` for Redux actions +- Memoize expensive computations with `useMemo` +- Memoize callbacks with `useCallback` + +**BigAmount (Precision-Aware Numeric Type):** +- See `docs/bigamount.md` for full API documentation +- Use `BigAmount.fromBaseUnit({ value, precision })` for constructing from raw blockchain values (preferred, lossless) +- Use `BigAmount.fromPrecision({ value, precision })` only when a precision-scale value is all that's available +- Call `.toPrecision()` / `.toBaseUnit()` directly on BigAmount for string extraction — no wrapper aliases +- Never cast `as BigAmount` — fix types as needed +- Naming: `CryptoBaseUnit` = raw integer, `CryptoPrecision` = human-readable (NOT "HumanBalance") + +**Wallet Integration:** +- Wallets are managed via `WalletProvider` context +- Each wallet has unique `walletId` (e.g., `metamask:0x123`, `ledger:ABC`) +- Portfolio state is filtered by active `walletId` +- Account discovery runs per wallet on connection + +### Session Completion + +**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. + +**MANDATORY WORKFLOW:** + +1. **File issues for remaining work** - Create issues for anything that needs follow-up +2. **Run quality gates** (if code changed) - Tests, linters, builds +3. **Update issue status** - Close finished work, update in-progress items +4. **PUSH TO REMOTE** - This is MANDATORY: + ```bash + git pull --rebase + bd sync + git push + git status # MUST show "up to date with origin" + ``` +5. **Clean up** - Clear stashes, prune remote branches +6. **Verify** - All changes committed AND pushed +7. **Hand off** - Provide context for next session + +**CRITICAL RULES:** +- Work is NOT complete until `git push` succeeds +- NEVER stop before pushing - that leaves work stranded locally +- NEVER say "ready to push when you are" - YOU must push +- If push fails, resolve and retry until it succeeds diff --git a/.claude/programming-guidelines.md b/.claude/programming-guidelines.md new file mode 100644 index 00000000000..de4d45e65d0 --- /dev/null +++ b/.claude/programming-guidelines.md @@ -0,0 +1,15 @@ +# Programming And Project Rules (TOC) + +This file is the TOC entrypoint for coding, project, and PR-specific rules. + +## Read Order + +1. [.claude/guidelines/global-rules.md](.claude/guidelines/global-rules.md) +2. [.claude/guidelines/beads-rules.md](.claude/guidelines/beads-rules.md) +3. [.claude/guidelines/project-rules.md](.claude/guidelines/project-rules.md) +4. [.claude/guidelines/pr-rules.md](.claude/guidelines/pr-rules.md) + +@.claude/guidelines/global-rules.md +@.claude/guidelines/beads-rules.md +@.claude/guidelines/project-rules.md +@.claude/guidelines/pr-rules.md diff --git a/.claude/test-scenarios/README.md b/.claude/test-scenarios/README.md index 7ab539ef1c1..ce482f85c2f 100644 --- a/.claude/test-scenarios/README.md +++ b/.claude/test-scenarios/README.md @@ -2,6 +2,12 @@ This directory contains automated test scenarios for the ShapeShift Web application. These scenarios are used by the `/test-agent` slash command to validate features and ensure quality. +## Scope Clarification + +- This directory is for `@neomaking`'s `/test-agent` workflow and related scenario docs. +- This is not `qabot` testing. +- `qabot` has its own fixture-based flow and instructions at `.claude/skills/qabot/SKILL.md`. + ## Directory Structure ``` diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 00000000000..125e9a4525e --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1 @@ +model_instructions_file = "AGENTS.md" diff --git a/.codex/skills b/.codex/skills new file mode 120000 index 00000000000..454b8427cd7 --- /dev/null +++ b/.codex/skills @@ -0,0 +1 @@ +../.claude/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 120000 index 681311eb9cf..00000000000 --- a/AGENTS.md +++ /dev/null @@ -1 +0,0 @@ -CLAUDE.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..2141357a451 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,40 @@ +# Agent Instructions (Source of Truth) + +This file is the canonical instruction entrypoint for local agent tooling in this repo. + +## Read Order + +1. This file (high-signal defaults and routing) +2. [.claude/guidelines/beads-rules.md](.claude/guidelines/beads-rules.md) +3. [.claude/programming-guidelines.md](.claude/programming-guidelines.md) +4. [.claude/contracts](.claude/contracts) +5. [.claude/skills](.claude/skills) +6. [.claude/test-scenarios/README.md](.claude/test-scenarios/README.md) +7. [.github/PULL_REQUEST_TEMPLATE.md](.github/PULL_REQUEST_TEMPLATE.md) + +## Must-Follow Defaults + +- Use `develop` as the base branch. +- Use `pnpm`. +- Keep changes surgical and focused. +- For complex multi-step tasks, prefer `bd` tracking before implementation. +- Prefer `origin` for new feature/fix branch pushes when permissions allow. +- If a PR branch is already on `fork`, keep using that existing `fork` branch. +- Run `pnpm run lint --fix` and `pnpm run type-check` after code changes. +- Use `.github/PULL_REQUEST_TEMPLATE.md` for PR bodies. + +## Routing + +- New second-class EVM chain work: [.claude/contracts/second-class-evm-chain.md](.claude/contracts/second-class-evm-chain.md) +- New swapper integration work: [.claude/contracts/swapper-integration.md](.claude/contracts/swapper-integration.md) +- Test scenarios (`/test-agent`, @neomaking flow): [.claude/test-scenarios/README.md](.claude/test-scenarios/README.md) +- QA automation (`qabot`, fixture-based flow): [.claude/skills/qabot/SKILL.md](.claude/skills/qabot/SKILL.md) +- Translation workflow: [.claude/skills/translate/SKILL.md](.claude/skills/translate/SKILL.md) + +## Tooling Notes + +- Claude compatibility is provided by `CLAUDE.md` importing this file. +- Codex compatibility is pinned via `.codex/config.toml`. +- Full legacy `CLAUDE.md` policy content was moved to `.claude/programming-guidelines.md` to avoid loss while keeping top-level entrypoints compact. +- Repo-local skills live in `.claude/skills`. +- Additional user-level skills/config may exist in `~/.agents`, `~/.codex/skills`, and `~/.openclaw`. diff --git a/CLAUDE.md b/CLAUDE.md index 9d96b91f5b4..fbe3523cb31 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,230 +1,6 @@ -# Claude Programming Guidelines +@AGENTS.md +@.claude/programming-guidelines.md -## Global Programming Rules +# Claude Compatibility Shim -### Code Quality & Style -- Look for opportunities to use existing code rather than creating new code -- Follow existing code conventions in each file/project -- Use existing libraries and utilities already present in the codebase -- Never assume a library is available - always check imports/package.json first -- Prefer composition over inheritance -- Write self-documenting code with clear variable and function names -- Keep functions small and focused on a single responsibility -- Avoid deep nesting - use early returns instead -- Prefer procedural and easy to understand code -- Avoid useEffect where practical - use it only when necessary and following best practices -- Choose the most straightforward approach that accomplishes the task -- Avoid "any" types - use specific type annotations instead -- For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern: `userSelected ?? smartDefault ?? fallback` -- When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore - -### Security & Best Practices -- Never expose, log, or commit secrets, API keys, or credentials -- Validate all inputs, especially user inputs -- Use parameterized queries to prevent SQL injection -- Sanitize data before displaying to prevent XSS -- Follow principle of least privilege -- Use HTTPS and secure communication protocols - -### Error Handling -- Handle errors gracefully with meaningful messages -- Don't silently catch and ignore exceptions -- Log errors appropriately for debugging -- Provide fallback behavior when possible -- Use proper HTTP status codes in APIs - -### Performance -- Avoid premature optimization, but be mindful of performance -- Use appropriate data structures for the task -- Minimize database queries and API calls -- Implement proper caching strategies -- Optimize images and assets for web delivery - -### Testing -- Write tests for critical business logic -- Test edge cases and error conditions -- Use descriptive test names that explain behavior -- Keep tests isolated and independent -- Mock external dependencies appropriately - -### Documentation & Communication -- Never add code comments unless explicitly requested -- When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality. -- Write clear commit messages explaining the "why" -- Use meaningful names for branches, variables, and functions -- Keep README files updated with setup and usage instructions - -### Rule Management -- When user says "add that to the project rules": take previous guidance, form a rule, add to project-specific section in CLAUDE.md -- When user says "add that to the global rules": take previous guidance, form a rule, add to global rules section in CLAUDE.md - ---- - -## Project-Specific Rules: ShapeShift - -### Project Overview -- **Project**: Decentralized crypto exchange platform -- **Main branch**: `develop` (not main/master) -- **Package manager**: pnpm -- **State management**: Redux Toolkit with redux-persist -- **Architecture**: Plugin-based for blockchain support - -### Code Quality & Standards -- Always run `pnpm run lint --fix` and `pnpm run type-check` after making changes -- Keep changes surgical where possible - minimize changes to make code reviews easier -- Make targeted, focused modifications rather than broad refactors unless specifically requested -- Never create documentation files unless explicitly requested -- Prefer editing existing files over creating new ones -- Memoize aggressively - wrap component variables in `useMemo` and callbacks in `useCallback` where possible -- Avoid `let` variable assignments - prefer `const` with inline IIFE switch statements or extract to functions for conditional logic -- For static JSX icon elements (e.g., ``) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo - -### CLI Tool Preferences -- Prefer `rg` (ripgrep) over `grep` for searching - it's faster and respects .gitignore -- Use `jq` for querying and manipulating JSON files instead of reading entire files into context - - Example: `jq '.yieldXYZ | keys' src/assets/translations/en/main.json` to list keys - - Example: `jq '.someKey.nested' file.json` to extract specific values -- This is especially important for large JSON files (like generated data or translation files) to avoid context bloat and improve performance - -### Git & Version Control -- Never commit changes unless explicitly requested -- When creating commits, follow the Git Safety Protocol (see session notes) -- **Before pushing**: always run `pnpm run lint --fix`, and if there are lint fixes, commit them before pushing. Never push without verifying lint passes first. -- Main branch is `develop` - use this for PRs -- Branch naming: Use descriptive names (e.g., `feat_gridplus`, `fix_wallet_connect`) -- When opening PRs (via `gh`, Aviator `av`, or any CLI tool), ALWAYS use the `.github/PULL_REQUEST_TEMPLATE.md` template as the base for the PR body -- **Editing PR descriptions**: `gh pr edit --body` fails on this repo due to a deprecated Projects Classic GraphQL error. Use the REST API instead: `gh api repos/shapeshift/web/pulls/ -X PATCH -F "body=@/path/to/body.md"` (write the body to a temp file first) - -### xstate PRs -- When a PR includes xstate state machines, the PR description MUST include a Mermaid `stateDiagram-v2` visualization of the machine's states and transitions -- Generate the diagram from the machine definition - show states, events, guards, and error/retry flows -- This serves as living documentation for both product and engineering reviewers - -### UI/UX Standards -- Account for light/dark mode using `useColorModeValue` hook -- Account for responsive mobile designs in all UI components -- When applying styles, use the existing standards and conventions of the codebase -- Use Chakra UI components and conventions - -### Internationalization (i18n) -- All copy/text must use translation keys - never hardcode strings -- Add English copy to `src/assets/translations/en/main.json` (find appropriate section) -- Ignore other language translation files - only update English -- Use the translation hook: `useTranslate()` from `react-polyglot` -- **Both steps required**: Translations must be (1) added to `en/main.json` AND (2) consumed via `translate('key')` - missing either step results in untranslated strings showing raw keys - -### Feature Flags -- Feature flags are stored in Redux state under `preferences.featureFlags` -- Feature flags are NOT persisted between sessions (blacklisted in redux-persist) -- Default values always come from environment variables prefixed with `VITE_FEATURE_` - -**To add a new feature flag:** -1. Add to `FeatureFlags` type in `src/state/slices/preferencesSlice/preferencesSlice.ts` -2. Add environment variable validation in `src/config.ts` (e.g., `VITE_FEATURE_MY_FLAG: bool({ default: false })`) -3. Add to initial state in `preferencesSlice.ts` (e.g., `MyFlag: getConfig().VITE_FEATURE_MY_FLAG`) -4. Add to test mock in `src/test/mocks/store.ts` -5. Set appropriate values in `.env`, `.env.development`, and `.env.production` - -**Usage:** -- Use `useFeatureFlag('FlagName')` hook to access feature flag values in components -- The `/flags` route provides a hidden UI for toggling feature flags at runtime (click settings modal header 5 times) -- Use `.env.development` for dev-only features and `.env.production` for prod settings - -### Redux State Management -- Uses Redux Toolkit with createSlice -- State is persisted with redux-persist (localforage) -- Migrations are required when changing persisted state structure (see `src/state/migrations/`) -- When adding new slices: - 1. Create slice in `src/state/slices//` - 2. Add to `src/state/reducer.ts` (both `slices` and `sliceReducers`) - 3. Add to `src/state/store.ts` `clearState()` function - 4. Add persist config if needed - 5. Export selectors from slice using `selectors` property - -### Contracts (Enforceable Integration Specs) - -Contracts live in `.claude/contracts/` (project) and `~/.claude/contracts/` (global, fallback). -They define the authoritative checklist of integration points for a feature type. - -**When building:** If a contract exists for the feature type being implemented, load it -and use it as your todo list. Every item must be addressed. - -**When reviewing:** If a contract exists for the feature type being reviewed, load it -and perform gap analysis against the PR diff. Flag missing items with severity prefixes. - -**Discovery:** Check `.claude/contracts/` first, then `~/.claude/contracts/`. - -Current contracts: -- `second-class-evm-chain.md` - All integration points for adding a new second-class EVM chain -- `swapper-integration.md` - Registration, testing, and completion checklist for new swappers - -### Type Definitions -- Prefer `type` over `interface` for type definitions -- Use strict typing - avoid `any` -- Use `Nominal` types for domain identifiers (e.g., `WalletId`, `AccountId`) -- Import types from `@shapeshiftoss/caip` for chain/account/asset IDs - -### Common Patterns - -**Selectors:** -- Use `createDeepEqualOutputSelector` from `@/state/selector-utils` for deep equality checks -- Use `createCachedSelector` from `re-reselect` for parameterized selectors -- Export selectors from slice using inline `selectors` property - -**Components:** -- Use `useAppSelector` for Redux state -- Use `useAppDispatch` for Redux actions -- Memoize expensive computations with `useMemo` -- Memoize callbacks with `useCallback` - -**BigAmount (Precision-Aware Numeric Type):** -- See `docs/bigamount.md` for full API documentation -- Use `BigAmount.fromBaseUnit({ value, precision })` for constructing from raw blockchain values (preferred, lossless) -- Use `BigAmount.fromPrecision({ value, precision })` only when a precision-scale value is all that's available -- Call `.toPrecision()` / `.toBaseUnit()` directly on BigAmount for string extraction — no wrapper aliases -- Never cast `as BigAmount` — fix types as needed -- Naming: `CryptoBaseUnit` = raw integer, `CryptoPrecision` = human-readable (NOT "HumanBalance") - -**Wallet Integration:** -- Wallets are managed via `WalletProvider` context -- Each wallet has unique `walletId` (e.g., `metamask:0x123`, `ledger:ABC`) -- Portfolio state is filtered by active `walletId` -- Account discovery runs per wallet on connection - -### Issue Tracking (beads) - -This project uses **bd** (beads) for issue tracking. Run `bd onboard` to get started. - -```bash -bd ready # Find available work -bd show # View issue details -bd update --status in_progress # Claim work -bd close # Complete work -bd sync # Sync with git -``` - -### Session Completion - -**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. - -**MANDATORY WORKFLOW:** - -1. **File issues for remaining work** - Create issues for anything that needs follow-up -2. **Run quality gates** (if code changed) - Tests, linters, builds -3. **Update issue status** - Close finished work, update in-progress items -4. **PUSH TO REMOTE** - This is MANDATORY: - ```bash - git pull --rebase - bd sync - git push - git status # MUST show "up to date with origin" - ``` -5. **Clean up** - Clear stashes, prune remote branches -6. **Verify** - All changes committed AND pushed -7. **Hand off** - Provide context for next session - -**CRITICAL RULES:** -- Work is NOT complete until `git push` succeeds -- NEVER stop before pushing - that leaves work stranded locally -- NEVER say "ready to push when you are" - YOU must push -- If push fails, resolve and retry until it succeeds +This file intentionally delegates project instructions to `AGENTS.md`. From 53470308648ac123ed7eabc62f90cc63e3511863 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:57:10 +0100 Subject: [PATCH 13/15] feat: idempotent release script state machine (#12110) --- RELEASE.md | 59 +++ scripts/release.test.ts | 225 +++++++++ scripts/release.ts | 989 +++++++++++++++++++++++----------------- 3 files changed, 867 insertions(+), 406 deletions(-) create mode 100644 RELEASE.md create mode 100644 scripts/release.test.ts diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000000..1945699179f --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,59 @@ +# Release process + +Single command, idempotent. Run `pnpm release` at any point - it figures out where you are and does the next step. + +## Regular release + +1. Run `pnpm release`, select Regular, confirm -> creates **prerelease PR** (develop -> release) +2. **Merge prerelease PR on GitHub** +3. Run `pnpm release` again -> generates AI release notes, creates **release PR** (release -> main) +4. **Merge release PR on GitHub** when CI passes +5. Run `pnpm release` again -> tags main with version, creates **private sync PR** (main -> private) +6. **Merge private sync PR on GitHub** +7. Run `pnpm release` again -> "done, nothing to do" + +## Hotfix release + +1. Run `pnpm release`, select Hotfix, pick commits -> creates **hotfix PR** (hotfix/vX.Y.Z -> main) +2. **Merge hotfix PR on GitHub** +3. Run `pnpm release` again -> tags, creates **private sync PR** + **backmerge PR** (main -> develop) +4. **Merge both PRs on GitHub** + +## How it works + +The script derives its state from observable git/GitHub state (branch SHAs, tags, open PRs) rather than tracking state in a file. This makes it idempotent - you can run it as many times as you want without creating duplicates or re-tagging. + +### Regular release states + +``` +idle (no prerelease) -> create develop -> release PR +prerelease_pr_open -> waiting for merge on GitHub +idle (prerelease merged) -> create release -> main PR with AI notes +release_pr_open -> waiting for merge on GitHub +merged_untagged -> tag main, create main -> private PR +tagged_private_stale -> waiting for private sync merge on GitHub +done -> nothing to do +``` + +### Hotfix states + +``` +idle -> cherry-pick commits, create hotfix -> main PR +hotfix_pr_open -> waiting for merge on GitHub +merged_untagged -> tag main, create private sync + backmerge PRs +tagged_private_stale -> waiting for PR merges on GitHub +done -> nothing to do +``` + +## Branch protection + +All four branches are protected - no direct pushes: + +- **main**: production +- **develop**: development +- **release**: staging between develop and main +- **private**: tracks main with different env vars (Cloudflare deployment) + +## AI release notes + +The release PR body is AI-generated using Claude CLI. It groups commits by feature domain, separates production changes from dev-only (feature-flagged) changes, and includes testing notes. Falls back to a raw commit list if Claude is unavailable. diff --git a/scripts/release.test.ts b/scripts/release.test.ts new file mode 100644 index 00000000000..7de7519a843 --- /dev/null +++ b/scripts/release.test.ts @@ -0,0 +1,225 @@ +import { describe, expect, it } from 'vitest' + +import { + buildReleasePrompt, + deriveHotfixState, + deriveReleaseState, + extractDescription, + extractPrNumbers, +} from './release' + +describe('deriveReleaseState', () => { + const base = { + releaseSha: 'aaa', + mainSha: 'aaa', + developSha: 'bbb', + privateSha: 'aaa', + latestTagSha: 'aaa', + openPrereleasePr: undefined, + openReleasePr: undefined, + } + + it('returns idle when develop is ahead of main and no PR exists', () => { + expect(deriveReleaseState(base)).toBe('idle') + }) + + it('returns idle when prerelease was merged (release matches develop, ahead of main)', () => { + expect( + deriveReleaseState({ + ...base, + releaseSha: 'bbb', + }), + ).toBe('idle') + }) + + it('returns prerelease_pr_open when develop -> release PR exists', () => { + expect( + deriveReleaseState({ + ...base, + openPrereleasePr: { number: 41, title: 'chore: prerelease v1.1016.0' }, + }), + ).toBe('prerelease_pr_open') + }) + + it('returns release_pr_open when a release -> main PR exists', () => { + expect( + deriveReleaseState({ + ...base, + releaseSha: 'bbb', + openReleasePr: { number: 42, title: 'chore: release v1.1016.0' }, + }), + ).toBe('release_pr_open') + }) + + it('returns merged_untagged when main is ahead of latest tag', () => { + expect( + deriveReleaseState({ + ...base, + mainSha: 'ccc', + releaseSha: 'ccc', + }), + ).toBe('merged_untagged') + }) + + it('returns tagged_private_stale when tagged but private is behind', () => { + expect( + deriveReleaseState({ + ...base, + privateSha: 'zzz', + }), + ).toBe('tagged_private_stale') + }) + + it('returns done when everything is in sync', () => { + expect( + deriveReleaseState({ + ...base, + developSha: 'aaa', + }), + ).toBe('done') + }) + + it('returns tagged_private_stale whenever private is behind main regardless of develop', () => { + expect( + deriveReleaseState({ + ...base, + privateSha: 'zzz', + }), + ).toBe('tagged_private_stale') + }) + + it('prioritizes prerelease_pr_open over everything', () => { + expect( + deriveReleaseState({ + ...base, + openPrereleasePr: { number: 41, title: 'chore: prerelease v1.1016.0' }, + openReleasePr: { number: 42, title: 'chore: release v1.1016.0' }, + }), + ).toBe('prerelease_pr_open') + }) + + it('prioritizes release_pr_open over merged_untagged', () => { + expect( + deriveReleaseState({ + ...base, + mainSha: 'ccc', + releaseSha: 'bbb', + openReleasePr: { number: 42, title: 'chore: release v1.1016.0' }, + }), + ).toBe('release_pr_open') + }) +}) + +describe('deriveHotfixState', () => { + const base = { + mainSha: 'aaa', + privateSha: 'aaa', + latestTagSha: 'aaa', + openHotfixPr: undefined, + } + + it('returns idle when no hotfix in progress', () => { + expect(deriveHotfixState(base)).toBe('idle') + }) + + it('returns hotfix_pr_open when hotfix PR exists', () => { + expect( + deriveHotfixState({ + ...base, + openHotfixPr: { number: 99, title: 'chore: hotfix v1.1015.1' }, + }), + ).toBe('hotfix_pr_open') + }) + + it('returns merged_untagged when main moved past tag', () => { + expect( + deriveHotfixState({ + ...base, + mainSha: 'bbb', + }), + ).toBe('merged_untagged') + }) + + it('returns tagged_private_stale when tagged but private behind', () => { + expect( + deriveHotfixState({ + ...base, + privateSha: 'zzz', + }), + ).toBe('tagged_private_stale') + }) +}) + +describe('extractPrNumbers', () => { + it('extracts PR numbers from commit messages', () => { + expect( + extractPrNumbers([ + 'feat: add chainflip lending (#12026)', + 'fix: bsc broadcast (#12053)', + 'chore: no pr number here', + ]), + ).toEqual([12026, 12053]) + }) + + it('deduplicates PR numbers', () => { + expect(extractPrNumbers(['feat: thing (#100)', 'fix: same thing (#100)'])).toEqual([100]) + }) + + it('returns empty array for no matches', () => { + expect(extractPrNumbers(['no numbers', 'also none'])).toEqual([]) + }) +}) + +describe('extractDescription', () => { + it('extracts description section from PR body', () => { + const body = '## Description\nThis is a fix for the thing.\n\n## Testing\nTest it.' + expect(extractDescription(body)).toBe('This is a fix for the thing.') + }) + + it('returns undefined for missing description', () => { + expect(extractDescription('## Testing\nJust tests.')).toBeUndefined() + }) + + it('returns undefined for very short descriptions', () => { + expect(extractDescription('## Description\nShort.\n## Testing')).toBeUndefined() + }) + + it('strips HTML comments', () => { + const body = + '## Description\nThis is visible and long enough to pass.\n## Testing' + expect(extractDescription(body)).toBe('This is visible and long enough to pass.') + }) + + it('truncates descriptions over 500 chars', () => { + const long = 'A'.repeat(600) + const body = `## Description\n${long}\n## Testing` + const result = extractDescription(body) + expect(result).toHaveLength(503) + expect(result?.endsWith('...')).toBe(true) + }) +}) + +describe('buildReleasePrompt', () => { + it('includes commit messages and PR context', () => { + const prBodies = new Map([[123, '## Description\nSome context here for the PR.\n## Testing']]) + const prompt = buildReleasePrompt( + 'v1.1016.0', + ['feat: something (#123)', 'fix: other thing (#456)'], + prBodies, + ) + + expect(prompt).toContain('v1.1016.0') + expect(prompt).toContain('feat: something (#123)') + expect(prompt).toContain('fix: other thing (#456)') + expect(prompt).toContain('Context: Some context here for the PR.') + expect(prompt).toContain('.env') + expect(prompt).toContain('.env.production') + expect(prompt).toContain('.env.development') + }) + + it('instructs Claude to read env files for flag detection', () => { + const prompt = buildReleasePrompt('v1.0.0', ['feat: thing (#1)'], new Map()) + expect(prompt).toContain('VITE_FEATURE_') + expect(prompt).toContain('OFF in production') + }) +}) diff --git a/scripts/release.ts b/scripts/release.ts index c8a3caa3d2a..464266221fb 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -12,57 +12,42 @@ import { simpleGit as git } from 'simple-git' import { exit, getLatestSemverTag } from './utils' -const fetch = async () => { - console.log(chalk.green('Fetching...')) - await git().fetch(['origin', '--tags', '--force']) +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +export type ReleaseState = + | 'idle' + | 'prerelease_pr_open' + | 'release_pr_open' + | 'merged_untagged' + | 'tagged_private_stale' + | 'done' + +export type HotfixState = + | 'idle' + | 'hotfix_pr_open' + | 'merged_untagged' + | 'tagged_private_stale' + | 'done' + +type GitHubPr = { + number: number + title: string } -export const assertIsCleanRepo = async () => { - const gitStatus = await git().status() - if (!gitStatus.isClean()) { - console.log(chalk.red('Your repository is not clean. Please commit or stash your changes.')) - exit() - } -} +type UnreleasedCommit = { hash: string; message: string } const releaseType = ['Regular', 'Hotfix'] as const type ReleaseType = (typeof releaseType)[number] -const inquireReleaseType = async (): Promise => { - const questions: inquirer.QuestionCollection<{ releaseType: ReleaseType }> = [ - { - type: 'list', - name: 'releaseType', - message: 'What type of release is this?', - choices: releaseType, - }, - ] - return (await inquirer.prompt(questions)).releaseType -} - -const inquireProceedWithCommits = async (commits: string[], action: 'create' | 'merge') => { - console.log(chalk.blue(['', ...commits, ''].join('\n'))) - const message = - action === 'create' - ? 'Do you want to create a release with these commits?' - : 'Do you want to merge and push these commits into main?' - const questions: inquirer.QuestionCollection<{ shouldProceed: boolean }> = [ - { - type: 'confirm', - default: 'y', - name: 'shouldProceed', - message, - choices: ['y', 'n'], - }, - ] - const { shouldProceed } = await inquirer.prompt(questions) - if (!shouldProceed) exit('Release cancelled.') -} +// --------------------------------------------------------------------------- +// Pure helpers (testable) +// --------------------------------------------------------------------------- -const CLAUDE_TIMEOUT_MS = 120_000 const PR_NUMBER_REGEX = /\(#(\d+)\)\s*$/ -const extractPrNumbers = (messages: string[]): number[] => { +export const extractPrNumbers = (messages: string[]): number[] => { const prNumbers: number[] = [] for (const msg of messages) { const match = msg.match(PR_NUMBER_REGEX) @@ -71,25 +56,7 @@ const extractPrNumbers = (messages: string[]): number[] => { return Array.from(new Set(prNumbers)) } -const fetchPrBodies = async (prNumbers: number[]): Promise> => { - const results = new Map() - const settled = await Promise.allSettled( - prNumbers.map(async prNum => { - const stdout = (await pify(exec)( - `gh api repos/{owner}/{repo}/pulls/${prNum} --jq '.body'`, - )) as string - return { prNum, body: stdout.trim() } - }), - ) - for (const result of settled) { - if (result.status === 'fulfilled' && result.value.body) { - results.set(result.value.prNum, result.value.body) - } - } - return results -} - -const extractDescription = (prBody: string): string | undefined => { +export const extractDescription = (prBody: string): string | undefined => { const descMatch = prBody.match(/## Description\s*\n([\s\S]*?)(?=\n## |$)/) if (!descMatch) return undefined const desc = descMatch[1].replace(//g, '').trim() @@ -98,66 +65,63 @@ const extractDescription = (prBody: string): string | undefined => { return desc.length > MAX_DESC_LENGTH ? `${desc.slice(0, MAX_DESC_LENGTH)}...` : desc } -const parseEnvFeatureFlags = (filePath: string): Record => { - const flags: Record = {} - if (!fs.existsSync(filePath)) return flags - const content = fs.readFileSync(filePath, 'utf-8') - for (const line of content.split('\n')) { - const trimmed = line.trim() - if (trimmed.startsWith('#') || !trimmed.includes('=')) continue - const [key, ...rest] = trimmed.split('=') - if (!key.startsWith('VITE_FEATURE_')) continue - const value = rest.join('=').trim().toLowerCase() - if (value === 'true') flags[key] = true - else if (value === 'false') flags[key] = false - } - return flags +export const deriveReleaseState = ({ + releaseSha, + mainSha, + developSha, + privateSha, + latestTagSha, + openPrereleasePr, + openReleasePr, +}: { + releaseSha: string + mainSha: string + developSha: string + privateSha: string + latestTagSha: string + openPrereleasePr: GitHubPr | undefined + openReleasePr: GitHubPr | undefined +}): ReleaseState => { + if (openPrereleasePr) return 'prerelease_pr_open' + + if (openReleasePr) return 'release_pr_open' + + if (mainSha !== latestTagSha) return 'merged_untagged' + + if (privateSha !== mainSha) return 'tagged_private_stale' + + if (releaseSha === mainSha && developSha === mainSha) return 'done' + + return 'idle' } -const getDevOnlyFlags = (): string[] => { - const rootDir = path.resolve(__dirname, '..') - const baseFlags = parseEnvFeatureFlags(path.join(rootDir, '.env')) - const prodOverrides = parseEnvFeatureFlags(path.join(rootDir, '.env.production')) - const devOverrides = parseEnvFeatureFlags(path.join(rootDir, '.env.development')) - - const prodFlags: Record = { ...baseFlags, ...prodOverrides } - const devFlags: Record = { ...baseFlags, ...devOverrides } - - const allKeys = new Set([...Object.keys(prodFlags), ...Object.keys(devFlags)]) - const devOnly: string[] = [] - for (const key of allKeys) { - if (devFlags[key] === true && prodFlags[key] !== true) { - devOnly.push(key.replace('VITE_FEATURE_', '')) - } - } - return devOnly.sort() +export const deriveHotfixState = ({ + mainSha, + privateSha, + latestTagSha, + openHotfixPr, +}: { + mainSha: string + privateSha: string + latestTagSha: string + openHotfixPr: GitHubPr | undefined +}): HotfixState => { + if (openHotfixPr) return 'hotfix_pr_open' + if (mainSha !== latestTagSha) return 'merged_untagged' + if (privateSha !== mainSha) return 'tagged_private_stale' + return 'idle' } -const getPrivateDisabledFlags = (): string[] => { - const rootDir = path.resolve(__dirname, '..') - const baseFlags = parseEnvFeatureFlags(path.join(rootDir, '.env')) - const prodOverrides = parseEnvFeatureFlags(path.join(rootDir, '.env.production')) - const privateOverrides = parseEnvFeatureFlags(path.join(rootDir, '.env.private')) +// --------------------------------------------------------------------------- +// AI release notes (kept from v1) +// --------------------------------------------------------------------------- - const prodFlags: Record = { ...baseFlags, ...prodOverrides } - const privateFlags: Record = { ...baseFlags, ...privateOverrides } - - const allKeys = new Set([...Object.keys(prodFlags), ...Object.keys(privateFlags)]) - const privateDisabled: string[] = [] - for (const key of allKeys) { - if (prodFlags[key] === true && privateFlags[key] !== true) { - privateDisabled.push(key.replace('VITE_FEATURE_', '')) - } - } - return privateDisabled.sort() -} +const CLAUDE_TIMEOUT_MS = 120_000 -const buildReleasePrompt = ( +export const buildReleasePrompt = ( version: string, messages: string[], prBodies: Map, - devOnlyFlags: string[], - privateDisabledFlags: string[], ): string => { const commitList = messages .map(msg => { @@ -171,50 +135,46 @@ const buildReleasePrompt = ( }) .join('\n') - const devOnlySection = - devOnlyFlags.length > 0 - ? `\n\n## Dev-only feature flags (enabled in dev, disabled in production)\n\n${devOnlyFlags - .map(f => `- ${f}`) - .join('\n')}` - : '' - - const privateDisabledSection = - privateDisabledFlags.length > 0 - ? `\n\n## Private-build disabled flags (enabled in production, disabled in private build)\n\n${privateDisabledFlags - .map(f => `- ${f}`) - .join('\n')}` - : '' - return `You are a release notes generator for ShapeShift Web, a decentralized crypto exchange. Given the commit list below for ${version}, produce grouped release notes in markdown with two clearly separated top-level sections. -## Rules +## STEP 1: Determine which features are behind a flag (OFF in production) + +Read the env files to determine which VITE_FEATURE_* flags are OFF in production: +- .env is the base (all environments inherit from it) +- .env.production overrides base for production +- .env.development overrides base for development + +A feature is "under flag" (not in production) if its effective production value is false, i.e. it is false in .env and not overridden to true in .env.production, OR it is overridden to false in .env.production. + +A feature is "dev-only" if it is ON in development (.env + .env.development overrides) but OFF in production. + +For each commit below, check if it relates to a flagged-off-in-prod feature by matching the feature name in the commit message to a VITE_FEATURE_* flag name (e.g. "celo" matches VITE_FEATURE_CELO, "across" matches VITE_FEATURE_ACROSS_SWAP, "agentic" matches VITE_FEATURE_AGENTIC_CHAT). Commits explicitly saying "behind feature flag" or "under flag" also count. + +## STEP 2: Write grouped release notes ### Section 1: "# Production changes - testing required" -1. Contains all PRs/commits that affect production code paths +1. Contains all PRs/commits that are NOT behind a flagged-off-in-prod feature 2. Group related commits under descriptive ## headings by feature domain (e.g. "TON chain + Stonfi swapper", "Yield improvements", "BigAmount migration") 3. List each commit as a bullet under its group, preserving the original text and PR number 4. After the bullet list, write 1-2 sentences summarizing what changed and what to test 5. For internal refactors with no user-facing changes (e.g. migrations, type changes, selector renames), note **regression testing only** and what to sanity-check 6. For dependency bumps, CI fixes, infra, docker, CSP, and asset data regeneration, group under "## Fixes, deps, and infra" with **no testing required** -7. If a commit relates to a feature listed in the private-build disabled flags below, append "**Note: disabled in private build.**" to its testing notes within the production section ### Section 2: "# Dev/local only - no production testing required" -8. Contains all PRs/commits that are gated behind dev-only feature flags listed below -9. Match commits to dev-only flags by feature name (e.g. "Celo" matches CELO, "agentic chat" matches AGENTIC_CHAT, "Mantle" matches MANTLE, "Across" matches ACROSS_SWAP, etc.) -10. Commits whose title explicitly says "behind feature flag" or "under flag" also belong here -11. Group by feature domain with brief description only - no testing notes needed since these are not visible in production +7. Contains ALL commits behind a feature flag that is OFF in production +8. Group by feature domain with brief description only - no testing notes needed since these are not visible in production ### General rules -12. Merge/backmerge commits (e.g. "Merge branch 'main' into develop") should be silently dropped -13. Keep testing notes brief and actionable - what a QA person should click on, not implementation details -14. Use present tense for summaries ("Enables TON chain" not "Enabled TON chain") -15. Do NOT use emdashes. Use regular hyphens. +9. Merge/backmerge commits (e.g. "Merge branch 'main' into develop") should be silently dropped +10. Keep testing notes brief and actionable - what a QA person should click on, not implementation details +11. Use present tense for summaries ("Enables TON chain" not "Enabled TON chain") +12. Do NOT use emdashes. Use regular hyphens. ## Commits -${commitList}${devOnlySection}${privateDisabledSection}` +${commitList}` } const spawnClaude = (cmd: string, args: string[], promptPath: string): Promise => { @@ -279,7 +239,7 @@ const isCcrAvailable = (): Promise => { }) } -const CLAUDE_ARGS = ['-p', '--model', 'opus', '--max-turns', '1'] +const CLAUDE_ARGS = ['-p', '--model', 'opus', '--max-turns', '3'] const runClaude = async (promptPath: string): Promise => { try { @@ -296,10 +256,8 @@ const generateReleaseSummary = async ( version: string, messages: string[], prBodies: Map, - devOnlyFlags: string[], - privateDisabledFlags: string[], ): Promise => { - const prompt = buildReleasePrompt(version, messages, prBodies, devOnlyFlags, privateDisabledFlags) + const prompt = buildReleasePrompt(version, messages, prBodies) const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'shapeshift-release-')) const promptPath = path.join(tmpDir, 'prompt.txt') @@ -320,56 +278,78 @@ const generateReleaseSummary = async ( } } -const createDraftPR = async (title: string, body: string): Promise => { - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'shapeshift-release-body-')) - const bodyPath = path.join(tmpDir, 'body.md') +// --------------------------------------------------------------------------- +// Git/GitHub helpers (side effects) +// --------------------------------------------------------------------------- + +const fetchOrigin = async () => { + console.log(chalk.green('Fetching...')) + await git().fetch(['origin', '--tags', '--force']) +} +const assertIsCleanRepo = async () => { + const gitStatus = await git().status() + if (!gitStatus.isClean()) { + console.log(chalk.red('Your repository is not clean. Please commit or stash your changes.')) + exit() + } +} + +const assertGhInstalled = async () => { try { - fs.writeFileSync(bodyPath, body, 'utf-8') - await pify(execFile)('gh', [ - 'pr', - 'create', - '--draft', - '--base', - 'main', - '--title', - title, - '--body-file', - bodyPath, - ]) - } finally { - try { - fs.rmSync(tmpDir, { recursive: true, force: true }) - } catch { - // best-effort cleanup - } + await pify(exec)('hash gh') + } catch { + exit(chalk.red('Please install GitHub CLI https://github.com/cli/cli#installation')) } } -const createDraftRegularPR = async (prBody: string, nextVersion: string): Promise => { - console.log(chalk.green('Creating draft PR...')) - await createDraftPR(`chore: release ${nextVersion}`, prBody) - console.log(chalk.green('Draft PR created.')) - exit(chalk.green(`Release ${nextVersion} created.`)) +const assertGhAuth = async () => { + try { + await pify(exec)('gh auth status') + } catch (e) { + exit(chalk.red((e as Error).message)) + } } -type GetCommitsReturn = { - messages: string[] - total: number +const getSha = async (ref: string): Promise => { + try { + return (await git().revparse([ref])).trim() + } catch { + return '' + } +} + +const getTagSha = async (tag: string): Promise => { + try { + return (await git().revparse([`${tag}^{}`])).trim() + } catch { + return '' + } } -const getCommits = async (branch: string): Promise => { - const latestTag = await getLatestSemverTag() - const range = latestTag ? `${latestTag}..origin/${branch}` : `origin/main..origin/${branch}` +const getCommitMessages = async (range: string): Promise => { const result = await pify(exec)(`git log --first-parent --pretty=format:"%s" ${range}`) const stdout = typeof result === 'string' ? result : (result as { stdout: string }).stdout - const messages = stdout.trim().split('\n').filter(Boolean) - - const total = messages.length - return { messages, total } + return stdout.trim().split('\n').filter(Boolean) } -type UnreleasedCommit = { hash: string; message: string } +const fetchPrBodies = async (prNumbers: number[]): Promise> => { + const results = new Map() + const settled = await Promise.allSettled( + prNumbers.map(async prNum => { + const stdout = (await pify(exec)( + `gh api repos/{owner}/{repo}/pulls/${prNum} --jq '.body'`, + )) as string + return { prNum, body: stdout.trim() } + }), + ) + for (const result of settled) { + if (result.status === 'fulfilled' && result.value.body) { + results.set(result.value.prNum, result.value.body) + } + } + return results +} const getUnreleasedCommits = async (): Promise => { const result = await pify(exec)( @@ -389,6 +369,98 @@ const getUnreleasedCommits = async (): Promise => { }) } +type WebReleaseType = Extract + +const getNextVersion = async (bump: WebReleaseType): Promise => { + const latestTag = await getLatestSemverTag() + const nextVersion = semver.inc(latestTag, bump) + if (!nextVersion) exit(chalk.red(`Could not increment ${latestTag} with bump type '${bump}'`)) + return `v${nextVersion}` +} + +const findOpenPr = async (head: string, base: string): Promise => { + const result = await pify(exec)( + `gh pr list --repo shapeshift/web --head ${head} --base ${base} --state open --json number,title --jq '.[0]'`, + ) + const stdout = typeof result === 'string' ? result : (result as { stdout: string }).stdout + const trimmed = stdout.trim() + if (!trimmed) return undefined + try { + return JSON.parse(trimmed) as GitHubPr + } catch { + return undefined + } +} + +const createPr = async ({ + base, + head, + title, + body, +}: { + base: string + head: string + title: string + body: string +}): Promise => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'shapeshift-release-body-')) + const bodyPath = path.join(tmpDir, 'body.md') + + try { + fs.writeFileSync(bodyPath, body, 'utf-8') + const result = await pify(execFile)('gh', [ + 'pr', + 'create', + '--repo', + 'shapeshift/web', + '--base', + base, + '--head', + head, + '--title', + title, + '--body-file', + bodyPath, + ]) + const stdout = typeof result === 'string' ? result : (result as { stdout: string }).stdout + return stdout.trim() + } finally { + try { + fs.rmSync(tmpDir, { recursive: true, force: true }) + } catch { + // best-effort cleanup + } + } +} + +// --------------------------------------------------------------------------- +// Inquirer prompts +// --------------------------------------------------------------------------- + +const inquireReleaseType = async (): Promise => { + const { type } = await inquirer.prompt<{ type: ReleaseType }>([ + { + type: 'list', + name: 'type', + message: 'What type of release is this?', + choices: releaseType, + }, + ]) + return type +} + +const inquireConfirm = async (message: string): Promise => { + const { shouldProceed } = await inquirer.prompt<{ shouldProceed: boolean }>([ + { + type: 'confirm', + default: true, + name: 'shouldProceed', + message, + }, + ]) + return shouldProceed +} + const inquireSelectCommits = async (commits: UnreleasedCommit[]): Promise => { const { selected } = await inquirer.prompt<{ selected: string[] }>([ { @@ -401,272 +473,377 @@ const inquireSelectCommits = async (commits: UnreleasedCommit[]): Promise selected.includes(c.hash)) } -const assertCommitsToRelease = (total: number) => { - if (!total) exit(chalk.red('No commits to release.')) -} +// --------------------------------------------------------------------------- +// Regular release flow (idempotent state machine) +// --------------------------------------------------------------------------- const doRegularRelease = async () => { - const { messages, total } = await getCommits('develop') - assertCommitsToRelease(total) + const nextVersion = await getNextVersion('minor') + const latestTag = await getLatestSemverTag() - const nextVersion = await getNextReleaseVersion('minor') + const [releaseSha, mainSha, developSha, privateSha, latestTagSha] = await Promise.all([ + getSha('origin/release'), + getSha('origin/main'), + getSha('origin/develop'), + getSha('origin/private'), + getTagSha(latestTag), + ]) - console.log(chalk.green('Generating AI release summary...')) - const prNumbers = extractPrNumbers(messages) - console.log(chalk.green(`Found ${prNumbers.length} PR references, fetching context...`)) + const [openPrereleasePr, openReleasePr] = await Promise.all([ + findOpenPr('develop', 'release'), + findOpenPr('release', 'main'), + ]) - const prBodies = prNumbers.length > 0 ? await fetchPrBodies(prNumbers) : new Map() - console.log(chalk.green(`Fetched ${prBodies.size}/${prNumbers.length} PR descriptions.`)) + const state = deriveReleaseState({ + releaseSha, + mainSha, + developSha, + privateSha, + latestTagSha, + openPrereleasePr, + openReleasePr, + }) - const devOnlyFlags = getDevOnlyFlags() - console.log(chalk.green(`Detected ${devOnlyFlags.length} dev-only feature flags.`)) + console.log(chalk.blue(`Release state: ${state}`)) + console.log(chalk.blue(`Current: ${latestTag} -> Next: ${nextVersion}`)) + + switch (state) { + case 'idle': { + // Two sub-states within idle: + // 1. Release branch ahead of main (prerelease merged) -> create release -> main PR + // 2. Release branch matches main (fresh start) -> create develop -> release PR + const prereleaseMerged = releaseSha !== mainSha + + if (prereleaseMerged) { + const messages = await getCommitMessages(`${latestTag}..origin/release`) + if (messages.length === 0) exit(chalk.red('No commits to release.')) + + console.log(chalk.green(`${messages.length} commits ready on release branch.`)) + console.log(chalk.green('Generating AI release summary...')) + + const prNumbers = extractPrNumbers(messages) + console.log(chalk.green(`Found ${prNumbers.length} PR references, fetching context...`)) + + const prBodies = + prNumbers.length > 0 ? await fetchPrBodies(prNumbers) : new Map() + console.log(chalk.green(`Fetched ${prBodies.size}/${prNumbers.length} PR descriptions.`)) + + const summary = await generateReleaseSummary(nextVersion, messages, prBodies) + const releaseBody = summary ?? messages.join('\n') + + if (summary) console.log(chalk.green('AI summary generated successfully.\n')) + console.log(chalk.blue(['', releaseBody, ''].join('\n'))) + + if (!(await inquireConfirm('Create release PR with this body?'))) exit('Release cancelled.') + + console.log(chalk.green('Creating release PR (release -> main)...')) + const releasePrUrl = await createPr({ + base: 'main', + head: 'release', + title: `chore: release ${nextVersion}`, + body: releaseBody, + }) + console.log(chalk.green(`Release PR created: ${releasePrUrl}`)) + console.log( + chalk.green( + '\nMerge the release PR on GitHub when CI passes, then run this script again.', + ), + ) + } else { + const messages = await getCommitMessages(`${latestTag}..origin/develop`) + if (messages.length === 0) exit(chalk.red('No commits to release.')) + + console.log(chalk.green(`${messages.length} commits to release.`)) + const commitList = messages.map(m => `- ${m}`).join('\n') + + if (!(await inquireConfirm(`Create prerelease PR with ${messages.length} commits?`))) + exit('Release cancelled.') + + console.log(chalk.green('Creating prerelease PR (develop -> release)...')) + const prereleasePrUrl = await createPr({ + base: 'release', + head: 'develop', + title: `chore: prerelease ${nextVersion}`, + body: `## Prerelease ${nextVersion}\n\n${commitList}`, + }) + console.log(chalk.green(`Prerelease PR created: ${prereleasePrUrl}`)) + console.log( + chalk.green( + '\nMerge the prerelease PR on GitHub, then run this script again to create the release PR.', + ), + ) + } + break + } - const privateDisabledFlags = getPrivateDisabledFlags() - console.log(chalk.green(`Detected ${privateDisabledFlags.length} private-build disabled flags.`)) + case 'prerelease_pr_open': { + if (!openPrereleasePr) break + console.log( + chalk.yellow( + `Prerelease PR is open: #${openPrereleasePr.number} - ${openPrereleasePr.title}`, + ), + ) + console.log( + chalk.yellow('Merge it on GitHub, then run this script again to create the release PR.'), + ) + break + } - const summary = await generateReleaseSummary( - nextVersion, - messages, - prBodies, - devOnlyFlags, - privateDisabledFlags, - ) - const prBody = summary ?? messages.join('\n') + case 'release_pr_open': { + if (!openReleasePr) break + console.log( + chalk.yellow(`Release PR is open: #${openReleasePr.number} - ${openReleasePr.title}`), + ) + console.log(chalk.yellow('Merge it on GitHub, then run this script again to finalize.')) + break + } - if (summary) { - console.log(chalk.green('AI summary generated successfully.\n')) - } + case 'merged_untagged': { + console.log(chalk.green(`Release merged to main. Tagging ${nextVersion}...`)) + await git().checkout(['main']) + await git().pull() + await git().tag(['-a', nextVersion, '-m', nextVersion]) + console.log(chalk.green('Pushing tag...')) + await git().push(['origin', '--tags']) + console.log(chalk.green(`Tagged ${nextVersion}.`)) + + console.log(chalk.green('Creating PR to sync private to main...')) + const privatePrUrl = await createPr({ + base: 'private', + head: 'main', + title: `chore: sync private to ${nextVersion}`, + body: `Sync private branch to main after release ${nextVersion}.`, + }) + console.log(chalk.green(`Private sync PR created: ${privatePrUrl}`)) + console.log(chalk.green('Merge it on GitHub to complete the release.')) + break + } - console.log(chalk.blue(['', prBody, ''].join('\n'))) + case 'tagged_private_stale': { + console.log(chalk.yellow(`${nextVersion} is tagged but private is behind main.`)) - const { shouldProceed } = await inquirer.prompt<{ shouldProceed: boolean }>([ - { - type: 'confirm', - default: 'y', - name: 'shouldProceed', - message: 'Do you want to create a release with this PR body?', - choices: ['y', 'n'], - }, - ]) - if (!shouldProceed) exit('Release cancelled.') - - console.log(chalk.green('Checking out develop...')) - await git().checkout(['develop']) - console.log(chalk.green('Pulling develop...')) - await git().pull() - console.log(chalk.green('Resetting release to develop...')) - // **note** - most devs are familiar with lowercase -b to check out a new branch - // capital -B will checkout and reset the branch to the current HEAD - // so we can reuse the release branch, and force push over it - // this is required as the fleek environment is pointed at this specific branch - await git().checkout(['-B', 'release']) - console.log(chalk.green('Force pushing release branch...')) - await git().push(['--force', 'origin', 'release']) - await createDraftRegularPR(prBody, nextVersion) - exit() -} + const existingPrivatePr = await findOpenPr('main', 'private') + if (existingPrivatePr) { + console.log( + chalk.yellow( + `Private sync PR already open: #${existingPrivatePr.number}. Merge it on GitHub.`, + ), + ) + } else { + console.log(chalk.green('Creating PR to sync private to main...')) + const privatePrUrl = await createPr({ + base: 'private', + head: 'main', + title: `chore: sync private to ${nextVersion}`, + body: `Sync private branch to main after release ${nextVersion}.`, + }) + console.log(chalk.green(`Private sync PR created: ${privatePrUrl}`)) + } + break + } -const doHotfixRelease = async () => { - const unreleased = await getUnreleasedCommits() - if (unreleased.length === 0) { - exit(chalk.red('No unreleased commits found between origin/main and origin/develop.')) - } + case 'done': { + console.log(chalk.green(`Release ${latestTag} is fully complete. Nothing to do.`)) + break + } - console.log(chalk.green(`Found ${unreleased.length} unreleased commit(s).\n`)) - const selected = await inquireSelectCommits(unreleased) - if (selected.length === 0) { - exit(chalk.yellow('No commits selected. Hotfix cancelled.')) + default: + break } +} - console.log(chalk.blue('\nSelected commits:')) - for (const c of selected) { - console.log(chalk.blue(` ${c.hash.slice(0, 8)} ${c.message}`)) - } - console.log() +// --------------------------------------------------------------------------- +// Hotfix release flow (idempotent state machine) +// --------------------------------------------------------------------------- - const { shouldProceed } = await inquirer.prompt<{ shouldProceed: boolean }>([ - { - type: 'confirm', - default: true, - name: 'shouldProceed', - message: 'Proceed with cherry-picking these commits onto main?', - }, +const doHotfixRelease = async () => { + const nextVersion = await getNextVersion('patch') + const latestTag = await getLatestSemverTag() + + const [mainSha, privateSha, latestTagSha] = await Promise.all([ + getSha('origin/main'), + getSha('origin/private'), + getTagSha(latestTag), ]) - if (!shouldProceed) exit('Hotfix cancelled.') - console.log(chalk.green('Checking out main...')) - await git().checkout(['main']) - console.log(chalk.green('Pulling main...')) - await git().pull() - const mainSha = (await git().revparse(['HEAD'])).trim() + const hotfixBranch = `hotfix/${nextVersion}` + const openHotfixPr = await findOpenPr(hotfixBranch, 'main') - const cherryPickOrder = [...selected].reverse() - for (const c of cherryPickOrder) { - console.log(chalk.green(`Cherry-picking ${c.hash.slice(0, 8)} ${c.message}...`)) - try { - await pify(exec)(`git cherry-pick ${c.hash}`) - } catch (err) { - try { - await pify(exec)('git cherry-pick --abort') - } catch { - // no-op + const state = deriveHotfixState({ + mainSha, + privateSha, + latestTagSha, + openHotfixPr, + }) + + console.log(chalk.blue(`Hotfix state: ${state}`)) + console.log(chalk.blue(`Current: ${latestTag} -> Next: ${nextVersion}`)) + + switch (state) { + case 'idle': { + const unreleased = await getUnreleasedCommits() + if (unreleased.length === 0) { + exit(chalk.red('No unreleased commits found between origin/main and origin/develop.')) } - await git().reset(['--hard', mainSha]) - const message = err instanceof Error ? err.message : String(err) - const shortHash = c.hash.slice(0, 8) - const shortMainSha = mainSha.slice(0, 8) - exit( - chalk.red( - `Cherry-pick failed for ${shortHash}: ${message}\nMain has been reset to ${shortMainSha}.`, - ), - ) - } - } - const nextVersion = await getNextReleaseVersion('patch') - console.log(chalk.green(`Tagging main with version ${nextVersion}`)) - await git().tag(['-a', nextVersion, '-m', nextVersion]) - console.log(chalk.green('Pushing main with tags...')) - await git().push(['origin', 'main', '--tags']) - - console.log(chalk.green('Resetting private to main...')) - await git().checkout(['-B', 'private']) - console.log(chalk.green('Pushing private...')) - await git().push(['--force', 'origin', 'private', '--tags']) - - console.log(chalk.green('Checking out develop...')) - await git().checkout(['develop']) - console.log(chalk.green('Pulling develop...')) - await git().pull() - const developSha = (await git().revparse(['HEAD'])).trim() - console.log(chalk.green('Merging main back into develop...')) - try { - await git().merge(['main']) - } catch (err) { - await git() - .merge(['--abort']) - .catch(() => {}) - await git().reset(['--hard', developSha]) - const message = err instanceof Error ? err.message : String(err) - exit( - chalk.red( - `Merge into develop failed: ${message}\n` + - `Hotfix ${nextVersion} was pushed to main but develop merge failed.\n` + - `Develop has been reset to ${developSha.slice( - 0, - 8, - )}. Please merge main into develop manually.`, - ), - ) - } - console.log(chalk.green('Pushing develop...')) - await git().push(['origin', 'develop']) + console.log(chalk.green(`Found ${unreleased.length} unreleased commit(s).\n`)) + const selected = await inquireSelectCommits(unreleased) + if (selected.length === 0) exit(chalk.yellow('No commits selected. Hotfix cancelled.')) - exit(chalk.green(`Hotfix release ${nextVersion} completed successfully.`)) -} + console.log(chalk.blue('\nSelected commits:')) + for (const c of selected) { + console.log(chalk.blue(` ${c.hash.slice(0, 8)} ${c.message}`)) + } -type WebReleaseType = Extract + if (!(await inquireConfirm('Create hotfix branch and PR?'))) exit('Hotfix cancelled.') + + console.log(chalk.green('Checking out main...')) + await git().checkout(['main']) + await git().pull() + + console.log(chalk.green(`Creating branch ${hotfixBranch}...`)) + await git().checkout(['-b', hotfixBranch]) + + const cherryPickOrder = [...selected].reverse() + for (const c of cherryPickOrder) { + console.log(chalk.green(`Cherry-picking ${c.hash.slice(0, 8)} ${c.message}...`)) + try { + await pify(exec)(`git cherry-pick ${c.hash}`) + } catch (err) { + try { + await pify(exec)('git cherry-pick --abort') + } catch { + // no-op + } + await git().checkout(['main']) + await pify(exec)(`git branch -D ${hotfixBranch}`) + const message = err instanceof Error ? err.message : String(err) + exit( + chalk.red( + `Cherry-pick failed for ${c.hash.slice(0, 8)}: ${message}\nHotfix branch deleted.`, + ), + ) + } + } -const getNextReleaseVersion = async (versionBump: WebReleaseType): Promise => { - const latestTag = await getLatestSemverTag() - const nextVersion = semver.inc(latestTag, versionBump) - if (!nextVersion) exit(chalk.red(`Could not bump version to ${nextVersion}`)) - return `v${nextVersion}` -} + console.log(chalk.green(`Pushing ${hotfixBranch}...`)) + await git().push(['-u', 'origin', hotfixBranch]) -const assertGhInstalled = async () => { - try { - await pify(exec)('hash gh') // will throw if gh is not installed - } catch (e) { - exit(chalk.red('Please install GitHub CLI https://github.com/cli/cli#installation')) - } -} + const commitList = selected.map(c => `- ${c.message}`).join('\n') + const prUrl = await createPr({ + base: 'main', + head: hotfixBranch, + title: `chore: hotfix ${nextVersion}`, + body: `## Hotfix ${nextVersion}\n\n${commitList}`, + }) -const assertGhAuth = async () => { - try { - await pify(exec)('gh auth status') // will throw if gh not authenticated - } catch (e) { - exit(chalk.red((e as Error).message)) - } -} + console.log(chalk.green(`Hotfix PR created: ${prUrl}`)) + console.log(chalk.green('Merge it on GitHub, then run this script again to finalize.')) + break + } -const isReleaseInProgress = async (): Promise => { - const { total } = await getCommits('release') - return Boolean(total) -} + case 'hotfix_pr_open': { + if (!openHotfixPr) break + console.log( + chalk.yellow(`Hotfix PR is open: #${openHotfixPr.number} - ${openHotfixPr.title}`), + ) + console.log(chalk.yellow('Merge it on GitHub, then run this script again to finalize.')) + break + } -const createRelease = async () => { - ;(await inquireReleaseType()) === 'Regular' ? await doRegularRelease() : await doHotfixRelease() -} + case 'merged_untagged': { + console.log(chalk.green(`Hotfix merged to main. Tagging ${nextVersion}...`)) + await git().checkout(['main']) + await git().pull() + await git().tag(['-a', nextVersion, '-m', nextVersion]) + console.log(chalk.green('Pushing tag...')) + await git().push(['origin', '--tags']) + console.log(chalk.green(`Tagged ${nextVersion}.`)) + + console.log(chalk.green('Creating PR to sync private to main...')) + const privatePrUrl = await createPr({ + base: 'private', + head: 'main', + title: `chore: sync private to ${nextVersion}`, + body: `Sync private branch to main after hotfix ${nextVersion}.`, + }) + console.log(chalk.green(`Private sync PR created: ${privatePrUrl}`)) + + console.log(chalk.green('Creating backmerge PR (main -> develop)...')) + const backmergeUrl = await createPr({ + base: 'develop', + head: 'main', + title: `chore: backmerge ${nextVersion} into develop`, + body: `Backmerge main into develop after hotfix ${nextVersion} to sync cherry-picked commits.`, + }) + console.log(chalk.green(`Backmerge PR created: ${backmergeUrl}`)) + console.log(chalk.green('Merge both PRs on GitHub to complete the hotfix.')) + break + } -const mergeRelease = async () => { - const { messages, total } = await getCommits('release') - assertCommitsToRelease(total) - await inquireProceedWithCommits(messages, 'merge') - - console.log(chalk.green('Checking out release...')) - await git().checkout(['release']) - console.log(chalk.green('Pulling release...')) - await git().pull() - console.log(chalk.green('Checking out main...')) - await git().checkout(['main']) - console.log(chalk.green('Pulling main...')) - await git().pull() - console.log(chalk.green('Merging release...')) - await git().merge(['release']) - const nextVersion = await getNextReleaseVersion('minor') - console.log(chalk.green(`Tagging main with version ${nextVersion}`)) - await git().tag(['-a', nextVersion, '-m', nextVersion]) - console.log(chalk.green('Pushing main...')) - await git().push(['origin', 'main', '--tags']) - /** - * we want private to track main, as Cloudflare builds with different env vars - * based off the branch name, and there's in sufficient information with a single branch name. - */ - console.log(chalk.green('Resetting private to main...')) - await git().checkout(['-B', 'private']) - console.log(chalk.green('Pushing private...')) - await git().push(['--force', 'origin', 'private', '--tags']) - console.log(chalk.green('Checking out develop...')) - await git().checkout(['develop']) - console.log(chalk.green('Pulling develop...')) - await git().pull() - const developSha = (await git().revparse(['HEAD'])).trim() - console.log(chalk.green('Merging main back into develop...')) - try { - await git().merge(['main']) - } catch (err) { - await git() - .merge(['--abort']) - .catch(() => {}) - await git().reset(['--hard', developSha]) - const message = err instanceof Error ? err.message : String(err) - exit( - chalk.red( - `Merge into develop failed: ${message}\n` + - `Release ${nextVersion} was pushed to main but develop merge failed.\n` + - `Develop has been reset to ${developSha.slice( - 0, - 8, - )}. Please merge main into develop manually.`, - ), - ) + case 'tagged_private_stale': { + console.log(chalk.yellow(`${nextVersion} is tagged but private is behind main.`)) + + const existingPrivatePr = await findOpenPr('main', 'private') + if (existingPrivatePr) { + console.log( + chalk.yellow( + `Private sync PR already open: #${existingPrivatePr.number}. Merge it on GitHub.`, + ), + ) + } else { + console.log(chalk.green('Creating PR to sync private to main...')) + const privatePrUrl = await createPr({ + base: 'private', + head: 'main', + title: `chore: sync private to ${nextVersion}`, + body: `Sync private branch to main after hotfix ${nextVersion}.`, + }) + console.log(chalk.green(`Private sync PR created: ${privatePrUrl}`)) + } + + const existingBackmerge = await findOpenPr('main', 'develop') + if (!existingBackmerge) { + const mainDevelopDiff = await getCommitMessages('origin/develop..origin/main') + if (mainDevelopDiff.length > 0) { + console.log(chalk.green('Creating backmerge PR (main -> develop)...')) + const backmergeUrl = await createPr({ + base: 'develop', + head: 'main', + title: `chore: backmerge ${nextVersion} into develop`, + body: `Backmerge main into develop after hotfix ${nextVersion}.`, + }) + console.log(chalk.green(`Backmerge PR created: ${backmergeUrl}`)) + } + } + break + } + + case 'done': { + console.log(chalk.green(`Hotfix ${latestTag} is fully complete. Nothing to do.`)) + break + } + + default: + break } - console.log(chalk.green('Pushing develop...')) - await git().push(['origin', 'develop']) - exit(chalk.green(`Release ${nextVersion} completed successfully.`)) } +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + const main = async () => { await assertIsCleanRepo() await assertGhInstalled() await assertGhAuth() - await fetch() - ;(await isReleaseInProgress()) ? await mergeRelease() : await createRelease() + await fetchOrigin() + + const type = await inquireReleaseType() + type === 'Regular' ? await doRegularRelease() : await doHotfixRelease() } main() From 262b639fa19b93eebeacceeb39c0dba0ffd0209e Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:43:04 +0100 Subject: [PATCH 14/15] feat: bebop solana swapper take 2 (#12111) --- packages/hdwallet-core/src/solana.ts | 6 + packages/hdwallet-gridplus/src/gridplus.ts | 10 ++ packages/hdwallet-gridplus/src/solana.ts | 42 +++++- packages/hdwallet-ledger/src/ledger.ts | 7 + packages/hdwallet-ledger/src/solana.ts | 25 +++- packages/hdwallet-native/src/solana.ts | 17 +++ packages/hdwallet-phantom/src/phantom.ts | 8 +- packages/hdwallet-phantom/src/solana.ts | 16 +++ packages/hdwallet-trezor/src/solana.ts | 28 +++- packages/hdwallet-trezor/src/trezor.ts | 6 + packages/hdwallet-vultisig/src/solana.ts | 16 +++ packages/hdwallet-vultisig/src/vultisig.ts | 8 +- .../src/swappers/BebopSwapper/BebopSwapper.ts | 2 + .../src/swappers/BebopSwapper/endpoints.ts | 65 ++++++++- .../BebopSwapper/executeSolanaMessage.ts | 53 ++++++++ .../getBebopSolanaTradeQuote.ts | 118 ++++++++++++++++ .../getBebopSolanaTradeRate.ts | 103 ++++++++++++++ .../src/swappers/BebopSwapper/types.ts | 64 ++++++++- .../BebopSwapper/utils/fetchFromBebop.ts | 126 +++++++++++++++++- .../BebopSwapper/utils/helpers/helpers.ts | 14 +- packages/swapper/src/types.ts | 20 +++ .../TradeConfirm/hooks/useTradeExecution.tsx | 31 ++++- .../useTradeNetworkFeeCryptoBaseUnit.tsx | 9 ++ src/lib/tradeExecution.ts | 51 +++++++ 24 files changed, 830 insertions(+), 15 deletions(-) create mode 100644 packages/swapper/src/swappers/BebopSwapper/executeSolanaMessage.ts create mode 100644 packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts create mode 100644 packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.ts diff --git a/packages/hdwallet-core/src/solana.ts b/packages/hdwallet-core/src/solana.ts index c4d1df77c7c..9ae459212e4 100644 --- a/packages/hdwallet-core/src/solana.ts +++ b/packages/hdwallet-core/src/solana.ts @@ -54,6 +54,11 @@ export interface SolanaSignTx { pubKey?: string } +export interface SolanaSignSerializedTx { + addressNList: BIP32Path + serializedTx: string // base64-encoded VersionedTransaction +} + export interface SolanaSignedTx { serialized: string signatures: string[] @@ -92,6 +97,7 @@ export interface SolanaWallet extends SolanaWalletInfo, HDWallet { solanaGetAddress(msg: SolanaGetAddress): Promise solanaGetAddresses?(msgs: SolanaGetAddress[]): Promise solanaSignTx(msg: SolanaSignTx): Promise + solanaSignSerializedTx?(msg: SolanaSignSerializedTx): Promise solanaSendTx?(msg: SolanaSignTx): Promise } diff --git a/packages/hdwallet-gridplus/src/gridplus.ts b/packages/hdwallet-gridplus/src/gridplus.ts index 5086d729ab1..3c52461bfd4 100644 --- a/packages/hdwallet-gridplus/src/gridplus.ts +++ b/packages/hdwallet-gridplus/src/gridplus.ts @@ -645,6 +645,16 @@ export class GridPlusHDWallet return solana.solanaSignTx(this.client, msg) } + async solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + ): Promise { + this.assertSolanaFwSupport() + if (!this.expectedActiveWalletId) throw new Error('Expected SafeCard ID not set') + + await this.validateActiveWallet(this.expectedActiveWalletId, this.expectedType) + return solana.solanaSignSerializedTx(this.client, msg) + } + async cosmosGetAddress(msg: core.CosmosGetAddress): Promise { if (!this.client) throw new Error('Device not connected') return cosmos.cosmosGetAddress(this.client, msg) diff --git a/packages/hdwallet-gridplus/src/solana.ts b/packages/hdwallet-gridplus/src/solana.ts index f3f34b381f2..44a91400ff0 100644 --- a/packages/hdwallet-gridplus/src/solana.ts +++ b/packages/hdwallet-gridplus/src/solana.ts @@ -1,5 +1,5 @@ import * as core from '@shapeshiftoss/hdwallet-core' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, VersionedTransaction } from '@solana/web3.js' import bs58 from 'bs58' import type { Client } from 'gridplus-sdk' import { Constants } from 'gridplus-sdk' @@ -61,3 +61,43 @@ export async function solanaSignTx( signatures: transaction.signatures.map(sig => Buffer.from(sig).toString('base64')), } } + +export async function solanaSignSerializedTx( + client: Client, + msg: core.SolanaSignSerializedTx, +): Promise { + const address = await solanaGetAddress(client, { + addressNList: msg.addressNList, + }) + if (!address) throw new Error('Failed to get Solana address') + + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const messageBytes = transaction.message.serialize() + + const signData = await client.sign({ + data: { + payload: messageBytes, + curveType: Constants.SIGNING.CURVES.ED25519, + hashType: Constants.SIGNING.HASHES.NONE, + encodingType: Constants.SIGNING.ENCODINGS.SOLANA, + signerPath: core.ed25519Path(msg.addressNList), + }, + }) + + if (!signData?.sig) throw new Error('No signature returned from device') + + const { r, s } = signData.sig + + if (!Buffer.isBuffer(r)) throw new Error('Invalid signature (r)') + if (!Buffer.isBuffer(s)) throw new Error('Invalid signature (s)') + + const signature = Buffer.concat([r, s]) + + transaction.addSignature(new PublicKey(address), signature) + + return { + serialized: Buffer.from(transaction.serialize()).toString('base64'), + signatures: transaction.signatures.map(sig => Buffer.from(sig).toString('base64')), + } +} diff --git a/packages/hdwallet-ledger/src/ledger.ts b/packages/hdwallet-ledger/src/ledger.ts index 490f4f6b159..a855c2b728a 100644 --- a/packages/hdwallet-ledger/src/ledger.ts +++ b/packages/hdwallet-ledger/src/ledger.ts @@ -698,6 +698,13 @@ export class LedgerHDWallet return solana.solanaSignTx(this.transport, msg) } + public async solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + ): Promise { + await this.validateCurrentApp('Solana') + return solana.solanaSignSerializedTx(this.transport, msg) + } + public async suiGetAddress(msg: core.SuiGetAddress): Promise { await this.validateCurrentApp('Sui') return sui.suiGetAddress(this.transport, msg) diff --git a/packages/hdwallet-ledger/src/solana.ts b/packages/hdwallet-ledger/src/solana.ts index 53aa67d3ef6..48f8e124cbf 100644 --- a/packages/hdwallet-ledger/src/solana.ts +++ b/packages/hdwallet-ledger/src/solana.ts @@ -1,5 +1,5 @@ import * as core from '@shapeshiftoss/hdwallet-core' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, VersionedTransaction } from '@solana/web3.js' import bs58 from 'bs58' import type { LedgerTransport } from './transport' @@ -46,3 +46,26 @@ export async function solanaSignTx( signatures: transaction.signatures.map(signature => Buffer.from(signature).toString('base64')), } } + +export async function solanaSignSerializedTx( + transport: LedgerTransport, + msg: core.SolanaSignSerializedTx, +): Promise { + const address = await solanaGetAddress(transport, msg) + + const bip32Path = addressNListToBIP32Path(msg.addressNList) + + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const txBuffer = Buffer.from(transaction.message.serialize()) + + const res = await transport.call('Solana', 'signTransaction', bip32Path, txBuffer) + handleError(res, transport, 'Unable to sign Solana transaction') + + transaction.addSignature(new PublicKey(address), res.payload.signature) + + return { + serialized: Buffer.from(transaction.serialize()).toString('base64'), + signatures: transaction.signatures.map(signature => Buffer.from(signature).toString('base64')), + } +} diff --git a/packages/hdwallet-native/src/solana.ts b/packages/hdwallet-native/src/solana.ts index 5403497f435..e9e60ecda3c 100644 --- a/packages/hdwallet-native/src/solana.ts +++ b/packages/hdwallet-native/src/solana.ts @@ -1,4 +1,5 @@ import * as core from '@shapeshiftoss/hdwallet-core' +import { VersionedTransaction } from '@solana/web3.js' import { Isolation } from './crypto' import { SolanaAdapter } from './crypto/isolation/adapters/solana' @@ -67,5 +68,21 @@ export function MixinNativeSolanaWallet { + return this.needsMnemonic(!!this.adapter, async () => { + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const signedTransaction = await this.adapter!.signTransaction(transaction, msg.addressNList) + return { + serialized: Buffer.from(signedTransaction.serialize()).toString('base64'), + signatures: signedTransaction.signatures.map(signature => + Buffer.from(signature).toString('base64'), + ), + } + }) + } } } diff --git a/packages/hdwallet-phantom/src/phantom.ts b/packages/hdwallet-phantom/src/phantom.ts index c896e474724..f34c9f41330 100644 --- a/packages/hdwallet-phantom/src/phantom.ts +++ b/packages/hdwallet-phantom/src/phantom.ts @@ -8,7 +8,7 @@ import isObject from 'lodash/isObject' import * as btc from './bitcoin' import * as eth from './ethereum' -import { solanaSendTx, solanaSignTx } from './solana' +import { solanaSendTx, solanaSignSerializedTx, solanaSignTx } from './solana' import * as sui from './sui' import type { PhantomEvmProvider, @@ -488,6 +488,12 @@ export class PhantomHDWallet return address ? solanaSignTx(msg, this.solanaProvider, address) : null } + public async solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + ): Promise { + return solanaSignSerializedTx(msg, this.solanaProvider) + } + public async solanaSendTx(msg: core.SolanaSignTx): Promise { const address = await this.solanaGetAddress() return address ? solanaSendTx(msg, this.solanaProvider, address) : null diff --git a/packages/hdwallet-phantom/src/solana.ts b/packages/hdwallet-phantom/src/solana.ts index b6754736d72..ed2fa1b631e 100644 --- a/packages/hdwallet-phantom/src/solana.ts +++ b/packages/hdwallet-phantom/src/solana.ts @@ -1,5 +1,6 @@ import * as core from '@shapeshiftoss/hdwallet-core' import type { PublicKey } from '@solana/web3.js' +import { VersionedTransaction } from '@solana/web3.js' import type { PhantomSolanaProvider } from './types' @@ -22,6 +23,21 @@ export async function solanaSignTx( } } +export async function solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + provider: PhantomSolanaProvider, +): Promise { + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const signedTransaction = await provider.signTransaction(transaction) + return { + serialized: Buffer.from(signedTransaction.serialize()).toString('base64'), + signatures: signedTransaction.signatures.map(signature => + Buffer.from(signature).toString('base64'), + ), + } +} + export async function solanaSendTx( msg: core.SolanaSignTx, provider: PhantomSolanaProvider, diff --git a/packages/hdwallet-trezor/src/solana.ts b/packages/hdwallet-trezor/src/solana.ts index d159f72b76b..19fe85c2649 100644 --- a/packages/hdwallet-trezor/src/solana.ts +++ b/packages/hdwallet-trezor/src/solana.ts @@ -1,5 +1,5 @@ import * as core from '@shapeshiftoss/hdwallet-core' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, VersionedTransaction } from '@solana/web3.js' import type { TrezorTransport } from './transport' import { handleError } from './utils' @@ -88,3 +88,29 @@ export async function solanaSignTx( signatures: transaction.signatures.map(sig => Buffer.from(sig).toString('base64')), } } + +export async function solanaSignSerializedTx( + transport: TrezorTransport, + msg: core.SolanaSignSerializedTx, +): Promise { + const address = await solanaGetAddress(transport, msg) + + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const serializedMessage = Buffer.from(transaction.message.serialize()) + + const res = await transport.call('solanaSignTransaction', { + path: core.solanaAddressNListToBIP32(msg.addressNList), + serializedTx: serializedMessage.toString('hex'), + }) + + handleError(transport, res, 'Unable to sign Solana transaction with Trezor') + + const signature = Buffer.from(res.payload.signature, 'hex') + transaction.addSignature(new PublicKey(address), signature) + + return { + serialized: Buffer.from(transaction.serialize()).toString('base64'), + signatures: transaction.signatures.map(sig => Buffer.from(sig).toString('base64')), + } +} diff --git a/packages/hdwallet-trezor/src/trezor.ts b/packages/hdwallet-trezor/src/trezor.ts index 7cd2a292515..9b8b55f0ca8 100644 --- a/packages/hdwallet-trezor/src/trezor.ts +++ b/packages/hdwallet-trezor/src/trezor.ts @@ -644,6 +644,12 @@ export class TrezorHDWallet return Sol.solanaSignTx(this.transport, msg) } + public async solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + ): Promise { + return Sol.solanaSignSerializedTx(this.transport, msg) + } + public solanaGetAccountPaths(msg: core.SolanaGetAccountPaths): core.SolanaAccountPath[] { return core.solanaGetAccountPaths(msg) } diff --git a/packages/hdwallet-vultisig/src/solana.ts b/packages/hdwallet-vultisig/src/solana.ts index e108d0d7c38..fd6e4951a82 100644 --- a/packages/hdwallet-vultisig/src/solana.ts +++ b/packages/hdwallet-vultisig/src/solana.ts @@ -1,5 +1,6 @@ import * as core from '@shapeshiftoss/hdwallet-core' import type { PublicKey } from '@solana/web3.js' +import { VersionedTransaction } from '@solana/web3.js' import type { VultisigSolanaProvider } from './types' @@ -22,6 +23,21 @@ export async function solanaSignTx( } } +export async function solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + provider: VultisigSolanaProvider, +): Promise { + const txBytes = Buffer.from(msg.serializedTx, 'base64') + const transaction = VersionedTransaction.deserialize(txBytes) + const signedTransaction = await provider.signTransaction(transaction) + return { + serialized: Buffer.from(signedTransaction.serialize()).toString('base64'), + signatures: signedTransaction.signatures.map(signature => + Buffer.from(signature).toString('base64'), + ), + } +} + export async function solanaSendTx( msg: core.SolanaSignTx, provider: VultisigSolanaProvider, diff --git a/packages/hdwallet-vultisig/src/vultisig.ts b/packages/hdwallet-vultisig/src/vultisig.ts index f753719ae7e..e8ba4ec7396 100644 --- a/packages/hdwallet-vultisig/src/vultisig.ts +++ b/packages/hdwallet-vultisig/src/vultisig.ts @@ -8,7 +8,7 @@ import isObject from 'lodash/isObject' import * as btc from './bitcoin' import * as cosmos from './cosmos' import * as eth from './ethereum' -import { solanaSendTx, solanaSignTx } from './solana' +import { solanaSendTx, solanaSignSerializedTx, solanaSignTx } from './solana' import * as thorchain from './thorchain' import type { VultisigEvmProvider, @@ -470,6 +470,12 @@ export class VultisigHDWallet return address ? solanaSignTx(msg, this.solanaProvider, address) : null } + public async solanaSignSerializedTx( + msg: core.SolanaSignSerializedTx, + ): Promise { + return solanaSignSerializedTx(msg, this.solanaProvider) + } + public async solanaSendTx(msg: core.SolanaSignTx): Promise { const address = await this.solanaGetAddress() return address ? solanaSendTx(msg, this.solanaProvider, address) : null diff --git a/packages/swapper/src/swappers/BebopSwapper/BebopSwapper.ts b/packages/swapper/src/swappers/BebopSwapper/BebopSwapper.ts index 03af585b90b..6b53ea77905 100644 --- a/packages/swapper/src/swappers/BebopSwapper/BebopSwapper.ts +++ b/packages/swapper/src/swappers/BebopSwapper/BebopSwapper.ts @@ -1,6 +1,8 @@ import type { Swapper } from '../../types' import { executeEvmTransaction } from '../../utils' +import { executeSolanaMessage } from './executeSolanaMessage' export const bebopSwapper: Swapper = { executeEvmTransaction, + executeSolanaMessage, } diff --git a/packages/swapper/src/swappers/BebopSwapper/endpoints.ts b/packages/swapper/src/swappers/BebopSwapper/endpoints.ts index 89ceb97be99..a9639698466 100644 --- a/packages/swapper/src/swappers/BebopSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/BebopSwapper/endpoints.ts @@ -1,14 +1,38 @@ +import { solanaChainId } from '@shapeshiftoss/caip' import { evm } from '@shapeshiftoss/chain-adapters' import BigNumber from 'bignumber.js' import { fromHex } from 'viem' -import type { GetEvmTradeQuoteInputBase, GetEvmTradeRateInput, SwapperApi } from '../../types' -import { checkEvmSwapStatus, getExecutableTradeStep, isExecutableTradeQuote } from '../../utils' +import type { + GetEvmTradeQuoteInputBase, + GetEvmTradeRateInput, + GetSolanaTradeQuoteInput, + GetSolanaTradeRateInput, + SwapperApi, +} from '../../types' +import { + checkEvmSwapStatus, + checkSolanaSwapStatus, + getExecutableTradeStep, + isExecutableTradeQuote, +} from '../../utils' +import { getBebopSolanaTradeQuote } from './getBebopSolanaTradeQuote/getBebopSolanaTradeQuote' +import { getBebopSolanaTradeRate } from './getBebopSolanaTradeRate/getBebopSolanaTradeRate' import { getBebopTradeQuote } from './getBebopTradeQuote/getBebopTradeQuote' import { getBebopTradeRate } from './getBebopTradeRate/getBebopTradeRate' +import { isSolanaChainId } from './utils/helpers/helpers' export const bebopApi: SwapperApi = { getTradeQuote: async (input, { assertGetEvmChainAdapter, assetsById, config }) => { + if (isSolanaChainId(input.sellAsset.chainId)) { + const tradeQuoteResult = await getBebopSolanaTradeQuote( + input as GetSolanaTradeQuoteInput, + assetsById, + config.VITE_BEBOP_API_KEY, + ) + return tradeQuoteResult.map(tradeQuote => [tradeQuote]) + } + const tradeQuoteResult = await getBebopTradeQuote( input as GetEvmTradeQuoteInputBase, assertGetEvmChainAdapter, @@ -19,6 +43,15 @@ export const bebopApi: SwapperApi = { return tradeQuoteResult.map(tradeQuote => [tradeQuote]) }, getTradeRate: async (input, { assertGetEvmChainAdapter, assetsById, config }) => { + if (isSolanaChainId(input.sellAsset.chainId)) { + const tradeRateResult = await getBebopSolanaTradeRate( + input as GetSolanaTradeRateInput, + assetsById, + config.VITE_BEBOP_API_KEY, + ) + return tradeRateResult.map(tradeRate => [tradeRate]) + } + const tradeRateResult = await getBebopTradeRate( input as GetEvmTradeRateInput, assertGetEvmChainAdapter, @@ -60,10 +93,24 @@ export const bebopApi: SwapperApi = { to, value, ...feeData, - // Use the higher amount of the node or the API, as the node doesn't always provide enough gas padding gasLimit: BigNumber.max(feeData.gasLimit, gas).toFixed(), }) }, + getUnsignedSolanaMessage: ({ tradeQuote, stepIndex }) => { + if (!isExecutableTradeQuote(tradeQuote)) throw new Error('Unable to execute a trade rate quote') + + const step = getExecutableTradeStep(tradeQuote, stepIndex) + + const { bebopSolanaSerializedTx, bebopQuoteId } = step + if (!bebopSolanaSerializedTx || !bebopQuoteId) { + throw new Error('Bebop Solana transaction metadata is required') + } + + return Promise.resolve({ + serializedTx: bebopSolanaSerializedTx, + quoteId: bebopQuoteId, + }) + }, getEvmTransactionFees: async ({ from, stepIndex, @@ -87,5 +134,15 @@ export const bebopApi: SwapperApi = { return feeData.networkFeeCryptoBaseUnit }, - checkTradeStatus: checkEvmSwapStatus, + checkTradeStatus: input => { + if (input.chainId === solanaChainId) { + return checkSolanaSwapStatus({ + txHash: input.txHash, + address: input.address, + assertGetSolanaChainAdapter: input.assertGetSolanaChainAdapter, + }) + } + + return checkEvmSwapStatus(input) + }, } diff --git a/packages/swapper/src/swappers/BebopSwapper/executeSolanaMessage.ts b/packages/swapper/src/swappers/BebopSwapper/executeSolanaMessage.ts new file mode 100644 index 00000000000..ec9ad000959 --- /dev/null +++ b/packages/swapper/src/swappers/BebopSwapper/executeSolanaMessage.ts @@ -0,0 +1,53 @@ +import bs58 from 'bs58' + +import type { SolanaMessageExecutionProps, SolanaMessageToSign, SwapperConfig } from '../../types' +import { bebopServiceFactory } from './utils/bebopService' + +type BebopOrderResponse = { + txHash: string + status: string +} + +export const executeSolanaMessage = async ( + messageData: SolanaMessageToSign, + { signSerializedTransaction }: SolanaMessageExecutionProps, + config: SwapperConfig, +): Promise => { + const { serializedTx, quoteId } = messageData + + if (!serializedTx || !quoteId) { + throw new Error('Missing serializedTx or quoteId in message data') + } + + const signatures = await signSerializedTransaction(serializedTx) + + if (!signatures.length) { + throw new Error('No signatures returned from wallet') + } + + const userSignatureBase64 = signatures[signatures.length - 1] + const userSignatureBytes = Buffer.from(userSignatureBase64, 'base64') + const userSignatureBase58 = bs58.encode(userSignatureBytes) + + const bebopService = bebopServiceFactory({ apiKey: config.VITE_BEBOP_API_KEY }) + const maybeOrderResponse = await bebopService.post( + 'https://api.bebop.xyz/pmm/solana/v3/order', + { + quote_id: quoteId, + signature: userSignatureBase58, + }, + ) + + if (maybeOrderResponse.isErr()) { + const error = maybeOrderResponse.unwrapErr() + throw new Error(`Bebop order submission failed: ${error.cause ?? error.message}`) + } + + const { data: orderResponse } = maybeOrderResponse.unwrap() + + if (!orderResponse.txHash) { + throw new Error('Bebop order response missing txHash') + } + + return orderResponse.txHash +} diff --git a/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts b/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts new file mode 100644 index 00000000000..06eead0b2be --- /dev/null +++ b/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeQuote/getBebopSolanaTradeQuote.ts @@ -0,0 +1,118 @@ +import type { AssetsByIdPartial } from '@shapeshiftoss/types' +import type { Result } from '@sniptt/monads' +import { Err, Ok } from '@sniptt/monads' +import { v4 as uuid } from 'uuid' + +import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants' +import type { + GetSolanaTradeQuoteInput, + SingleHopTradeQuoteSteps, + SwapErrorRight, + TradeQuote, +} from '../../../types' +import { SwapperName, TradeQuoteError } from '../../../types' +import { makeSwapErrorRight } from '../../../utils' +import { fetchBebopSolanaQuote } from '../utils/fetchFromBebop' +import { assertValidTrade, calculateRate } from '../utils/helpers/helpers' + +export async function getBebopSolanaTradeQuote( + input: GetSolanaTradeQuoteInput, + _assetsById: AssetsByIdPartial, + apiKey: string, +): Promise> { + const { + sellAsset, + buyAsset, + accountNumber, + sendAddress, + receiveAddress, + affiliateBps, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + } = input + + const assertion = assertValidTrade({ buyAsset, sellAsset }) + if (assertion.isErr()) return Err(assertion.unwrapErr()) + + const takerAddress = sendAddress || receiveAddress + + if (!takerAddress) { + return Err( + makeSwapErrorRight({ + message: 'Cannot execute quote without a wallet address', + code: TradeQuoteError.UnknownError, + }), + ) + } + + const slippageTolerancePercentageDecimal = + input.slippageTolerancePercentageDecimal ?? + getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Bebop) + + const maybeBebopQuoteResponse = await fetchBebopSolanaQuote({ + buyAsset, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + takerAddress, + receiverAddress: receiveAddress ?? takerAddress, + slippageTolerancePercentageDecimal, + affiliateBps, + apiKey, + }) + + if (maybeBebopQuoteResponse.isErr()) return Err(maybeBebopQuoteResponse.unwrapErr()) + const bebopQuoteResponse = maybeBebopQuoteResponse.unwrap() + + const sellTokenAddress = Object.keys(bebopQuoteResponse.sellTokens)[0] + const buyTokenAddress = Object.keys(bebopQuoteResponse.buyTokens)[0] + + if (!sellTokenAddress || !buyTokenAddress) { + return Err( + makeSwapErrorRight({ + message: 'Invalid token addresses in response', + code: TradeQuoteError.InvalidResponse, + }), + ) + } + + const sellAmount = bebopQuoteResponse.sellTokens[sellTokenAddress].amount + const buyAmount = bebopQuoteResponse.buyTokens[buyTokenAddress].amount + + const rate = calculateRate({ buyAmount, sellAmount, buyAsset, sellAsset }) + + const buyTokenData = bebopQuoteResponse.buyTokens[buyTokenAddress] + const buyAmountBeforeFeesCryptoBaseUnit = buyTokenData.amountBeforeFee || buyAmount + const buyAmountAfterFeesCryptoBaseUnit = buyAmount + + // Bebop Solana is gasless - Bebop pays the network fees via co-signing + const networkFeeCryptoBaseUnit = '0' + + return Ok({ + id: uuid(), + quoteOrRate: 'quote' as const, + receiveAddress: receiveAddress ?? takerAddress, + affiliateBps, + slippageTolerancePercentageDecimal, + rate, + swapperName: SwapperName.Bebop, + steps: [ + { + estimatedExecutionTimeMs: 0, + allowanceContract: '0x0', + buyAsset, + sellAsset, + accountNumber, + rate, + feeData: { + protocolFees: {}, + networkFeeCryptoBaseUnit, + }, + buyAmountBeforeFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + source: SwapperName.Bebop, + bebopSolanaSerializedTx: bebopQuoteResponse.solana_tx, + bebopQuoteId: bebopQuoteResponse.quoteId, + }, + ] as SingleHopTradeQuoteSteps, + }) +} diff --git a/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.ts b/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.ts new file mode 100644 index 00000000000..273cf48c5ba --- /dev/null +++ b/packages/swapper/src/swappers/BebopSwapper/getBebopSolanaTradeRate/getBebopSolanaTradeRate.ts @@ -0,0 +1,103 @@ +import type { AssetsByIdPartial } from '@shapeshiftoss/types' +import type { Result } from '@sniptt/monads' +import { Err, Ok } from '@sniptt/monads' +import { v4 as uuid } from 'uuid' + +import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constants' +import type { + GetSolanaTradeRateInput, + SingleHopTradeRateSteps, + SwapErrorRight, + TradeRate, +} from '../../../types' +import { SwapperName, TradeQuoteError } from '../../../types' +import { makeSwapErrorRight } from '../../../utils' +import { fetchBebopSolanaPrice } from '../utils/fetchFromBebop' +import { assertValidTrade, calculateRate } from '../utils/helpers/helpers' + +export async function getBebopSolanaTradeRate( + input: GetSolanaTradeRateInput, + _assetsById: AssetsByIdPartial, + apiKey: string, +): Promise> { + const { + sellAsset, + buyAsset, + accountNumber, + receiveAddress, + affiliateBps, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + } = input + + const assertion = assertValidTrade({ buyAsset, sellAsset }) + if (assertion.isErr()) return Err(assertion.unwrapErr()) + + const slippageTolerancePercentageDecimal = + input.slippageTolerancePercentageDecimal ?? + getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Bebop) + + const maybeBebopPriceResponse = await fetchBebopSolanaPrice({ + buyAsset, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + receiveAddress, + affiliateBps, + apiKey, + }) + + if (maybeBebopPriceResponse.isErr()) return Err(maybeBebopPriceResponse.unwrapErr()) + const bebopPriceResponse = maybeBebopPriceResponse.unwrap() + + const sellTokenAddress = Object.keys(bebopPriceResponse.sellTokens)[0] + const buyTokenAddress = Object.keys(bebopPriceResponse.buyTokens)[0] + + if (!sellTokenAddress || !buyTokenAddress) { + return Err( + makeSwapErrorRight({ + message: 'Invalid token addresses in response', + code: TradeQuoteError.InvalidResponse, + }), + ) + } + + const sellAmount = bebopPriceResponse.sellTokens[sellTokenAddress].amount + const buyAmount = bebopPriceResponse.buyTokens[buyTokenAddress].amount + + const rate = calculateRate({ buyAmount, sellAmount, buyAsset, sellAsset }) + + const buyTokenData = bebopPriceResponse.buyTokens[buyTokenAddress] + const buyAmountBeforeFeesCryptoBaseUnit = buyTokenData.amountBeforeFee || buyAmount + const buyAmountAfterFeesCryptoBaseUnit = buyAmount + + // Bebop Solana is gasless - Bebop pays the network fees via co-signing + const networkFeeCryptoBaseUnit = '0' + + return Ok({ + id: uuid(), + quoteOrRate: 'rate' as const, + accountNumber: undefined, + receiveAddress, + affiliateBps, + slippageTolerancePercentageDecimal, + rate, + swapperName: SwapperName.Bebop, + steps: [ + { + estimatedExecutionTimeMs: 0, + allowanceContract: '0x0', + buyAsset, + sellAsset, + accountNumber, + rate, + feeData: { + protocolFees: {}, + networkFeeCryptoBaseUnit, + }, + buyAmountBeforeFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + source: SwapperName.Bebop, + }, + ] as SingleHopTradeRateSteps, + }) +} diff --git a/packages/swapper/src/swappers/BebopSwapper/types.ts b/packages/swapper/src/swappers/BebopSwapper/types.ts index e9851c9a837..d7d5a35e5cd 100644 --- a/packages/swapper/src/swappers/BebopSwapper/types.ts +++ b/packages/swapper/src/swappers/BebopSwapper/types.ts @@ -1,7 +1,7 @@ import { KnownChainIds } from '@shapeshiftoss/types' import type { Address, Hex } from 'viem' -export const bebopSupportedChainIds = [ +export const bebopSupportedEvmChainIds = [ KnownChainIds.EthereumMainnet, KnownChainIds.PolygonMainnet, KnownChainIds.ArbitrumMainnet, @@ -11,6 +11,11 @@ export const bebopSupportedChainIds = [ KnownChainIds.BnbSmartChainMainnet, ] as const +export const bebopSupportedChainIds = [ + ...bebopSupportedEvmChainIds, + KnownChainIds.SolanaMainnet, +] as const + export type BebopSupportedChainId = (typeof bebopSupportedChainIds)[number] export type BebopQuoteRoute = { @@ -113,6 +118,7 @@ export const chainIdToBebopChain: Record = { [KnownChainIds.AvalancheMainnet]: 'avalanche', [KnownChainIds.OptimismMainnet]: 'optimism', [KnownChainIds.BnbSmartChainMainnet]: 'bsc', + [KnownChainIds.SolanaMainnet]: 'solana', } export const BEBOP_NATIVE_MARKER = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' @@ -121,3 +127,59 @@ export const BEBOP_NATIVE_MARKER = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' // This is Vitalik's address, same as what Bebop's own UI uses for price-only quotes. // MUST NEVER be used for executable quote transactions. export const BEBOP_DUMMY_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' as Address + +// Solana dummy address for rate quotes - uses the Token Program address which cannot be a real wallet +export const BEBOP_SOLANA_DUMMY_ADDRESS = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' + +export type BebopSolanaQuoteResponse = { + requestId: string + type: string + status: 'QUOTE_SUCCESS' | 'QUOTE_INDIC_ROUTE' + quoteId: string + chainId: number + approvalType: string + nativeToken: string + taker: string + receiver: string + expiry: number + slippage: number + gasFee: { + native: string + usd: number + } + buyTokens: Record< + string, + { + amount: string + decimals: number + priceUsd: number + symbol: string + minimumAmount: string + price: number + priceBeforeFee: number + amountBeforeFee: string + deltaFromExpected: number + } + > + sellTokens: Record< + string, + { + amount: string + decimals: number + priceUsd: number + symbol: string + price: number + priceBeforeFee: number + } + > + settlementAddress: string + approvalTarget: string + requiredSignatures: string[] + priceImpact: number + warnings: string[] + solana_tx: string + blockhash: string + makers: string[] + partnerFee?: Record + protocolFee?: Record +} diff --git a/packages/swapper/src/swappers/BebopSwapper/utils/fetchFromBebop.ts b/packages/swapper/src/swappers/BebopSwapper/utils/fetchFromBebop.ts index ba81eb7dcb2..51ee80da830 100644 --- a/packages/swapper/src/swappers/BebopSwapper/utils/fetchFromBebop.ts +++ b/packages/swapper/src/swappers/BebopSwapper/utils/fetchFromBebop.ts @@ -8,10 +8,10 @@ import { getAddress } from 'viem' import type { SwapErrorRight } from '../../../types' import { TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' -import type { BebopQuoteResponse, BebopSupportedChainId } from '../types' -import { BEBOP_DUMMY_ADDRESS, chainIdToBebopChain } from '../types' +import type { BebopQuoteResponse, BebopSolanaQuoteResponse, BebopSupportedChainId } from '../types' +import { BEBOP_DUMMY_ADDRESS, BEBOP_SOLANA_DUMMY_ADDRESS, chainIdToBebopChain } from '../types' import { bebopServiceFactory } from './bebopService' -import { assetIdToBebopToken } from './helpers/helpers' +import { assetIdToBebopSolanaToken, assetIdToBebopToken } from './helpers/helpers' export const fetchBebopQuote = async ({ buyAsset, @@ -143,3 +143,123 @@ export const fetchBebopPrice = ({ apiKey, }) } + +export const fetchBebopSolanaQuote = async ({ + buyAsset, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + takerAddress, + receiverAddress, + slippageTolerancePercentageDecimal, + affiliateBps, + apiKey, +}: { + buyAsset: Asset + sellAsset: Asset + sellAmountIncludingProtocolFeesCryptoBaseUnit: string + takerAddress: string + receiverAddress: string + slippageTolerancePercentageDecimal: string + affiliateBps?: string + apiKey: string +}): Promise> => { + try { + const sellToken = assetIdToBebopSolanaToken(sellAsset.assetId) + const buyToken = assetIdToBebopSolanaToken(buyAsset.assetId) + const sellAmountFormatted = bn(sellAmountIncludingProtocolFeesCryptoBaseUnit).toFixed(0) + const slippagePercentage = bn(slippageTolerancePercentageDecimal ?? 0.003) + .times(100) + .toNumber() + + const url = 'https://api.bebop.xyz/pmm/solana/v3/quote' + + const params = new URLSearchParams({ + sell_tokens: sellToken, + buy_tokens: buyToken, + sell_amounts: sellAmountFormatted, + taker_address: takerAddress, + receiver_address: receiverAddress, + slippage: slippagePercentage.toString(), + approval_type: 'Standard', + skip_validation: 'false', + gasless: 'true', + source: 'shapeshift', + }) + + if (affiliateBps && affiliateBps !== '0') { + params.set('fee', affiliateBps) + } + + const bebopService = bebopServiceFactory({ apiKey }) + const maybeResponse = await bebopService.get(`${url}?${params}`) + + if (maybeResponse.isErr()) { + return Err( + makeSwapErrorRight({ + message: 'Failed to fetch quote from Bebop Solana', + cause: maybeResponse.unwrapErr().cause, + code: TradeQuoteError.QueryFailed, + }), + ) + } + + const response = maybeResponse.unwrap() + + if (response.data.status !== 'QUOTE_SUCCESS') { + return Err( + makeSwapErrorRight({ + message: `Bebop Solana quote not executable: ${response.data.status}`, + code: TradeQuoteError.InvalidResponse, + }), + ) + } + + if (!response.data.solana_tx) { + return Err( + makeSwapErrorRight({ + message: 'Missing solana_tx in response', + code: TradeQuoteError.InvalidResponse, + }), + ) + } + + return Ok(response.data) + } catch (error) { + return Err( + makeSwapErrorRight({ + message: 'Unexpected error fetching Bebop Solana quote', + cause: error, + code: TradeQuoteError.QueryFailed, + }), + ) + } +} + +export const fetchBebopSolanaPrice = ({ + buyAsset, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + receiveAddress, + affiliateBps, + apiKey, +}: { + buyAsset: Asset + sellAsset: Asset + sellAmountIncludingProtocolFeesCryptoBaseUnit: string + receiveAddress: string | undefined + affiliateBps?: string + apiKey: string +}): Promise> => { + const address = receiveAddress || BEBOP_SOLANA_DUMMY_ADDRESS + + return fetchBebopSolanaQuote({ + buyAsset, + sellAsset, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + takerAddress: address, + receiverAddress: address, + slippageTolerancePercentageDecimal: '0.01', + affiliateBps, + apiKey, + }) +} diff --git a/packages/swapper/src/swappers/BebopSwapper/utils/helpers/helpers.ts b/packages/swapper/src/swappers/BebopSwapper/utils/helpers/helpers.ts index d9d158dcdd2..20b274c6a68 100644 --- a/packages/swapper/src/swappers/BebopSwapper/utils/helpers/helpers.ts +++ b/packages/swapper/src/swappers/BebopSwapper/utils/helpers/helpers.ts @@ -1,5 +1,5 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' -import { fromAssetId } from '@shapeshiftoss/caip' +import { fromAssetId, solanaChainId } from '@shapeshiftoss/caip' import type { Asset } from '@shapeshiftoss/types' import { bn, convertPrecision, isToken } from '@shapeshiftoss/utils' import { Err, Ok } from '@sniptt/monads' @@ -10,12 +10,24 @@ import { makeSwapErrorRight } from '../../../../utils' import type { BebopSupportedChainId } from '../../types' import { BEBOP_NATIVE_MARKER, bebopSupportedChainIds } from '../../types' +export const isSolanaChainId = (chainId: ChainId): boolean => { + return chainId === solanaChainId +} + export const assetIdToBebopToken = (assetId: AssetId): string => { if (!isToken(assetId)) return BEBOP_NATIVE_MARKER const { assetReference } = fromAssetId(assetId) return getAddress(assetReference) } +export const assetIdToBebopSolanaToken = (assetId: AssetId): string => { + const { assetReference, chainId } = fromAssetId(assetId) + if (chainId === solanaChainId && !isToken(assetId)) { + return 'So11111111111111111111111111111111111111112' + } + return assetReference +} + export const isSupportedChainId = (chainId: ChainId): chainId is BebopSupportedChainId => { return bebopSupportedChainIds.includes(chainId as BebopSupportedChainId) } diff --git a/packages/swapper/src/types.ts b/packages/swapper/src/types.ts index 4d51a2f7024..f8a60c75509 100644 --- a/packages/swapper/src/types.ts +++ b/packages/swapper/src/types.ts @@ -429,6 +429,8 @@ export type TradeQuoteStep = { value: Hex gas?: string } + bebopSolanaSerializedTx?: string + bebopQuoteId?: string jupiterQuoteResponse?: QuoteResponse solanaTransactionMetadata?: { addressLookupTableAddresses: string[] @@ -681,6 +683,15 @@ export type SolanaTransactionExecutionProps = { signAndBroadcastTransaction: (txToSign: SolanaSignTx) => Promise } +export type SolanaMessageToSign = { + serializedTx: string + quoteId: string +} + +export type SolanaMessageExecutionProps = { + signSerializedTransaction: (serializedTx: string) => Promise +} + export type TronTransactionExecutionProps = { signAndBroadcastTransaction: (txToSign: tron.TronSignTx) => Promise } @@ -752,6 +763,7 @@ export type GetUnsignedTonTransactionArgs = CommonGetUnsignedTransactionArgs & export type GetUnsignedEvmMessageArgs = CommonGetUnsignedTransactionArgs & EvmAccountMetadata & Omit +export type GetUnsignedSolanaMessageArgs = CommonGetUnsignedTransactionArgs export type GetUnsignedUtxoTransactionArgs = CommonGetUnsignedTransactionArgs & UtxoAccountMetadata & UtxoSwapperDeps @@ -830,6 +842,11 @@ export type Swapper = { txToSign: SolanaSignTx, callbacks: SolanaTransactionExecutionProps, ) => Promise + executeSolanaMessage?: ( + messageData: SolanaMessageToSign, + callbacks: SolanaMessageExecutionProps, + config: SwapperConfig, + ) => Promise executeTronTransaction?: ( txToSign: tron.TronSignTx, callbacks: TronTransactionExecutionProps, @@ -868,6 +885,7 @@ export type SwapperApi = { input: GetUnsignedCosmosSdkTransactionArgs, ) => Promise> getUnsignedSolanaTransaction?: (input: GetUnsignedSolanaTransactionArgs) => Promise + getUnsignedSolanaMessage?: (input: GetUnsignedSolanaMessageArgs) => Promise getUnsignedTronTransaction?: (input: GetUnsignedTronTransactionArgs) => Promise getUnsignedSuiTransaction?: (input: GetUnsignedSuiTransactionArgs) => Promise getUnsignedNearTransaction?: (input: GetUnsignedNearTransactionArgs) => Promise @@ -926,6 +944,8 @@ export type SolanaTransactionExecutionInput = CommonTradeExecutionInput & SolanaTransactionExecutionProps & SolanaAccountMetadata +export type SolanaMessageExecutionInput = CommonTradeExecutionInput & SolanaMessageExecutionProps + export type TronTransactionExecutionInput = CommonTradeExecutionInput & TronTransactionExecutionProps & TronAccountMetadata diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx index 3ae71906d05..460b7e56137 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx @@ -7,7 +7,7 @@ import type { StarknetSignTx, SuiSignTx, } from '@shapeshiftoss/hdwallet-core' -import { isGridPlus, supportsETH } from '@shapeshiftoss/hdwallet-core/wallet' +import { isGridPlus, supportsETH, supportsSolana } from '@shapeshiftoss/hdwallet-core/wallet' import { isTrezor } from '@shapeshiftoss/hdwallet-trezor' import type { SupportedTradeQuoteStepIndex, TradeQuote } from '@shapeshiftoss/swapper' import { @@ -423,6 +423,35 @@ export const useTradeExecution = ( return } + if (swapperName === SwapperName.Bebop && hop?.bebopSolanaSerializedTx && hop?.bebopQuoteId) { + const output = await execution.execSolanaMessage({ + swapperName, + tradeQuote, + stepIndex: hopIndex, + slippageTolerancePercentageDecimal, + signSerializedTransaction: async (serializedTx: string) => { + if (!wallet || !supportsSolana(wallet) || !wallet.solanaSignSerializedTx) { + throw new Error('Wallet does not support signing serialized Solana transactions') + } + + const result = await wallet.solanaSignSerializedTx({ + addressNList: toAddressNList(accountMetadata.bip44Params), + serializedTx, + }) + + if (!result?.signatures) { + throw new Error('Failed to sign Bebop Solana transaction') + } + + trackMixpanelEventOnExecute() + return result.signatures + }, + }) + + cancelPollingRef.current = output?.cancelPolling + return + } + const { chainNamespace: stepSellAssetChainNamespace } = fromChainId(stepSellAssetChainId) const receiverAddress = diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeNetworkFeeCryptoBaseUnit.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeNetworkFeeCryptoBaseUnit.tsx index c3f885c97b3..7bac2752074 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeNetworkFeeCryptoBaseUnit.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeNetworkFeeCryptoBaseUnit.tsx @@ -92,6 +92,15 @@ export const useTradeNetworkFeeCryptoBaseUnit = ({ return '0' } + if ( + swapperName === SwapperName.Bebop && + hop.bebopSolanaSerializedTx && + hop.bebopQuoteId + ) { + // Bebop Solana is gasless - Bebop pays the fees + return '0' + } + const { chainNamespace: stepSellAssetChainNamespace } = fromChainId(stepSellAssetChainId) diff --git a/src/lib/tradeExecution.ts b/src/lib/tradeExecution.ts index c8cd267423c..9d3149d0519 100644 --- a/src/lib/tradeExecution.ts +++ b/src/lib/tradeExecution.ts @@ -8,6 +8,7 @@ import type { NearTransactionExecutionInput, RelayerTxDetailsArgs, SellTxHashArgs, + SolanaMessageExecutionInput, SolanaTransactionExecutionInput, StarknetTransactionExecutionInput, StatusArgs, @@ -569,6 +570,56 @@ export class TradeExecution { ) } + async execSolanaMessage({ + swapperName, + tradeQuote, + stepIndex, + slippageTolerancePercentageDecimal, + signSerializedTransaction, + }: SolanaMessageExecutionInput) { + const buildSignBroadcast = async ( + swapper: Swapper & SwapperApi, + { + tradeQuote, + chainId, + stepIndex, + slippageTolerancePercentageDecimal, + config, + }: CommonGetUnsignedTransactionArgs, + ) => { + if (!swapper.getUnsignedSolanaMessage) { + throw Error('missing implementation for getUnsignedSolanaMessage') + } + if (!swapper.executeSolanaMessage) { + throw Error('missing implementation for executeSolanaMessage') + } + + const unsignedMessageResult = await swapper.getUnsignedSolanaMessage({ + tradeQuote, + chainId, + stepIndex, + slippageTolerancePercentageDecimal, + config, + }) + + return await swapper.executeSolanaMessage( + unsignedMessageResult, + { signSerializedTransaction }, + config, + ) + } + + return await this._execWalletAgnostic( + { + swapperName, + tradeQuote, + stepIndex, + slippageTolerancePercentageDecimal, + }, + buildSignBroadcast, + ) + } + async execTronTransaction({ swapperName, tradeQuote, From 548199388c30a79e092bf88f37848f98feb03105 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:58:49 +0100 Subject: [PATCH 15/15] chore: remove unused @chainflip/rpc and @chainflip/extrinsics deps (#12109) --- package.json | 2 - pnpm-lock.yaml | 1845 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 1494 insertions(+), 353 deletions(-) diff --git a/package.json b/package.json index 35acbbda215..94be37bdd56 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@arbitrum/sdk": "^4.0.1", "@cetusprotocol/aggregator-sdk": "^1.4.2", "@cetusprotocol/cetus-sui-clmm-sdk": "5.4.0", - "@chainflip/rpc": "2.1.1", "@chakra-ui/icons": "^2.2.4", "@chakra-ui/react": "^2.10.7", "@chakra-ui/system": "^2.6.2", @@ -245,7 +244,6 @@ "xstate": "5.28.0" }, "devDependencies": { - "@chainflip/extrinsics": "1.6.2", "@commitlint/cli": "^15.0.0", "@commitlint/config-conventional": "^15.0.0", "@keplr-wallet/types": "^0.12.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e27b46db32..5636260fb6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,16 +83,13 @@ importers: version: 1.2.11(zod@3.25.76) '@arbitrum/sdk': specifier: ^4.0.1 - version: 4.0.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.0.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@cetusprotocol/aggregator-sdk': specifier: ^1.4.2 version: 1.4.5(axios@1.13.6)(typescript@5.2.2) '@cetusprotocol/cetus-sui-clmm-sdk': specifier: 5.4.0 version: 5.4.0(@mysten/bcs@1.9.2)(@mysten/sui@1.45.2(typescript@5.2.2)) - '@chainflip/rpc': - specifier: 2.1.1 - version: 2.1.1 '@chakra-ui/icons': specifier: ^2.2.4 version: 2.2.4(@chakra-ui/react@2.10.7(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.2)(react@19.2.4))(@types/react@19.1.2)(react@19.2.4))(@types/react@19.1.2)(framer-motion@12.7.4(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) @@ -128,7 +125,7 @@ importers: version: 1.7.6 '@keepkey/hdwallet-keepkey-rest': specifier: 1.40.42 - version: 1.40.42(@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 1.40.42(@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@keepkey/keepkey-sdk': specifier: 0.2.57 version: 0.2.57 @@ -140,7 +137,7 @@ importers: version: 1.3.4(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4)) '@metaplex-foundation/js': specifier: ^0.20.1 - version: 0.20.1(arweave@1.15.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + version: 0.20.1(arweave@1.15.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@moralisweb3/common-evm-utils': specifier: 2.27.2 version: 2.27.2 @@ -167,7 +164,7 @@ importers: version: 2.6.1(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4) '@reown/walletkit': specifier: ^1.2.6 - version: 1.5.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 1.5.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@sentry-internal/browser-utils': specifier: 8.26.0 version: 8.26.0 @@ -257,10 +254,10 @@ importers: version: 0.5.10 '@solana/pay': specifier: ^0.2.6 - version: 0.2.6(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + version: 0.2.6(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@solana/web3.js': specifier: 1.98.0 - version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@tanstack/pacer': specifier: ^0.9.0 version: 0.9.1 @@ -290,7 +287,7 @@ importers: version: 3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@walletconnect/core': specifier: ^2.20.2 - version: 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/utils': specifier: ^2.20.2 version: 2.23.7(typescript@5.2.2)(zod@3.25.76) @@ -302,7 +299,7 @@ importers: version: 6.0.105(zod@3.25.76) alchemy-sdk: specifier: ^3.4.1 - version: 3.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 3.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) axios: specifier: ^1.13.5 version: 1.13.6(debug@4.4.3) @@ -368,7 +365,7 @@ importers: version: 1.0.4 ethers: specifier: 6.11.1 - version: 6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 6.11.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) eventemitter2: specifier: 5.0.1 version: 5.0.1 @@ -545,7 +542,7 @@ importers: version: 6.3.11(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tronweb: specifier: 6.1.0 - version: 6.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 6.1.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) use-long-press: specifier: ^3.3.0 version: 3.3.0(react@19.2.4) @@ -557,10 +554,10 @@ importers: version: 1.1.2(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) wagmi: specifier: ^2.9.2 - version: 2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) web-vitals: specifier: ^2.1.4 version: 2.1.4 @@ -571,9 +568,6 @@ importers: specifier: 5.28.0 version: 5.28.0 devDependencies: - '@chainflip/extrinsics': - specifier: 1.6.2 - version: 1.6.2 '@commitlint/cli': specifier: ^15.0.0 version: 15.0.0 @@ -675,13 +669,13 @@ importers: version: 5.1.4(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) '@walletconnect/ethereum-provider': specifier: ^2.20.2 - version: 2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': specifier: ^2.20.2 version: 2.23.7 '@walletconnect/web3-provider': specifier: ^1.8.0 - version: 1.8.0(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 1.8.0(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@6.0.6) assert: specifier: ^2.0.0 version: 2.1.0 @@ -735,7 +729,7 @@ importers: version: 2.1.0 happy-dom: specifier: ^20.0.2 - version: 20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) http-proxy-middleware: specifier: ^2.0.9 version: 2.0.9(@types/express@4.17.25) @@ -807,7 +801,7 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) vitest: specifier: 3.0.9 - version: 3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/caip: dependencies: @@ -852,10 +846,10 @@ importers: version: link:../utils '@solana/spl-token': specifier: ^0.4.9 - version: 0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + version: 0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) '@solana/web3.js': specifier: 1.98.0 - version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@ton/core': specifier: ^0.62.1 version: 0.62.1(@ton/crypto@3.3.0) @@ -879,7 +873,7 @@ importers: version: 3.1.13 ethers: specifier: 6.11.1 - version: 6.11.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + version: 6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) lodash: specifier: ^4.17.23 version: 4.17.23 @@ -894,7 +888,7 @@ importers: version: 6.6.2 viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: '@types/bs58check': specifier: ^2.1.0 @@ -1105,7 +1099,7 @@ importers: devDependencies: vitest: specifier: 3.0.9 - version: 3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/hdwallet-keepkey: dependencies: @@ -1309,7 +1303,7 @@ importers: version: 4.2.0 '@ledgerhq/device-core': specifier: 0.6.9 - version: 0.6.9(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + version: 0.6.9(react-redux@9.2.0(@types/react@19.1.2)(react@18.3.1)(redux@5.0.1))(react@18.3.1) '@ledgerhq/hw-app-btc': specifier: 10.13.0 version: 10.13.0 @@ -1318,7 +1312,7 @@ importers: version: 6.32.9 '@ledgerhq/hw-app-eth': specifier: 7.0.0 - version: 7.0.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + version: 7.0.0(react-redux@9.2.0(@types/react@19.1.2)(react@18.3.1)(redux@5.0.1))(react@18.3.1) '@ledgerhq/hw-app-near': specifier: 6.31.10 version: 6.31.10 @@ -1391,7 +1385,7 @@ importers: version: 10.13.0 '@ledgerhq/hw-app-eth': specifier: 7.0.0 - version: 7.0.0(react-redux@9.2.0(@types/react@19.1.2)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + version: 7.0.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4) '@ledgerhq/hw-transport': specifier: 6.31.13 version: 6.31.13 @@ -1762,10 +1756,10 @@ importers: version: 3.5.34 vite: specifier: 6.4.1 - version: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) vite-plugin-node-polyfills: specifier: ^0.23.0 - version: 0.23.0(rollup@4.59.0)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 0.23.0(rollup@4.59.0)(vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) packages/hdwallet-seeker: dependencies: @@ -2021,7 +2015,7 @@ importers: version: 19.1.2(@types/react@19.1.2) '@vitejs/plugin-react': specifier: ^4.2.0 - version: 4.7.0(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0)) + version: 4.7.0(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) jsdom: specifier: 28.0.0 version: 28.0.0(@noble/hashes@2.0.1) @@ -2036,13 +2030,13 @@ importers: version: 5.2.2 vite: specifier: ^5.0.0 - version: 5.4.21(@types/node@22.19.13)(terser@5.46.0) + version: 5.4.21(@types/node@25.3.5)(terser@5.46.0) vite-plugin-node-polyfills: specifier: 0.23.0 - version: 0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0)) + version: 0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) vitest: specifier: 4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/swapper: dependencies: @@ -2105,7 +2099,7 @@ importers: version: 5.9.0 '@uniswap/v3-sdk': specifier: ^3.13.1 - version: 3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + version: 3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) axios: specifier: ^1.13.5 version: 1.13.6(debug@4.4.3) @@ -3044,15 +3038,6 @@ packages: '@mysten/bcs': '>=0.8.1' '@mysten/sui': 1.45.2 - '@chainflip/extrinsics@1.6.2': - resolution: {integrity: sha512-CseddihWRvjnuNLM94+EXqqNVexdJVEGj218tibgWqw0CnGmo9cJyHbln6sqgQPlSl4/LqBbeBR9XniJekKwZQ==} - - '@chainflip/rpc@2.1.1': - resolution: {integrity: sha512-YS5i+hrZHbIDFEqpn5Yl/msdChihUjXcdsNj51hzGqVAfoQ+uWBCsblp0uLBKdBT3iOVMAmvfz/cPtg/ozuMVw==} - - '@chainflip/utils@2.1.2': - resolution: {integrity: sha512-ujHjiVRdm5AbN5XotoIlVtN7RMmkR7PnrMW/pGhWg+2Yr6vtKyKas/GSRHrX4VGnm7H1j7xKcSuj9GNag60n5A==} - '@chakra-ui/anatomy@2.2.2': resolution: {integrity: sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==} @@ -3380,9 +3365,6 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@date-fns/utc@2.1.1': - resolution: {integrity: sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==} - '@defuse-protocol/one-click-sdk-typescript@0.1.16': resolution: {integrity: sha512-uro+FN+6S/8GMNfd+Xbvhk3OgbNM2au57Zy8+aJmco0BhAB9irzQGeyf/qX9yIY0e+msgMvS3pP/tmm16I9IVw==} engines: {node: '>=16', pnpm: '>=8'} @@ -7878,6 +7860,9 @@ packages: '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + '@types/node@25.3.5': + resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -12164,8 +12149,8 @@ packages: immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@4.3.8: + resolution: {integrity: sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -15532,8 +15517,8 @@ packages: tendermint-tx-builder@1.0.16: resolution: {integrity: sha512-QooHyd5BeyoqruneSzmDUUpyD1/U877mEDMLPRk/e2SvobZuALJWijQReny0hqTkukseQpIZNKMD4ypMa1pCaQ==} - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} + terser-webpack-plugin@5.3.17: + resolution: {integrity: sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -16000,6 +15985,9 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.22.0: resolution: {integrity: sha512-RKZvifiL60xdsIuC80UY0dq8Z7DbJUV8/l2hOVbyZAxBzEeQU4Z58+4ZzJ6WN2Lidi9KzT5EbiGX+PI/UGYuRw==} @@ -17269,6 +17257,17 @@ snapshots: - bufferutil - utf-8-validate + '@arbitrum/sdk@4.0.4(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + async-mutex: 0.4.1 + ethers: 5.7.2(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@asamuzakjp/css-color@4.1.2': dependencies: '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) @@ -17324,7 +17323,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -17384,7 +17383,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -18167,7 +18166,7 @@ snapshots: '@babel/parser': 7.29.0 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -18199,6 +18198,31 @@ snapshots: - use-sync-external-store - utf-8-validate - zod + optional: true + + '@base-org/account@2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.44.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.2.2)(zod@3.25.76) + preact: 10.24.2 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod '@bitcoinerlab/secp256k1@1.2.0': dependencies: @@ -18262,20 +18286,6 @@ snapshots: - debug - encoding - '@chainflip/extrinsics@1.6.2': {} - - '@chainflip/rpc@2.1.1': - dependencies: - '@chainflip/utils': 2.1.2 - zod: 3.25.76 - - '@chainflip/utils@2.1.2': - dependencies: - '@date-fns/utc': 2.1.1 - '@noble/hashes': 2.0.1 - bignumber.js: 9.3.1 - date-fns: 4.1.0 - '@chakra-ui/anatomy@2.2.2': {} '@chakra-ui/anatomy@2.3.6': {} @@ -18458,6 +18468,29 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate + optional: true + + '@coinbase/cdp-sdk@1.44.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + abitype: 1.0.6(typescript@5.2.2)(zod@3.25.76) + axios: 1.13.6(debug@4.4.3) + axios-retry: 4.5.0(axios@1.13.6) + jose: 6.1.3 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate '@coinbase/wallet-sdk@3.9.3': dependencies: @@ -18492,6 +18525,27 @@ snapshots: - use-sync-external-store - utf-8-validate - zod + optional: true + + '@coinbase/wallet-sdk@4.3.6(@types/react@19.1.2)(bufferutil@4.1.0)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.2.2)(zod@3.25.76) + preact: 10.24.2 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod '@commitlint/cli@15.0.0': dependencies: @@ -18748,6 +18802,16 @@ snapshots: - bufferutil - utf-8-validate + '@cosmjs/socket@0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@cosmjs/stream': 0.29.5 + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@cosmjs/stargate@0.28.13(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@confio/ics23': 0.6.8 @@ -18786,6 +18850,25 @@ snapshots: - debug - utf-8-validate + '@cosmjs/stargate@0.29.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.29.5 + '@cosmjs/encoding': 0.29.5 + '@cosmjs/math': 0.29.5 + '@cosmjs/proto-signing': 0.29.5 + '@cosmjs/stream': 0.29.5 + '@cosmjs/tendermint-rpc': 0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@cosmjs/utils': 0.29.5 + cosmjs-types: 0.5.2 + long: 4.0.0 + protobufjs: 6.11.4 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/stargate@0.29.5(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@confio/ics23': 0.6.8 @@ -18805,6 +18888,25 @@ snapshots: - debug - utf-8-validate + '@cosmjs/stargate@0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.29.5 + '@cosmjs/encoding': 0.29.5 + '@cosmjs/math': 0.29.5 + '@cosmjs/proto-signing': 0.29.5 + '@cosmjs/stream': 0.29.5 + '@cosmjs/tendermint-rpc': 0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@cosmjs/utils': 0.29.5 + cosmjs-types: 0.5.2 + long: 4.0.0 + protobufjs: 6.11.4 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/stream@0.28.13': dependencies: xstream: 11.14.0 @@ -18847,6 +18949,23 @@ snapshots: - debug - utf-8-validate + '@cosmjs/tendermint-rpc@0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@cosmjs/crypto': 0.29.5 + '@cosmjs/encoding': 0.29.5 + '@cosmjs/json-rpc': 0.29.5 + '@cosmjs/math': 0.29.5 + '@cosmjs/socket': 0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@cosmjs/stream': 0.29.5 + '@cosmjs/utils': 0.29.5 + axios: 0.21.4 + readonly-date: 1.0.0 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/utils@0.28.13': {} '@cosmjs/utils@0.29.5': {} @@ -18904,8 +19023,6 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@date-fns/utc@2.1.1': {} - '@defuse-protocol/one-click-sdk-typescript@0.1.16': dependencies: axios: 1.13.6(debug@4.4.3) @@ -19291,7 +19408,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.14.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -19682,6 +19799,32 @@ snapshots: - bufferutil - utf-8-validate + '@ethersproject/providers@5.7.2(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + bech32: 1.1.4 + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abstract-provider': 5.8.0 @@ -19708,6 +19851,32 @@ snapshots: - bufferutil - utf-8-validate + '@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@ethersproject/abstract-provider': 5.8.0 + '@ethersproject/abstract-signer': 5.8.0 + '@ethersproject/address': 5.8.0 + '@ethersproject/base64': 5.8.0 + '@ethersproject/basex': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/constants': 5.8.0 + '@ethersproject/hash': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/networks': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/random': 5.8.0 + '@ethersproject/rlp': 5.8.0 + '@ethersproject/sha2': 5.8.0 + '@ethersproject/strings': 5.8.0 + '@ethersproject/transactions': 5.8.0 + '@ethersproject/web': 5.8.0 + bech32: 1.1.4 + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@ethersproject/random@5.7.0': dependencies: '@ethersproject/bytes': 5.7.0 @@ -20045,11 +20214,11 @@ snapshots: - supports-color optional: true - '@gemini-wallet/core@0.3.2(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@gemini-wallet/core@0.3.2(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -20073,7 +20242,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -20203,22 +20372,22 @@ snapshots: transitivePeerDependencies: - debug - '@irys/sdk@0.0.2(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@5.0.10)': + '@irys/sdk@0.0.2(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@6.0.6)': dependencies: '@ethersproject/bignumber': 5.8.0 '@ethersproject/contracts': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@ethersproject/wallet': 5.8.0 '@irys/query': 0.0.1(debug@4.4.3) '@near-js/crypto': 0.0.3 '@near-js/keystores-browser': 0.0.3 '@near-js/providers': 0.0.4 '@near-js/transactions': 0.1.1 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@supercharge/promise-pool': 3.2.0 algosdk: 1.24.1 aptos: 1.8.5(debug@4.4.3) - arbundles: 0.10.1(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@5.0.10) + arbundles: 0.10.1(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@6.0.6) async-retry: 1.3.3 axios: 1.13.6(debug@4.4.3) base64url: 3.0.1 @@ -20282,9 +20451,9 @@ snapshots: google-protobuf: 3.21.4 pbjs: 0.0.5 - '@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: - '@keepkey/proto-tx-builder': 0.9.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@keepkey/proto-tx-builder': 0.9.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) eip-712: 1.0.0 eventemitter2: 5.0.1 lodash: 4.17.23 @@ -20295,10 +20464,10 @@ snapshots: - debug - utf-8-validate - '@keepkey/hdwallet-keepkey-rest@1.40.42(@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@keepkey/hdwallet-keepkey-rest@1.40.42(@keepkey/hdwallet-core@1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: - '@keepkey/hdwallet-core': 1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@keepkey/hdwallet-keepkey': 1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@keepkey/hdwallet-core': 1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@keepkey/hdwallet-keepkey': 1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@keepkey/keepkey-sdk': 0.2.57 lodash: 4.17.23 semver: 6.3.1 @@ -20309,17 +20478,17 @@ snapshots: - supports-color - utf-8-validate - '@keepkey/hdwallet-keepkey@1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@keepkey/hdwallet-keepkey@1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@ethereumjs/common': 2.6.5 '@ethereumjs/tx': 3.5.2 '@keepkey/device-protocol': 7.13.4 - '@keepkey/hdwallet-core': 1.53.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@keepkey/proto-tx-builder': 0.9.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@keepkey/hdwallet-core': 1.53.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@keepkey/proto-tx-builder': 0.9.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metamask/eth-sig-util': 7.0.3 '@shapeshiftoss/bitcoinjs-lib': 5.2.0-shapeshift.2(patch_hash=e4f2073629f9722c1676758983b96101da73e2bc33971821a2d19319c0ab8ee6) bignumber.js: 9.3.1 - bnb-javascript-sdk-nobroadcast: 2.16.15(bufferutil@4.1.0)(utf-8-validate@5.0.10) + bnb-javascript-sdk-nobroadcast: 2.16.15(bufferutil@4.1.0)(utf-8-validate@6.0.6) crypto-js: 4.2.0 eip55: 2.1.1 google-protobuf: 3.21.4 @@ -20337,17 +20506,17 @@ snapshots: '@keepkey/keepkey-sdk@0.2.57': {} - '@keepkey/proto-tx-builder@0.9.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@keepkey/proto-tx-builder@0.9.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@cosmjs/amino': 0.29.5 '@cosmjs/crypto': 0.29.4 '@cosmjs/encoding': 0.29.5 '@cosmjs/proto-signing': 0.29.5 - '@cosmjs/stargate': 0.29.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@cosmjs/stargate': 0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 cosmjs-types: 0.5.2 google-protobuf: 3.21.4 - osmojs: 0.37.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + osmojs: 0.37.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil - debug @@ -20405,7 +20574,7 @@ snapshots: '@kwsites/file-exists@1.1.1': dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -20438,14 +20607,14 @@ snapshots: - react - react-redux - '@ledgerhq/device-core@0.6.9(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + '@ledgerhq/device-core@0.6.9(react-redux@9.2.0(@types/react@19.1.2)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': dependencies: '@ledgerhq/devices': 8.7.0 '@ledgerhq/errors': 6.29.0 '@ledgerhq/hw-transport': 6.31.13 '@ledgerhq/live-network': 2.2.3 '@ledgerhq/logs': 6.13.0 - '@ledgerhq/types-live': 6.98.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + '@ledgerhq/types-live': 6.98.0(react-redux@9.2.0(@types/react@19.1.2)(react@18.3.1)(redux@5.0.1))(react@18.3.1) '@noble/hashes': 1.8.0 semver: 7.7.4 transitivePeerDependencies: @@ -21007,7 +21176,7 @@ snapshots: dependencies: openapi-fetch: 0.13.8 - '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.17)(eventemitter2@6.4.9)(readable-stream@3.6.0)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.17)(eventemitter2@6.4.9)(readable-stream@3.6.0)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@metamask/sdk-analytics': 0.0.5 bufferutil: 4.1.0 @@ -21017,7 +21186,7 @@ snapshots: eciesjs: 0.4.17 eventemitter2: 6.4.9 readable-stream: 3.6.0 - socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) utf-8-validate: 5.0.10 uuid: 8.3.2 transitivePeerDependencies: @@ -21033,7 +21202,7 @@ snapshots: '@metamask/onboarding': 1.0.1 '@metamask/providers': 16.1.0 '@metamask/sdk-analytics': 0.0.5 - '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.17)(eventemitter2@6.4.9)(readable-stream@3.6.0)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.17)(eventemitter2@6.4.9)(readable-stream@3.6.0)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@metamask/sdk-install-modal-web': 0.32.1 '@paulmillr/qr': 0.2.1 bowser: 2.14.1 @@ -21054,6 +21223,35 @@ snapshots: - encoding - supports-color - utf-8-validate + optional: true + + '@metamask/sdk@0.33.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@babel/runtime': 7.28.6 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-analytics': 0.0.5 + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.17)(eventemitter2@6.4.9)(readable-stream@3.6.0)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@metamask/sdk-install-modal-web': 0.32.1 + '@paulmillr/qr': 0.2.1 + bowser: 2.14.1 + cross-fetch: 4.1.0 + debug: 4.3.4 + eciesjs: 0.4.17 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.4 + readable-stream: 3.6.0 + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate '@metamask/snaps-registry@1.2.2': dependencies: @@ -21118,7 +21316,7 @@ snapshots: '@scure/base': 1.1.9 '@types/debug': 4.1.12 '@types/lodash': 4.14.182 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 lodash: 4.17.23 pony-cause: 2.1.11 semver: 7.7.4 @@ -21130,7 +21328,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 semver: 7.7.4 superstruct: 1.0.4 transitivePeerDependencies: @@ -21165,7 +21363,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 pony-cause: 2.1.11 semver: 7.7.4 uuid: 9.0.1 @@ -21179,43 +21377,43 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.1.9 '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 pony-cause: 2.1.11 semver: 7.7.4 uuid: 9.0.1 transitivePeerDependencies: - supports-color - '@metaplex-foundation/beet-solana@0.3.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@metaplex-foundation/beet-solana@0.3.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@metaplex-foundation/beet-solana@0.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@metaplex-foundation/beet-solana@0.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@metaplex-foundation/beet-solana@0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@metaplex-foundation/beet-solana@0.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - bufferutil - encoding @@ -21226,7 +21424,7 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -21234,7 +21432,7 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -21242,32 +21440,32 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color '@metaplex-foundation/cusper@0.0.2': {} - '@metaplex-foundation/js@0.20.1(arweave@1.15.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@metaplex-foundation/js@0.20.1(arweave@1.15.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: - '@irys/sdk': 0.0.2(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@5.0.10) + '@irys/sdk': 0.0.2(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@6.0.6) '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/mpl-auction-house': 2.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@metaplex-foundation/mpl-bubblegum': 0.6.2(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@metaplex-foundation/mpl-candy-guard': 0.3.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@metaplex-foundation/mpl-candy-machine': 5.1.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@metaplex-foundation/mpl-candy-machine-core': 0.1.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@metaplex-foundation/mpl-auction-house': 2.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@metaplex-foundation/mpl-bubblegum': 0.6.2(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@metaplex-foundation/mpl-candy-guard': 0.3.2(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@metaplex-foundation/mpl-candy-machine': 5.1.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@metaplex-foundation/mpl-candy-machine-core': 0.1.2(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@noble/ed25519': 1.7.5 '@noble/hashes': 1.8.0 - '@solana/spl-account-compression': 0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/spl-account-compression': 0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bignumber.js: 9.3.1 bn.js: 5.2.3 bs58: 5.0.0 buffer: 6.0.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 eventemitter3: 4.0.7 lodash.clonedeep: 4.5.0 lodash.isequal: 4.5.0 @@ -21283,13 +21481,13 @@ snapshots: - typescript - utf-8-validate - '@metaplex-foundation/mpl-auction-house@2.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-auction-house@2.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.6.1 - '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 transitivePeerDependencies: - bufferutil @@ -21299,15 +21497,15 @@ snapshots: - typescript - utf-8-validate - '@metaplex-foundation/mpl-bubblegum@0.6.2(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-bubblegum@0.6.2(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/spl-account-compression': 0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.1.8(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/mpl-token-metadata': 2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/spl-account-compression': 0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/spl-token': 0.1.8(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 js-sha3: 0.8.0 transitivePeerDependencies: @@ -21318,12 +21516,12 @@ snapshots: - typescript - utf-8-validate - '@metaplex-foundation/mpl-candy-guard@0.3.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-candy-guard@0.3.2(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.4.0 - '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 transitivePeerDependencies: - bufferutil @@ -21331,12 +21529,12 @@ snapshots: - supports-color - utf-8-validate - '@metaplex-foundation/mpl-candy-machine-core@0.1.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-candy-machine-core@0.1.2(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.4.0 - '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.3.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 transitivePeerDependencies: - bufferutil @@ -21344,13 +21542,13 @@ snapshots: - supports-color - utf-8-validate - '@metaplex-foundation/mpl-candy-machine@5.1.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-candy-machine@5.1.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil - encoding @@ -21359,15 +21557,15 @@ snapshots: - typescript - utf-8-validate - '@metaplex-foundation/mpl-token-metadata@2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@metaplex-foundation/mpl-token-metadata@2.13.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@metaplex-foundation/cusper': 0.0.2 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - bufferutil - encoding @@ -21577,7 +21775,7 @@ snapshots: dependencies: '@open-draft/until': 1.0.3 '@xmldom/xmldom': 0.7.13 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 headers-utils: 3.0.2 outvariant: 1.4.3 strict-event-emitter: 0.2.8 @@ -22650,11 +22848,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -22672,6 +22870,17 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-common@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + '@reown/appkit-common@1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: big.js: 6.2.2 @@ -22718,13 +22927,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -22788,6 +22997,41 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-controllers@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + '@reown/appkit-controllers@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -22823,12 +23067,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) lit: 3.3.0 valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) transitivePeerDependencies: @@ -22899,6 +23143,46 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@reown/appkit-pay@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -22992,13 +23276,13 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -23071,6 +23355,48 @@ snapshots: - valtio - zod + '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - valtio + - zod + '@reown/appkit-scaffold-ui@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -23148,11 +23474,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -23219,6 +23545,42 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@phosphor-icons/webcomponents': 2.1.5 + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + '@reown/appkit-ui@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@phosphor-icons/webcomponents': 2.1.5 @@ -23293,16 +23655,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23378,6 +23740,53 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@wallet-standard/wallet': 1.1.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + optionalDependencies: + '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@reown/appkit-utils@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -23436,9 +23845,9 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit-wallet@1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 '@walletconnect/logger': 2.1.2 zod: 3.25.76 @@ -23458,6 +23867,17 @@ snapshots: - typescript - utf-8-validate + '@reown/appkit-wallet@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@walletconnect/logger': 3.0.2 + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + '@reown/appkit-wallet@1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -23511,21 +23931,21 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23603,6 +24023,55 @@ snapshots: - utf-8-validate - zod + '@reown/appkit@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@reown/appkit-scaffold-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + bs58: 6.0.0 + semver: 7.7.2 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + optionalDependencies: + '@lit/react': 1.0.8(@types/react@19.1.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@reown/appkit@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -23652,14 +24121,14 @@ snapshots: - utf-8-validate - zod - '@reown/walletkit@1.5.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/walletkit@1.5.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 3.0.2 '@walletconnect/pay': 1.0.5(typescript@5.2.2)(zod@3.25.76) - '@walletconnect/sign-client': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': 2.23.6 '@walletconnect/utils': 2.23.6(typescript@5.2.2)(zod@3.25.76) transitivePeerDependencies: @@ -23796,6 +24265,17 @@ snapshots: - typescript - utf-8-validate - zod + optional: true + + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: @@ -23806,6 +24286,17 @@ snapshots: - typescript - utf-8-validate - zod + optional: true + + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} @@ -23855,7 +24346,7 @@ snapshots: '@scure/bip32@1.1.5': dependencies: '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.2 + '@noble/secp256k1': 1.7.1 '@scure/base': 1.1.9 '@scure/bip32@1.4.0': @@ -23866,8 +24357,8 @@ snapshots: '@scure/bip32@1.6.2': dependencies: - '@noble/curves': 1.8.2 - '@noble/hashes': 1.7.2 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@scure/base': 1.2.6 '@scure/bip32@1.7.0': @@ -23893,7 +24384,7 @@ snapshots: '@scure/bip39@1.5.4': dependencies: - '@noble/hashes': 1.7.2 + '@noble/hashes': 1.7.1 '@scure/base': 1.2.6 '@scure/bip39@1.6.0': @@ -24443,6 +24934,11 @@ snapshots: '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + optional: true + + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@solana-program/system@0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: @@ -24472,6 +24968,11 @@ snapshots: '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + optional: true + + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: @@ -24838,6 +25339,38 @@ snapshots: - bufferutil - fastestsmallesttextencoderdecoder - utf-8-validate + optional: true + + '@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana/accounts': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/codecs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/errors': 5.5.1(typescript@5.2.2) + '@solana/functional': 5.5.1(typescript@5.2.2) + '@solana/instruction-plans': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/instructions': 5.5.1(typescript@5.2.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/offchain-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/plugin-core': 5.5.1(typescript@5.2.2) + '@solana/programs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.2.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.2.2) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/signers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/sysvars': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate '@solana/nominal-types@2.3.0(typescript@5.2.2)': dependencies: @@ -24896,11 +25429,11 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/pay@0.2.6(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@solana/pay@0.2.6(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@solana/qr-code-styling': 1.6.0 - '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) bignumber.js: 9.3.1 cross-fetch: 3.2.0 js-base64: 3.7.8 @@ -25066,6 +25599,20 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.2.2) + '@solana/functional': 5.5.1(typescript@5.2.2) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.2.2) + '@solana/subscribable': 5.5.1(typescript@5.2.2) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.2.2)': dependencies: @@ -25139,6 +25686,27 @@ snapshots: - bufferutil - fastestsmallesttextencoderdecoder - utf-8-validate + optional: true + + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.2.2) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.2.2) + '@solana/functional': 5.5.1(typescript@5.2.2) + '@solana/promises': 5.5.1(typescript@5.2.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.2.2) + '@solana/rpc-subscriptions-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.2.2) + '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/subscribable': 5.5.1(typescript@5.2.2) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: @@ -25266,11 +25834,11 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/spl-account-compression@0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@solana/spl-account-compression@0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 borsh: 0.7.0 js-sha3: 0.8.0 @@ -25281,10 +25849,10 @@ snapshots: - supports-color - utf-8-validate - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript @@ -25297,6 +25865,14 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': + dependencies: + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25321,10 +25897,18 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token@0.1.8(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': + dependencies: + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + + '@solana/spl-token@0.1.8(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@babel/runtime': 7.28.6 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 buffer: 6.0.3 buffer-layout: 1.2.2 @@ -25334,12 +25918,12 @@ snapshots: - encoding - utf-8-validate - '@solana/spl-token@0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -25348,13 +25932,13 @@ snapshots: - typescript - utf-8-validate - '@solana/spl-token@0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + '@solana/spl-token@0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -25378,6 +25962,21 @@ snapshots: - typescript - utf-8-validate + '@solana/spl-token@0.4.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + buffer: 6.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@solana/subscribable@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25462,6 +26061,26 @@ snapshots: - bufferutil - fastestsmallesttextencoderdecoder - utf-8-validate + optional: true + + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/errors': 5.5.1(typescript@5.2.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/promises': 5.5.1(typescript@5.2.2) + '@solana/rpc': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: @@ -25996,6 +26615,29 @@ snapshots: - typescript - utf-8-validate + '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)': + dependencies: + '@babel/runtime': 7.28.6 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@solana/buffer-layout': 4.0.1 + '@solana/codecs-numbers': 2.3.0(typescript@5.2.2) + agentkeepalive: 4.6.0 + bn.js: 5.2.3 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + node-fetch: 2.7.0 + rpc-websockets: 9.3.5 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + '@solflare-wallet/metamask-sdk@1.0.3(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-standard-features': 1.3.0 @@ -27073,6 +27715,10 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/node@25.3.5': + dependencies: + undici-types: 7.18.2 + '@types/normalize-package-data@2.4.4': {} '@types/parse-json@4.0.2': {} @@ -27277,7 +27923,7 @@ snapshots: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.2.2) '@typescript-eslint/visitor-keys': 8.56.1 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 eslint: 8.57.1 typescript: 5.2.2 transitivePeerDependencies: @@ -27287,7 +27933,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.2.2) '@typescript-eslint/types': 8.56.1 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -27311,7 +27957,7 @@ snapshots: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.2.2) '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.2.2) - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 eslint: 8.57.1 ts-api-utils: 2.4.0(typescript@5.2.2) typescript: 5.2.2 @@ -27326,7 +27972,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 semver: 7.7.4 @@ -27342,7 +27988,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.2.2) '@typescript-eslint/types': 8.56.1 '@typescript-eslint/visitor-keys': 8.56.1 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.4 tinyglobby: 0.2.15 @@ -27430,14 +28076,14 @@ snapshots: tiny-warning: 1.0.3 toformat: 2.0.0 - '@uniswap/swap-router-contracts@1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': + '@uniswap/swap-router-contracts@1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: '@openzeppelin/contracts': 3.4.2-solc-0.7 '@uniswap/v2-core': 1.0.1 '@uniswap/v3-core': 1.0.1 '@uniswap/v3-periphery': 1.4.4 dotenv: 14.3.2 - hardhat-watcher: 2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + hardhat-watcher: 2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) transitivePeerDependencies: - hardhat @@ -27455,12 +28101,12 @@ snapshots: '@uniswap/v3-core': 1.0.1 base64-sol: 1.0.1 - '@uniswap/v3-sdk@3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': + '@uniswap/v3-sdk@3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/solidity': 5.8.0 '@uniswap/sdk-core': 7.11.0 - '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) '@uniswap/v3-periphery': 1.4.4 '@uniswap/v3-staker': 1.0.0 tiny-invariant: 1.3.3 @@ -27742,7 +28388,7 @@ snapshots: transitivePeerDependencies: - react-dom - '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0))': + '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -27750,7 +28396,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.21(@types/node@22.19.13)(terser@5.46.0) + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) transitivePeerDependencies: - supports-color @@ -27800,14 +28446,14 @@ snapshots: msw: 0.36.8 vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.0.18(msw@0.36.8)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(msw@0.36.8)(vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 0.36.8 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@3.0.9': dependencies: @@ -27860,19 +28506,19 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': dependencies: - '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.2)(bufferutil@4.1.0)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.2)(bufferutil@4.1.0)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + porto: 0.2.35(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)))(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: @@ -27941,11 +28587,11 @@ snapshots: typescript: 5.2.2 optional: true - '@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.2.2) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) zustand: 5.0.0(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) optionalDependencies: '@tanstack/query-core': 5.69.0 @@ -28010,9 +28656,9 @@ snapshots: '@walletconnect/window-metadata': 1.0.0 detect-browser: 5.2.0 - '@walletconnect/client@1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@walletconnect/client@1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: - '@walletconnect/core': 1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/core': 1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/iso-crypto': 1.8.0 '@walletconnect/types': 1.8.0 '@walletconnect/utils': 1.8.0 @@ -28020,9 +28666,9 @@ snapshots: - bufferutil - utf-8-validate - '@walletconnect/core@1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@walletconnect/core@1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: - '@walletconnect/socket-transport': 1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/socket-transport': 1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/types': 1.8.0 '@walletconnect/utils': 1.8.0 transitivePeerDependencies: @@ -28117,13 +28763,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -28131,7 +28777,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -28161,13 +28807,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 @@ -28175,7 +28821,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -28249,13 +28895,57 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.39.3 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 '@walletconnect/relay-api': 1.0.11 @@ -28337,6 +29027,50 @@ snapshots: - utf-8-validate - zod + '@walletconnect/core@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.44.0 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/crypto@1.1.0': dependencies: '@noble/ciphers': 1.2.0 @@ -28356,18 +29090,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -28443,6 +29177,52 @@ snapshots: - utf-8-validate - zod + '@walletconnect/ethereum-provider@2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.23.7 + '@walletconnect/universal-provider': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@walletconnect/events@1.0.1': dependencies: keyvaluestorage-interface: 1.0.0 @@ -28503,6 +29283,16 @@ snapshots: - bufferutil - utf-8-validate + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@walletconnect/keyvaluestorage@1.1.1': dependencies: '@walletconnect/safe-json': 1.0.2 @@ -28700,16 +29490,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -28736,16 +29526,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -28808,9 +29598,45 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 @@ -28880,11 +29706,47 @@ snapshots: - utf-8-validate - zod - '@walletconnect/socket-transport@1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@walletconnect/sign-client@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/socket-transport@1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@walletconnect/types': 1.8.0 '@walletconnect/utils': 1.8.0 - ws: 7.5.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 7.5.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -29214,7 +30076,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -29223,9 +30085,49 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -29254,7 +30156,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -29262,11 +30164,11 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - es-toolkit: 1.33.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -29294,7 +30196,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -29303,7 +30205,7 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': 2.23.2 '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) es-toolkit: 1.39.3 @@ -29374,6 +30276,46 @@ snapshots: - utf-8-validate - zod + '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + es-toolkit: 1.44.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/utils@1.8.0': dependencies: '@walletconnect/browser-utils': 1.8.0 @@ -29473,7 +30415,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -29491,7 +30433,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -29517,7 +30459,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -29535,7 +30477,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -29694,14 +30636,14 @@ snapshots: - uploadthing - zod - '@walletconnect/web3-provider@1.8.0(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@walletconnect/web3-provider@1.8.0(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: - '@walletconnect/client': 1.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/client': 1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/http-connection': 1.8.0 '@walletconnect/qrcode-modal': 1.8.0 '@walletconnect/types': 1.8.0 '@walletconnect/utils': 1.8.0 - web3-provider-engine: 16.0.1(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + web3-provider-engine: 16.0.1(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -30022,7 +30964,7 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - alchemy-sdk@3.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + alchemy-sdk@3.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/abstract-provider': 5.8.0 @@ -30031,11 +30973,11 @@ snapshots: '@ethersproject/contracts': 5.8.0 '@ethersproject/hash': 5.8.0 '@ethersproject/networks': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@ethersproject/units': 5.8.0 '@ethersproject/wallet': 5.8.0 '@ethersproject/web': 5.8.0 - '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) axios: 1.13.6(debug@4.4.3) sturdy-websocket: 0.2.1 websocket: 1.0.35 @@ -30108,11 +31050,11 @@ snapshots: transitivePeerDependencies: - debug - arbundles@0.10.1(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@5.0.10): + arbundles@0.10.1(arweave@1.15.7)(bufferutil@4.1.0)(debug@4.4.3)(utf-8-validate@6.0.6): dependencies: '@ethersproject/bytes': 5.8.0 '@ethersproject/hash': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@ethersproject/signing-key': 5.8.0 '@ethersproject/transactions': 5.8.0 '@ethersproject/wallet': 5.8.0 @@ -30764,6 +31706,37 @@ snapshots: - react-native-b4a - utf-8-validate + bnb-javascript-sdk-nobroadcast@2.16.15(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + axios: 0.21.1 + bech32: 1.1.4 + big.js: 5.2.2 + bip32: 2.0.6 + bip39: 3.1.0 + bn.js: 4.12.3 + camelcase: 5.3.1 + crypto-browserify: 3.12.1 + crypto-js: 3.3.0 + elliptic: 6.6.1 + eslint-utils: 1.4.3 + events: 3.3.0 + is_js: 0.9.0 + lodash: 4.17.23 + minimist: 1.2.8 + ndjson: 1.5.0 + protocol-buffers-encodings: 1.2.0 + pumpify: 2.0.1 + secure-random: 1.1.2 + tiny-secp256k1: 1.1.7 + url: 0.11.4 + uuid: 3.4.0 + websocket-stream: 5.5.2(bufferutil@4.1.0)(utf-8-validate@6.0.6) + transitivePeerDependencies: + - bufferutil + - debug + - react-native-b4a + - utf-8-validate + body-parser@1.20.4: dependencies: bytes: 3.1.2 @@ -31681,6 +32654,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.4.3: + dependencies: + ms: 2.1.3 + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -32040,6 +33017,18 @@ snapshots: - supports-color - utf-8-validate + engine.io-client@6.6.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + engine.io-parser@5.2.3: {} enhanced-resolve@5.20.0: @@ -32585,7 +33574,7 @@ snapshots: ajv: 6.14.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -32929,6 +33918,42 @@ snapshots: - bufferutil - utf-8-validate + ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/providers': 5.7.2(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/solidity': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/units': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@ethersproject/web': 5.7.1 + '@ethersproject/wordlists': 5.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@adraffy/ens-normalize': 1.10.1 @@ -32955,7 +33980,7 @@ snapshots: - bufferutil - utf-8-validate - ethers@6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + ethers@6.13.5(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: '@adraffy/ens-normalize': 1.10.1 '@noble/curves': 1.2.0 @@ -32963,7 +33988,7 @@ snapshots: '@types/node': 22.7.5 aes-js: 4.0.0-beta.5 tslib: 2.7.0 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -33289,7 +34314,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 for-each@0.3.5: dependencies: @@ -33660,6 +34685,7 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate + optional: true happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: @@ -33672,7 +34698,6 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate - optional: true har-schema@2.0.0: {} @@ -33683,12 +34708,12 @@ snapshots: hard-rejection@2.1.0: {} - hardhat-watcher@2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)): + hardhat-watcher@2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)): dependencies: chokidar: 3.6.0 - hardhat: 2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10) + hardhat: 2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10) - hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10): + hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10): dependencies: '@ethereumjs/util': 9.1.0 '@ethersproject/abi': 5.8.0 @@ -33708,7 +34733,7 @@ snapshots: find-up: 5.0.0 fp-ts: 1.19.3 fs-extra: 7.0.1 - immutable: 4.3.7 + immutable: 4.3.8 io-ts: 1.10.4 json-stream-stringify: 3.1.6 keccak: 3.0.4 @@ -33730,7 +34755,7 @@ snapshots: uuid: 8.3.2 ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: - ts-node: 10.9.2(@types/node@22.19.13)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - bufferutil @@ -33902,7 +34927,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -33951,7 +34976,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -33987,7 +35012,7 @@ snapshots: immer@9.0.21: {} - immutable@4.3.7: {} + immutable@4.3.8: {} import-fresh@3.3.1: dependencies: @@ -34362,6 +35387,10 @@ snapshots: dependencies: ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)): + dependencies: + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + isows@1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -34438,7 +35467,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.19.13 + '@types/node': 25.3.5 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -35326,7 +36355,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -35979,6 +37008,21 @@ snapshots: - debug - utf-8-validate + osmojs@0.37.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@babel/runtime': 7.28.6 + '@cosmjs/amino': 0.29.3 + '@cosmjs/proto-signing': 0.29.3 + '@cosmjs/stargate': 0.29.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@cosmjs/tendermint-rpc': 0.29.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@osmonauts/lcd': 0.8.0 + long: 5.3.2 + protobufjs: 6.11.4 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + outvariant@1.4.3: {} own-keys@1.0.1: @@ -36034,8 +37078,8 @@ snapshots: ox@0.6.7(typescript@5.2.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 abitype: 1.0.8(typescript@5.2.2)(zod@3.25.76) @@ -36362,21 +37406,21 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + porto@0.2.35(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)))(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)) hono: 4.12.3 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.2.2) ox: 0.9.17(typescript@5.2.2)(zod@4.3.6) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) zod: 4.3.6 zustand: 5.0.11(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) optionalDependencies: '@tanstack/react-query': 5.69.0(react@19.2.4) react: 19.2.4 typescript: 5.2.2 - wagmi: 2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) transitivePeerDependencies: - '@types/react' - immer @@ -37717,7 +38761,7 @@ snapshots: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -37751,10 +38795,21 @@ snapshots: - supports-color - utf-8-validate + socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) + socket.io-parser: 4.2.5 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -38166,12 +39221,11 @@ snapshots: - typescript - utf-8-validate - terser-webpack-plugin@5.3.16(webpack@5.105.3): + terser-webpack-plugin@5.3.17(webpack@5.105.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - serialize-javascript: 6.0.2 terser: 5.46.0 webpack: 5.105.3 @@ -38321,13 +39375,13 @@ snapshots: trim-newlines@3.0.1: {} - tronweb@6.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + tronweb@6.1.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: '@babel/runtime': 7.26.10 axios: 1.12.2 bignumber.js: 9.1.2 ethereum-cryptography: 2.2.1 - ethers: 6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ethers: 6.13.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) eventemitter3: 5.0.1 google-protobuf: 3.21.4 semver: 7.7.1 @@ -38372,14 +39426,14 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@22.19.13)(typescript@5.2.2): + ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.13 + '@types/node': 25.3.5 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -38620,6 +39674,8 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.18.2: {} + undici-types@7.22.0: {} undici@5.29.0: @@ -38964,6 +40020,23 @@ snapshots: - utf-8-validate - zod + viem@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.2.2)(zod@3.25.76) + isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.6.7(typescript@5.2.2)(zod@3.25.76) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.40.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -39037,7 +40110,7 @@ snapshots: vite-node@3.0.9(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) @@ -39055,6 +40128,27 @@ snapshots: - tsx - yaml + vite-node@3.0.9(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3(supports-color@8.1.1) + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite-plugin-checker@0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@babel/code-frame': 7.29.0 @@ -39072,11 +40166,11 @@ snapshots: optionator: 0.9.4 typescript: 5.2.2 - vite-plugin-node-polyfills@0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0)): + vite-plugin-node-polyfills@0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.59.0) node-stdlib-browser: 1.3.1 - vite: 5.4.21(@types/node@22.19.13)(terser@5.46.0) + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) transitivePeerDependencies: - rollup @@ -39088,9 +40182,17 @@ snapshots: transitivePeerDependencies: - rollup + vite-plugin-node-polyfills@0.23.0(rollup@4.59.0)(vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + '@rollup/plugin-inject': 5.0.5(rollup@4.59.0) + node-stdlib-browser: 1.3.1 + vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - rollup + vite-tsconfig-paths@5.1.4(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.2.2) optionalDependencies: @@ -39099,13 +40201,13 @@ snapshots: - supports-color - typescript - vite@5.4.21(@types/node@22.19.13)(terser@5.46.0): + vite@5.4.21(@types/node@25.3.5)(terser@5.46.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 rollup: 4.59.0 optionalDependencies: - '@types/node': 22.19.13 + '@types/node': 25.3.5 fsevents: 2.3.3 terser: 5.46.0 @@ -39124,7 +40226,22 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.3.5 + fsevents: 2.3.3 + terser: 5.46.0 + tsx: 4.21.0 + yaml: 2.8.2 + + vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 3.0.9 '@vitest/mocker': 3.0.9(msw@0.36.8)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -39134,7 +40251,7 @@ snapshots: '@vitest/spy': 3.0.9 '@vitest/utils': 3.0.9 chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -39149,7 +40266,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.19.13 - happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) jsdom: 28.0.0(@noble/hashes@2.0.1) transitivePeerDependencies: - jiti @@ -39165,7 +40282,7 @@ snapshots: - tsx - yaml - vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 3.0.9 '@vitest/mocker': 3.0.9(msw@0.27.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -39184,12 +40301,12 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.0.9(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.0.9(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.19.13 + '@types/node': 25.3.5 happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) jsdom: 28.0.0(@noble/hashes@2.0.1) transitivePeerDependencies: @@ -39206,10 +40323,10 @@ snapshots: - tsx - yaml - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(msw@0.36.8)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(msw@0.36.8)(vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -39226,11 +40343,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@types/node': 22.19.13 + '@types/node': 25.3.5 happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) jsdom: 28.0.0(@noble/hashes@2.0.1) transitivePeerDependencies: @@ -39256,14 +40373,14 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76): dependencies: '@tanstack/react-query': 5.69.0(react@19.2.4) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)) react: 19.2.4 use-sync-external-store: 1.4.0(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: @@ -39475,7 +40592,7 @@ snapshots: - encoding - utf-8-validate - web3-provider-engine@16.0.1(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + web3-provider-engine@16.0.1(@babel/core@7.29.0)(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: async: 2.6.4 backoff: 2.5.0 @@ -39496,7 +40613,7 @@ snapshots: readable-stream: 3.6.0 request: 2.88.2 semaphore: 1.1.0 - ws: 5.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 5.2.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) xhr: 2.6.0 xtend: 4.0.2 transitivePeerDependencies: @@ -39652,7 +40769,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(webpack@5.105.3) + terser-webpack-plugin: 5.3.17(webpack@5.105.3) watchpack: 2.5.1 webpack-sources: 3.3.4 transitivePeerDependencies: @@ -39677,6 +40794,18 @@ snapshots: - bufferutil - utf-8-validate + websocket-stream@5.5.2(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + duplexify: 3.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + safe-buffer: 5.2.1 + ws: 3.3.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + xtend: 4.0.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + websocket@1.0.35: dependencies: bufferutil: 4.1.0 @@ -39837,12 +40966,21 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@5.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): + ws@3.3.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: async-limiter: 1.0.1 + safe-buffer: 5.1.2 + ultron: 1.1.1 optionalDependencies: bufferutil: 4.1.0 - utf-8-validate: 5.0.10 + utf-8-validate: 6.0.6 + + ws@5.2.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + async-limiter: 1.0.1 + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: @@ -39854,10 +40992,10 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 6.0.6 - ws@7.5.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + ws@7.5.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): optionalDependencies: bufferutil: 4.1.0 - utf-8-validate: 5.0.10 + utf-8-validate: 6.0.6 ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: @@ -39869,6 +41007,11 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 + ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.1.0