From aaffd3f7ed1015c5caad134cb73e4a50d51868ca Mon Sep 17 00:00:00 2001 From: Sergo Date: Sat, 25 Apr 2026 15:41:58 +0100 Subject: [PATCH 01/17] WIP: start extracting mango client --- .gitignore | 3 + package.json | 14 +- pnpm-lock.yaml | 792 +++++++++++++++++++++----------------------- pnpm-workspace.yaml | 6 +- 4 files changed, 398 insertions(+), 417 deletions(-) diff --git a/.gitignore b/.gitignore index 7d0d0d765..0e7e3efcd 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts +# workspace package build artifacts +packages/*/dist + # Speedy Web Compiler .swc/ diff --git a/package.json b/package.json index 7e62cc275..0036366b6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "next build", + "build": "pnpm run build:packages && next build", + "build:packages": "pnpm -r --filter './packages/**' run build", "build:info": "node scripts/update-build-info.js", "build-sb": "storybook build", "coverage": "vitest --coverage", @@ -18,9 +19,10 @@ "sb": "storybook dev -p 6006", "scan": "next dev & react-scan localhost:3000", "start": "next start", - "test": "vitest --project specs run", + "test": "pnpm run test:packages && vitest --project specs run", "test:changed": "pnpm vitest:test related --run", - "test:ci": "vitest --project specs run", + "test:packages": "pnpm -r --filter './packages/**' run test", + "test:ci": "pnpm run test:packages && vitest --project specs run", "test:e2e": "playwright test", "test:e2e:ci": "playwright test", "test:e2e:ui": "playwright test --ui", @@ -32,7 +34,7 @@ "vitest:test": "vitest --project specs" }, "dependencies": { - "@blockworks-foundation/mango-client": "3.6.7", + "@explorer/decoder-mango": "workspace:*", "@bonfida/spl-name-service": "0.1.30", "@codama/dynamic-client": "0.1.0", "@codama/dynamic-parsers": "1.1.30", @@ -128,7 +130,6 @@ "swr": "2.2.0", "tailwind-merge": "3.0.2", "tweetnacl": "1.0.3", - "typescript": "5.7.3", "undici": "6.25.0", "use-async-effect": "2.2.7" }, @@ -190,9 +191,10 @@ "styled-jsx": "5.1.6", "tailwindcss": "3.4.17", "tsx": "4.21.0", + "typescript": "catalog:", "typescript-eslint": "8.57.1", "vite-plugin-node-polyfills": "0.23.0", - "vitest": "3.2.4" + "vitest": "catalog:" }, "packageManager": "pnpm@10.17.1", "devEngines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93b95475e..5857f22f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,15 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + typescript: + specifier: 5.7.3 + version: 5.7.3 + vitest: + specifier: 3.2.4 + version: 3.2.4 + overrides: '@babel/runtime@<7.26.10': 7.26.10 avsc: git://github.com/Irys-xyz/avsc#a730cc8018b79e114b6a3381bbb57760a24c6cef @@ -56,9 +65,6 @@ importers: .: dependencies: - '@blockworks-foundation/mango-client': - specifier: 3.6.7 - version: 3.6.7(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10) '@bonfida/spl-name-service': specifier: 0.1.30 version: 0.1.30(@solana/buffer-layout@3.0.0)(@solana/spl-token@0.1.8(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.98.4(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10))(bn.js@5.2.1)(borsh@0.7.0)(bufferutil@4.0.7)(utf-8-validate@5.0.10) @@ -74,6 +80,9 @@ importers: '@coral-xyz/anchor': specifier: 0.30.1 version: 0.30.1(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10) + '@explorer/decoder-mango': + specifier: workspace:* + version: link:packages/decoder-mango '@fast-csv/format': specifier: 5.0.5 version: 5.0.5 @@ -271,7 +280,7 @@ importers: version: 4.2.1 lighthouse-sdk: specifier: 2.0.1 - version: 2.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + version: 2.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) micromatch: specifier: 4.0.8 version: 4.0.8 @@ -328,7 +337,7 @@ importers: version: 4.0.1 sas-lib: specifier: 1.0.8 - version: 1.0.8(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + version: 1.0.8(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) sonner: specifier: 2.0.7 version: 2.0.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -344,9 +353,6 @@ importers: tweetnacl: specifier: 1.0.3 version: 1.0.3 - typescript: - specifier: 5.7.3 - version: 5.7.3 undici: specifier: 6.25.0 version: 6.25.0 @@ -380,16 +386,16 @@ importers: version: 10.1.4(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10)) '@storybook/addon-docs': specifier: 10.1.4 - version: 10.1.4(@types/react@19.2.14)(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + version: 10.1.4(@types/react@19.2.14)(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) '@storybook/addon-onboarding': specifier: 9.1.10 version: 9.1.10(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10)) '@storybook/addon-vitest': specifier: 10.1.4 - version: 10.1.4(@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4))(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vitest@3.2.4) + version: 10.1.4(@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4))(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vitest@3.2.4) '@storybook/nextjs-vite': specifier: 10.1.4 - version: 10.1.4(@babel/core@7.28.5)(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + version: 10.1.4(@babel/core@7.28.5)(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) '@storybook/react': specifier: 10.1.4 version: 10.1.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3) @@ -431,10 +437,10 @@ importers: version: 5.14.5 '@vitejs/plugin-react': specifier: 4.3.4 - version: 4.3.4(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.3.4(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/browser': specifier: 3.2.4 - version: 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) + version: 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) '@vitest/coverage-v8': specifier: 3.2.4 version: 3.2.4(@vitest/browser@3.2.4)(vitest@3.2.4) @@ -525,15 +531,37 @@ importers: tsx: specifier: 4.21.0 version: 4.21.0 + typescript: + specifier: 'catalog:' + version: 5.7.3 typescript-eslint: specifier: 8.57.1 version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3) vite-plugin-node-polyfills: specifier: 0.23.0 - version: 0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) vitest: - specifier: 3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + specifier: 'catalog:' + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + + packages/decoder-mango: + dependencies: + '@blockworks-foundation/mango-client': + specifier: 3.6.7 + version: 3.6.7(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10) + '@project-serum/serum': + specifier: 0.13.61 + version: 0.13.61(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10) + '@solana/web3.js': + specifier: ^1.98.0 + version: 1.98.4(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10) + devDependencies: + typescript: + specifier: 'catalog:' + version: 5.7.3 + vitest: + specifier: 'catalog:' + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) packages: @@ -1141,8 +1169,8 @@ packages: resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} engines: {node: '>=18'} - '@emnapi/runtime@1.9.1': - resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -1951,10 +1979,6 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} - engines: {node: '>=8'} - '@jest/create-cache-key-function@29.7.0': resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2278,8 +2302,8 @@ packages: resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} engines: {node: '>= 20.19.0'} - '@nodable/entities@2.1.0': - resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@nodable/entities@2.1.1': + resolution: {integrity: sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2530,11 +2554,11 @@ packages: '@popperjs/core@2.11.7': resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} - '@posthog/core@1.29.1': - resolution: {integrity: sha512-q+/t/DZALr50YTE0dFgfGSS9EgwcyAlqsn+JS61wLkwdcDM5yu/YTDM8oMKmJupsyjSZlVkDuHZAMd4ab7AxzQ==} + '@posthog/core@1.30.0': + resolution: {integrity: sha512-ar4Z/AghVJP248qR4PoaKctdljvt00HTb4bMXO2TyOFIWBoep5I9djwOHfgI6+ujgCN/Nlt24Hk30hjjQzLnvg==} - '@posthog/types@1.373.4': - resolution: {integrity: sha512-n+0AbGRYYsbi+CQXQi2rF1lwTSyASlaogcw4YSkzB5KeMa4Y6nhNb7+TTnu9aVor+BycsQYCa2OsBrMMbaTekw==} + '@posthog/types@1.377.0': + resolution: {integrity: sha512-h02Cu6NtdBq6CwPFbJuzkiTjFHOjADMMwGUrlBYMP3P4IWhDlrlkYEQOqqMfVfBtn1GkEuDzcxlU2eipmhRHDA==} '@prisma/instrumentation@7.2.0': resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==} @@ -5742,8 +5766,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.59.2': - resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + '@typescript-eslint/project-service@8.60.0': + resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -5760,8 +5784,8 @@ packages: resolution: {integrity: sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.2': - resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + '@typescript-eslint/scope-manager@8.60.0': + resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.57.0': @@ -5776,8 +5800,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.59.2': - resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + '@typescript-eslint/tsconfig-utils@8.60.0': + resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -5801,8 +5825,8 @@ packages: resolution: {integrity: sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.2': - resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + '@typescript-eslint/types@8.60.0': + resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.30.0': @@ -5823,8 +5847,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.59.2': - resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + '@typescript-eslint/typescript-estree@8.60.0': + resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -5850,8 +5874,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.59.2': - resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + '@typescript-eslint/utils@8.60.0': + resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -5869,8 +5893,8 @@ packages: resolution: {integrity: sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.2': - resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + '@typescript-eslint/visitor-keys@8.60.0': + resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -6422,8 +6446,8 @@ packages: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} - baseline-browser-mapping@2.10.20: - resolution: {integrity: sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==} + baseline-browser-mapping@2.10.8: + resolution: {integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -6535,8 +6559,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -6621,9 +6645,6 @@ packages: caniuse-lite@1.0.30001780: resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} - caniuse-lite@1.0.30001788: - resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} - canvg@3.0.11: resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==} engines: {node: '>=10.0.0'} @@ -6702,6 +6723,10 @@ packages: ci-info@2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -7174,8 +7199,8 @@ packages: electron-to-chromium@1.5.194: resolution: {integrity: sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==} - electron-to-chromium@1.5.340: - resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==} + electron-to-chromium@1.5.264: + resolution: {integrity: sha512-1tEf0nLgltC3iy9wtlYDlQDc5Rg9lEKVjEmIHJ21rI9OcqkvD45K1oyNIRA4rR1z3LgJ7KeGzEBojVcV6m4qjA==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -7572,8 +7597,8 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-builder@1.1.5: - resolution: {integrity: sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==} + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} fast-xml-parser@4.5.4: resolution: {integrity: sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==} @@ -7772,8 +7797,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.14.0: - resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -7840,8 +7865,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphql@16.13.2: - resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} + graphql@16.13.1: + resolution: {integrity: sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} handlebars@4.7.9: @@ -7856,9 +7881,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -7878,10 +7900,6 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - hash-base@2.0.2: resolution: {integrity: sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==} @@ -7918,8 +7936,8 @@ packages: hermes-estree@0.32.0: resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} - hermes-estree@0.35.0: - resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} + hermes-estree@0.33.3: + resolution: {integrity: sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} @@ -7927,8 +7945,8 @@ packages: hermes-parser@0.32.0: resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==} - hermes-parser@0.35.0: - resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} + hermes-parser@0.33.3: + resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==} hi-base32@0.5.1: resolution: {integrity: sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==} @@ -8060,10 +8078,6 @@ packages: resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} engines: {node: '>=12.0.0'} - internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} - engines: {node: '>= 0.4'} - internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -8726,8 +8740,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} @@ -8875,61 +8889,61 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - metro-babel-transformer@0.83.6: - resolution: {integrity: sha512-1AnuazBpzY3meRMr04WUw14kRBkV0W3Ez+AA75FAeNpRyWNN5S3M3PHLUbZw7IXq7ZeOzceyRsHStaFrnWd+8w==} + metro-babel-transformer@0.83.5: + resolution: {integrity: sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA==} engines: {node: '>=20.19.4'} - metro-cache-key@0.83.6: - resolution: {integrity: sha512-5gdK4PVpgNOHi7xCGrgesNP1AuOA2TiPqpcirGXZi4RLLzX1VMowpkgTVtBfpQQCqWoosQF9yrSo9/KDQg1eBg==} + metro-cache-key@0.83.5: + resolution: {integrity: sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw==} engines: {node: '>=20.19.4'} - metro-cache@0.83.6: - resolution: {integrity: sha512-DpvZE32feNkqfZkI4Fic7YI/Kw8QP9wdl1rC4YKPrA77wQbI9vXbxjmfkCT/EGwBTFOPKqvIXo+H3BNe93YyiQ==} + metro-cache@0.83.5: + resolution: {integrity: sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng==} engines: {node: '>=20.19.4'} - metro-config@0.83.6: - resolution: {integrity: sha512-G5622400uNtnAMlppEA5zkFAZltEf7DSGhOu09BkisCxOlVMWfdosD/oPyh4f2YVQsc1MBYyp4w6OzbExTYarg==} + metro-config@0.83.5: + resolution: {integrity: sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w==} engines: {node: '>=20.19.4'} - metro-core@0.83.6: - resolution: {integrity: sha512-l+yQ2fuIgR//wszUlMrrAa9+Z+kbKazd0QOh0VQY7jC4ghb7yZBBSla/UMYRBZZ6fPg9IM+wD3+h+37a5f9etw==} + metro-core@0.83.5: + resolution: {integrity: sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ==} engines: {node: '>=20.19.4'} - metro-file-map@0.83.6: - resolution: {integrity: sha512-Jg3oN604C7GWbQwFAUXt8KsbMXeKfsxbZ5HFy4XFM3ggTS+ja9QgUmq9B613kgXv3G4M6rwiI6cvh9TRly4x3w==} + metro-file-map@0.83.5: + resolution: {integrity: sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ==} engines: {node: '>=20.19.4'} - metro-minify-terser@0.83.6: - resolution: {integrity: sha512-Vx3/Ne9Q+EIEDLfKzZUOtn/rxSNa/QjlYxc42nvK4Mg8mB6XUgd3LXX5ZZVq7lzQgehgEqLrbgShJPGfeF8PnQ==} + metro-minify-terser@0.83.5: + resolution: {integrity: sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q==} engines: {node: '>=20.19.4'} - metro-resolver@0.83.6: - resolution: {integrity: sha512-lAwR/FsT1uJ5iCt4AIsN3boKfJ88aN8bjvDT5FwBS0tKeKw4/sbdSTWlFxc7W/MUTN5RekJ3nQkJRIWsvs28tA==} + metro-resolver@0.83.5: + resolution: {integrity: sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A==} engines: {node: '>=20.19.4'} - metro-runtime@0.83.6: - resolution: {integrity: sha512-WQPua1G2VgYbwRn6vSKxOhTX7CFbSf/JdUu6Nd8bZnPXckOf7HQ2y51NXNQHoEsiuawathrkzL8pBhv+zgZFmg==} + metro-runtime@0.83.5: + resolution: {integrity: sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA==} engines: {node: '>=20.19.4'} - metro-source-map@0.83.6: - resolution: {integrity: sha512-AqJbOMMpeyyM4iNI91pchqDIszzNuuHApEhg6OABqZ+9mjLEqzcIEQ/fboZ7x74fNU5DBd2K36FdUQYPqlGClA==} + metro-source-map@0.83.5: + resolution: {integrity: sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ==} engines: {node: '>=20.19.4'} - metro-symbolicate@0.83.6: - resolution: {integrity: sha512-4nvkmv9T7ozhprlPwk/+xm0SVPsxly5kYyMHdNaOlFemFz4df9BanvD46Ac6OISu/4Idinzfk2KVb++6OfzPAQ==} + metro-symbolicate@0.83.5: + resolution: {integrity: sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA==} engines: {node: '>=20.19.4'} hasBin: true - metro-transform-plugins@0.83.6: - resolution: {integrity: sha512-V+zoY2Ul0v0BW6IokJkTud3raXmDdbdwkUQ/5eiSoy0jKuKMhrDjdH+H5buCS5iiJdNbykOn69Eip+Sqymkodg==} + metro-transform-plugins@0.83.5: + resolution: {integrity: sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw==} engines: {node: '>=20.19.4'} - metro-transform-worker@0.83.6: - resolution: {integrity: sha512-G5kDJ/P0ZTIf57t3iyAd5qIXbj2Wb1j7WtIDh82uTFQHe2Mq2SO9aXG9j1wI+kxZlIe58Z22XEXIKMl89z0ibQ==} + metro-transform-worker@0.83.5: + resolution: {integrity: sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA==} engines: {node: '>=20.19.4'} - metro@0.83.6: - resolution: {integrity: sha512-pbdndsAZ2F/ceopDdhVbttpa/hfLzXPJ/husc+QvQ33R0D9UXJKzTn5+OzOXx4bpQNtAKF2bY88cCI3Zl44xDQ==} + metro@0.83.5: + resolution: {integrity: sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ==} engines: {node: '>=20.19.4'} hasBin: true @@ -9227,8 +9241,8 @@ packages: node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - node-releases@2.0.37: - resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} node-stdlib-browser@1.3.1: resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} @@ -9258,8 +9272,8 @@ packages: o3@1.0.3: resolution: {integrity: sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ==} - ob1@0.83.6: - resolution: {integrity: sha512-m/xZYkwcjo6UqLMrUICEB3iHk7Bjt3RSR7KXMi6Y1MO/kGkPhoRmfUDF6KAan3rLAZ7ABRqnQyKUTwaqZgUV4w==} + ob1@0.83.5: + resolution: {integrity: sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg==} engines: {node: '>=20.19.4'} object-assign@4.1.1: @@ -9488,10 +9502,6 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} @@ -9605,8 +9615,8 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - posthog-node@5.34.1: - resolution: {integrity: sha512-kGl0kSfh2+Ey3KL5Sji3yv9W5xwPK9sTkINRoFqCh9fbYXWWY6Zwi5Psv2QmRcbYiMJBk/iecnoOKVDRRga6PA==} + posthog-node@5.35.9: + resolution: {integrity: sha512-kNdZFdK9hNMqEjqWJnZ4YDCQ8YfGCdG3GczvRw0qljS2K/ZEqN2P5QqmQYNcMjxCkT6OeoAdkkM7c4htbIpG6g==} engines: {node: ^20.20.0 || >=22.22.0} peerDependencies: rxjs: ^7.0.0 @@ -10462,11 +10472,11 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - strnum@1.1.2: - resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - strnum@2.2.3: - resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} + strnum@2.3.0: + resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} style-to-js@1.1.18: resolution: {integrity: sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==} @@ -10538,8 +10548,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tapable@2.3.2: - resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} terser-webpack-plugin@5.4.0: @@ -10844,8 +10854,8 @@ packages: undici-types@7.12.0: resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} - undici-types@7.25.0: - resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} + undici-types@7.27.0: + resolution: {integrity: sha512-sqqlwW3zm+cE82GwKdGyn3pcze7LXlx/4jUgA0vtAf6Fa81KMrJqc3VfWmmeOTUIElW9IdPsLwMUIpiOZQgK3A==} undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} @@ -10890,8 +10900,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -10921,13 +10931,17 @@ packages: '@types/react': optional: true - use-composed-ref@1.3.0: - resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true - use-isomorphic-layout-effect@1.2.0: - resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -10935,11 +10949,11 @@ packages: '@types/react': optional: true - use-latest@1.2.1: - resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} peerDependencies: '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true @@ -10983,7 +10997,6 @@ packages: uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true vfile-message@4.0.3: @@ -11313,18 +11326,6 @@ packages: utf-8-validate: optional: true - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - wsl-utils@0.1.0: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} @@ -11337,6 +11338,10 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -11354,21 +11359,11 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} - hasBin: true - yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} - hasBin: true - yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -11962,7 +11957,7 @@ snapshots: '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 7.29.2 '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -11980,7 +11975,7 @@ snapshots: dependencies: '@babel/compat-data': 7.26.8 '@babel/helper-validator-option': 7.25.9 - browserslist: 4.28.2 + browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -11988,7 +11983,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.2 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -12004,7 +11999,7 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -12172,7 +12167,7 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 + '@babel/parser': 7.29.2 '@babel/types': 7.28.5 '@babel/template@7.28.6': @@ -12527,7 +12522,7 @@ snapshots: '@csstools/css-tokenizer@3.0.3': {} - '@emnapi/runtime@1.9.1': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true @@ -13026,11 +13021,11 @@ snapshots: '@inquirer/core': 10.3.2(@types/node@22.15.3) '@inquirer/prompts': 7.10.1(@types/node@22.15.3) chalk: 5.6.2 - commander: 14.0.3 + commander: 14.0.1 fast-glob: 3.3.3 ora: 8.2.0 - posthog-node: 5.34.1(rxjs@7.8.1) - yaml: 2.8.3 + posthog-node: 5.35.9(rxjs@7.8.1) + yaml: 2.8.2 zod: 4.4.3 transitivePeerDependencies: - '@types/node' @@ -13157,7 +13152,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.9.1 + '@emnapi/runtime': 1.10.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -13315,8 +13310,6 @@ snapshots: '@istanbuljs/schema@0.1.3': {} - '@istanbuljs/schema@0.1.6': {} - '@jest/create-cache-key-function@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -13403,12 +13396,12 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: glob: 10.5.0 magic-string: 0.30.21 react-docgen-typescript: 2.4.0(typescript@5.7.3) - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: typescript: 5.7.3 @@ -13787,7 +13780,7 @@ snapshots: '@noble/hashes@2.2.0': {} - '@nodable/entities@2.1.0': {} + '@nodable/entities@2.1.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -14103,11 +14096,11 @@ snapshots: '@popperjs/core@2.11.7': {} - '@posthog/core@1.29.1': + '@posthog/core@1.30.0': dependencies: - '@posthog/types': 1.373.4 + '@posthog/types': 1.377.0 - '@posthog/types@1.373.4': {} + '@posthog/types@1.377.0': {} '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)': dependencies: @@ -15283,9 +15276,9 @@ snapshots: '@react-native/dev-middleware': 0.82.1(bufferutil@4.0.7)(utf-8-validate@5.0.10) debug: 4.4.3 invariant: 2.2.4 - metro: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) - metro-config: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) - metro-core: 0.83.6 + metro: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-config: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-core: 0.83.5 semver: 7.7.2 transitivePeerDependencies: - bufferutil @@ -16426,7 +16419,7 @@ snapshots: '@solana/errors@2.3.0(typescript@5.7.3)': dependencies: chalk: 5.6.2 - commander: 14.0.3 + commander: 14.0.1 typescript: 5.7.3 '@solana/errors@4.0.0(typescript@5.7.3)': @@ -16551,7 +16544,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -16564,11 +16557,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/rpc-parsed-types': 2.3.0(typescript@5.7.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.7.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) typescript: 5.7.3 @@ -16903,23 +16896,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.0.0(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.0.0(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.0.0(typescript@5.7.3) '@solana/functional': 2.0.0(typescript@5.7.3) '@solana/rpc-subscriptions-spec': 2.0.0(typescript@5.7.3) '@solana/subscribable': 2.0.0(typescript@5.7.3) typescript: 5.7.3 - ws: 8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.7.3) '@solana/functional': 2.3.0(typescript@5.7.3) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.7.3) '@solana/subscribable': 2.3.0(typescript@5.7.3) typescript: 5.7.3 - ws: 8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-channel-websocket@6.5.0(bufferutil@4.0.7)(typescript@5.7.3)(utf-8-validate@5.0.10)': dependencies: @@ -16959,7 +16952,7 @@ snapshots: optionalDependencies: typescript: 5.7.3 - '@solana/rpc-subscriptions@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.0.0(typescript@5.7.3) '@solana/fast-stable-stringify': 2.0.0(typescript@5.7.3) @@ -16967,7 +16960,7 @@ snapshots: '@solana/promises': 2.0.0(typescript@5.7.3) '@solana/rpc-spec-types': 2.0.0(typescript@5.7.3) '@solana/rpc-subscriptions-api': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/rpc-subscriptions-channel-websocket': 2.0.0(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.0.0(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.0.0(typescript@5.7.3) '@solana/rpc-transformers': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -16977,7 +16970,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.7.3) '@solana/fast-stable-stringify': 2.3.0(typescript@5.7.3) @@ -16985,7 +16978,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.7.3) '@solana/rpc-spec-types': 2.3.0(typescript@5.7.3) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.7.3) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17069,7 +17062,7 @@ snapshots: '@solana/errors': 6.5.0(typescript@5.7.3) '@solana/rpc-spec': 6.5.0(typescript@5.7.3) '@solana/rpc-spec-types': 6.5.0(typescript@5.7.3) - undici-types: 7.25.0 + undici-types: 7.27.0 optionalDependencies: typescript: 5.7.3 @@ -17333,7 +17326,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/codecs-strings': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17341,7 +17334,7 @@ snapshots: '@solana/keys': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/promises': 2.0.0(typescript@5.7.3) '@solana/rpc': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transaction-messages': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transactions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17350,7 +17343,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17358,7 +17351,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/promises': 2.3.0(typescript@5.7.3) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17643,7 +17636,7 @@ snapshots: - typescript - utf-8-validate - '@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': + '@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/addresses': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) @@ -17656,11 +17649,11 @@ snapshots: '@solana/rpc': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/rpc-parsed-types': 2.0.0(typescript@5.7.3) '@solana/rpc-spec-types': 2.0.0(typescript@5.7.3) - '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/signers': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/sysvars': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/transaction-confirmation': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) '@solana/transactions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) typescript: 5.7.3 @@ -17710,10 +17703,10 @@ snapshots: axe-core: 4.11.0 storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) - '@storybook/addon-docs@10.1.4(@types/react@19.2.14)(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1))': + '@storybook/addon-docs@10.1.4(@types/react@19.2.14)(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1))': dependencies: '@mdx-js/react': 3.1.0(@types/react@19.2.14)(react@19.2.6) - '@storybook/csf-plugin': 10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + '@storybook/csf-plugin': 10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) '@storybook/icons': 2.0.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@storybook/react-dom-shim': 10.1.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10)) react: 19.2.6 @@ -17731,41 +17724,41 @@ snapshots: dependencies: storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) - '@storybook/addon-vitest@10.1.4(@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4))(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vitest@3.2.4)': + '@storybook/addon-vitest@10.1.4(@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4))(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vitest@3.2.4)': dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) optionalDependencies: - '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) - '@vitest/browser-playwright': 4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) + '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) + '@vitest/browser-playwright': 4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) '@vitest/runner': 3.2.4 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - react - react-dom - '@storybook/builder-vite@10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1))': + '@storybook/builder-vite@10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1))': dependencies: - '@storybook/csf-plugin': 10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) - '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@storybook/csf-plugin': 10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) + '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) ts-dedent: 2.2.0 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - esbuild - msw - rollup - webpack - '@storybook/csf-plugin@10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1))': + '@storybook/csf-plugin@10.1.4(esbuild@0.27.1)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1))': dependencies: storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.1 rollup: 4.50.1 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) webpack: 5.99.5(esbuild@0.27.1) '@storybook/global@5.0.0': {} @@ -17775,18 +17768,18 @@ snapshots: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - '@storybook/nextjs-vite@10.1.4(@babel/core@7.28.5)(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1))': + '@storybook/nextjs-vite@10.1.4(@babel/core@7.28.5)(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1))': dependencies: - '@storybook/builder-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + '@storybook/builder-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) '@storybook/react': 10.1.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3) - '@storybook/react-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + '@storybook/react-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) next: 16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.6) - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-plugin-storybook-nextjs: 3.1.5(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-storybook-nextjs: 3.1.5(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: typescript: 5.7.3 transitivePeerDependencies: @@ -17804,11 +17797,11 @@ snapshots: react-dom: 19.2.6(react@19.2.6) storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) - '@storybook/react-vite@10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1))': + '@storybook/react-vite@10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@rollup/pluginutils': 5.3.0(rollup@4.50.1) - '@storybook/builder-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.99.5(esbuild@0.27.1)) + '@storybook/builder-vite': 10.1.4(esbuild@0.27.1)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(rollup@4.50.1)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.99.5(esbuild@0.27.1)) '@storybook/react': 10.1.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3) empathic: 2.0.0 magic-string: 0.30.21 @@ -17818,7 +17811,7 @@ snapshots: resolve: 1.22.11 storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) tsconfig-paths: 4.2.0 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - esbuild - msw @@ -18182,10 +18175,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.2(typescript@5.7.3)': + '@typescript-eslint/project-service@8.60.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.7.3) - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.7.3) + '@typescript-eslint/types': 8.60.0 debug: 4.4.3 typescript: 5.7.3 transitivePeerDependencies: @@ -18206,10 +18199,10 @@ snapshots: '@typescript-eslint/types': 8.57.1 '@typescript-eslint/visitor-keys': 8.57.1 - '@typescript-eslint/scope-manager@8.59.2': + '@typescript-eslint/scope-manager@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.7.3)': dependencies: @@ -18219,7 +18212,7 @@ snapshots: dependencies: typescript: 5.7.3 - '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.7.3)': + '@typescript-eslint/tsconfig-utils@8.60.0(typescript@5.7.3)': dependencies: typescript: 5.7.3 @@ -18241,7 +18234,7 @@ snapshots: '@typescript-eslint/types@8.57.1': {} - '@typescript-eslint/types@8.59.2': {} + '@typescript-eslint/types@8.60.0': {} '@typescript-eslint/typescript-estree@8.30.0(typescript@5.7.3)': dependencies: @@ -18287,12 +18280,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.2(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.60.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/project-service': 8.59.2(typescript@5.7.3) - '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.7.3) - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/visitor-keys': 8.59.2 + '@typescript-eslint/project-service': 8.60.0(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.7.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.4 @@ -18304,7 +18297,7 @@ snapshots: '@typescript-eslint/utils@8.30.0(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.4(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.30.0 '@typescript-eslint/types': 8.30.0 '@typescript-eslint/typescript-estree': 8.30.0(typescript@5.7.3) @@ -18335,12 +18328,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.2(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3)': + '@typescript-eslint/utils@8.60.0(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.7.3) eslint: 9.39.4(jiti@2.4.2) typescript: 5.7.3 transitivePeerDependencies: @@ -18361,31 +18354,31 @@ snapshots: '@typescript-eslint/types': 8.57.1 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.59.2': + '@typescript-eslint/visitor-keys@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.3.4(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitejs/plugin-react@4.3.4(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.26.10 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4)': + '@vitest/browser-playwright@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4)': dependencies: - '@vitest/browser': 4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) - '@vitest/mocker': 4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/browser': 4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) + '@vitest/mocker': 4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.1.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - bufferutil - msw @@ -18393,16 +18386,16 @@ snapshots: - vite optional: true - '@vitest/browser@3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4)': + '@vitest/browser@3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/utils': 3.2.4 magic-string: 0.30.21 sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) ws: 8.18.3(bufferutil@4.0.7)(utf-8-validate@5.0.10) optionalDependencies: playwright: 1.57.0 @@ -18412,17 +18405,17 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4)': + '@vitest/browser@4.0.17(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4)': dependencies: - '@vitest/mocker': 4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/utils': 4.0.17 magic-string: 0.30.21 pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - ws: 8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + ws: 8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - msw @@ -18445,21 +18438,21 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: - '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) + '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) transitivePeerDependencies: - supports-color '@vitest/eslint-plugin@1.6.16(@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3))(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3))(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3)(vitest@3.2.4)': dependencies: - '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/utils': 8.60.0(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3) eslint: 9.39.4(jiti@2.4.2) optionalDependencies: '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3))(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3) typescript: 5.7.3 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -18471,23 +18464,23 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.7.5(@types/node@22.15.3)(typescript@5.7.3) - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.0.17(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.17 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.7.5(@types/node@22.15.3)(typescript@5.7.3) - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) optional: true '@vitest/pretty-format@3.2.4': @@ -19058,7 +19051,7 @@ snapshots: dependencies: '@babel/helper-plugin-utils': 7.28.6 '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.6 + '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 test-exclude: 6.0.0 transitivePeerDependencies: @@ -19121,7 +19114,7 @@ snapshots: base64url@3.0.1: {} - baseline-browser-mapping@2.10.20: {} + baseline-browser-mapping@2.10.8: {} bech32@1.1.4: {} @@ -19254,13 +19247,13 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) - browserslist@4.28.2: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.10.20 - caniuse-lite: 1.0.30001788 - electron-to-chromium: 1.5.340 - node-releases: 2.0.37 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + baseline-browser-mapping: 2.10.8 + caniuse-lite: 1.0.30001780 + electron-to-chromium: 1.5.264 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) bs58@4.0.1: dependencies: @@ -19341,8 +19334,6 @@ snapshots: caniuse-lite@1.0.30001780: {} - caniuse-lite@1.0.30001788: {} - canvg@3.0.11: dependencies: '@babel/runtime': 7.26.10 @@ -19434,6 +19425,8 @@ snapshots: ci-info@2.0.0: {} + ci-info@3.8.0: {} + ci-info@3.9.0: {} ci-info@4.4.0: {} @@ -19575,7 +19568,7 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.2 + browserslist: 4.28.1 core-js@3.48.0: optional: true @@ -19808,7 +19801,7 @@ snapshots: define-properties@1.2.0: dependencies: - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.2 object-keys: 1.1.1 define-properties@1.2.1: @@ -19909,7 +19902,7 @@ snapshots: electron-to-chromium@1.5.194: {} - electron-to-chromium@1.5.340: {} + electron-to-chromium@1.5.264: {} elliptic@6.6.1: dependencies: @@ -19936,7 +19929,7 @@ snapshots: enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.2 + tapable: 2.3.0 entities@4.5.0: {} @@ -20244,7 +20237,7 @@ snapshots: eslint: 9.39.4(jiti@2.4.2) eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@9.39.4(jiti@2.4.2)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.5.5)(eslint@9.39.4(jiti@2.4.2)) - get-tsconfig: 4.14.0 + get-tsconfig: 4.13.6 globby: 13.1.4 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -20643,20 +20636,21 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-builder@1.1.5: + fast-xml-builder@1.2.0: dependencies: path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 fast-xml-parser@4.5.4: dependencies: - strnum: 1.1.2 + strnum: 1.0.5 fast-xml-parser@5.7.0: dependencies: - '@nodable/entities': 2.1.0 - fast-xml-builder: 1.1.5 + '@nodable/entities': 2.1.1 + fast-xml-builder: 1.2.0 path-expression-matcher: 1.5.0 - strnum: 2.2.3 + strnum: 2.3.0 fastestsmallesttextencoderdecoder@1.0.22: {} @@ -20850,7 +20844,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.14.0: + get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -20906,7 +20900,7 @@ snapshots: globby@13.1.4: dependencies: dir-glob: 3.0.1 - fast-glob: 3.3.3 + fast-glob: 3.3.2 ignore: 5.3.2 merge2: 1.4.1 slash: 4.0.0 @@ -20917,7 +20911,7 @@ snapshots: graceful-fs@4.2.11: {} - graphql@16.13.2: + graphql@16.13.1: optional: true handlebars@4.7.9: @@ -20933,10 +20927,6 @@ snapshots: has-flag@4.0.0: {} - has-property-descriptors@1.0.0: - dependencies: - get-intrinsic: 1.3.0 - has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -20953,10 +20943,6 @@ snapshots: dependencies: has-symbols: 1.1.0 - has@1.0.3: - dependencies: - function-bind: 1.1.2 - hash-base@2.0.2: dependencies: inherits: 2.0.4 @@ -21014,7 +21000,7 @@ snapshots: hermes-estree@0.32.0: {} - hermes-estree@0.35.0: {} + hermes-estree@0.33.3: {} hermes-parser@0.25.1: dependencies: @@ -21024,9 +21010,9 @@ snapshots: dependencies: hermes-estree: 0.32.0 - hermes-parser@0.35.0: + hermes-parser@0.33.3: dependencies: - hermes-estree: 0.35.0 + hermes-estree: 0.33.3 hi-base32@0.5.1: {} @@ -21180,12 +21166,6 @@ snapshots: through: 2.3.8 wrap-ansi: 7.0.0 - internal-slot@1.0.5: - dependencies: - get-intrinsic: 1.3.0 - has: 1.0.3 - side-channel: 1.0.4 - internal-slot@1.0.7: dependencies: es-errors: 1.3.0 @@ -21255,7 +21235,7 @@ snapshots: is-boolean-object@1.1.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.8 has-tostringtag: 1.0.2 is-boolean-object@1.2.2: @@ -21410,7 +21390,7 @@ snapshots: is-typed-array@1.1.10: dependencies: available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.8 for-each: 0.3.3 gopd: 1.2.0 has-tostringtag: 1.0.2 @@ -21437,7 +21417,7 @@ snapshots: is-weakset@2.0.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.8 get-intrinsic: 1.3.0 is-weakset@2.0.3: @@ -21471,7 +21451,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/parser': 7.29.2 - '@istanbuljs/schema': 0.1.6 + '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: @@ -21630,7 +21610,7 @@ snapshots: '@jest/types': 29.5.0 '@types/node': 22.15.3 chalk: 4.1.2 - ci-info: 3.9.0 + ci-info: 3.8.0 graceful-fs: 4.2.11 picomatch: 2.3.1 @@ -21641,7 +21621,7 @@ snapshots: chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 - picomatch: 2.3.2 + picomatch: 2.3.1 jest-validate@29.7.0: dependencies: @@ -21870,10 +21850,10 @@ snapshots: transitivePeerDependencies: - supports-color - lighthouse-sdk@2.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)): + lighthouse-sdk@2.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)): dependencies: '@minhducsun2002/leb128': 1.0.0 - '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) '@webassemblyjs/leb128': 1.14.1 leb: 1.0.0 transitivePeerDependencies: @@ -21941,7 +21921,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.18.1: {} + lodash-es@4.17.23: {} lodash.clonedeep@4.5.0: {} @@ -22195,51 +22175,50 @@ snapshots: merge2@1.4.1: {} - metro-babel-transformer@0.83.6: + metro-babel-transformer@0.83.5: dependencies: '@babel/core': 7.28.5 flow-enums-runtime: 0.0.6 - hermes-parser: 0.35.0 - metro-cache-key: 0.83.6 + hermes-parser: 0.33.3 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-cache-key@0.83.6: + metro-cache-key@0.83.5: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.83.6: + metro-cache@0.83.5: dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 https-proxy-agent: 7.0.6 - metro-core: 0.83.6 + metro-core: 0.83.5 transitivePeerDependencies: - supports-color - metro-config@0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10): + metro-config@0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10): dependencies: connect: 3.7.0 flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) - metro-cache: 0.83.6 - metro-core: 0.83.6 - metro-runtime: 0.83.6 - yaml: 2.8.3 + metro: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-cache: 0.83.5 + metro-core: 0.83.5 + metro-runtime: 0.83.5 + yaml: 2.8.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro-core@0.83.6: + metro-core@0.83.5: dependencies: flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 - metro-resolver: 0.83.6 + metro-resolver: 0.83.5 - metro-file-map@0.83.6: + metro-file-map@0.83.5: dependencies: debug: 4.4.3 fb-watchman: 2.0.2 @@ -22253,46 +22232,46 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.83.6: + metro-minify-terser@0.83.5: dependencies: flow-enums-runtime: 0.0.6 terser: 5.46.1 - metro-resolver@0.83.6: + metro-resolver@0.83.5: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.83.6: + metro-runtime@0.83.5: dependencies: '@babel/runtime': 7.26.10 flow-enums-runtime: 0.0.6 - metro-source-map@0.83.6: + metro-source-map@0.83.5: dependencies: '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.83.6 + metro-symbolicate: 0.83.5 nullthrows: 1.1.1 - ob1: 0.83.6 + ob1: 0.83.5 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.83.6: + metro-symbolicate@0.83.5: dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.83.6 + metro-source-map: 0.83.5 nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.83.6: + metro-transform-plugins@0.83.5: dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.29.1 @@ -22303,27 +22282,27 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10): + metro-transform-worker@0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10): dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.29.1 '@babel/parser': 7.29.2 '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - metro: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) - metro-babel-transformer: 0.83.6 - metro-cache: 0.83.6 - metro-cache-key: 0.83.6 - metro-minify-terser: 0.83.6 - metro-source-map: 0.83.6 - metro-transform-plugins: 0.83.6 + metro: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-babel-transformer: 0.83.5 + metro-cache: 0.83.5 + metro-cache-key: 0.83.5 + metro-minify-terser: 0.83.5 + metro-source-map: 0.83.5 + metro-transform-plugins: 0.83.5 nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10): + metro@0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10): dependencies: '@babel/code-frame': 7.29.0 '@babel/core': 7.28.5 @@ -22340,24 +22319,24 @@ snapshots: error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 - hermes-parser: 0.35.0 + hermes-parser: 0.33.3 image-size: 1.2.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.6 - metro-cache: 0.83.6 - metro-cache-key: 0.83.6 - metro-config: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) - metro-core: 0.83.6 - metro-file-map: 0.83.6 - metro-resolver: 0.83.6 - metro-runtime: 0.83.6 - metro-source-map: 0.83.6 - metro-symbolicate: 0.83.6 - metro-transform-plugins: 0.83.6 - metro-transform-worker: 0.83.6(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-babel-transformer: 0.83.5 + metro-cache: 0.83.5 + metro-cache-key: 0.83.5 + metro-config: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) + metro-core: 0.83.5 + metro-file-map: 0.83.5 + metro-resolver: 0.83.5 + metro-runtime: 0.83.5 + metro-source-map: 0.83.5 + metro-symbolicate: 0.83.5 + metro-transform-plugins: 0.83.5 + metro-transform-worker: 0.83.5(bufferutil@4.0.7)(utf-8-validate@5.0.10) mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 @@ -22653,7 +22632,7 @@ snapshots: '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.6 - graphql: 16.13.2 + graphql: 16.13.1 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -22728,8 +22707,8 @@ snapshots: dependencies: '@next/env': 16.2.6 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.20 - caniuse-lite: 1.0.30001788 + baseline-browser-mapping: 2.10.8 + caniuse-lite: 1.0.30001780 postcss: 8.4.31 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) @@ -22770,7 +22749,7 @@ snapshots: node-releases@2.0.19: {} - node-releases@2.0.37: {} + node-releases@2.0.27: {} node-stdlib-browser@1.3.1: dependencies: @@ -22820,7 +22799,7 @@ snapshots: dependencies: capability: 0.2.5 - ob1@0.83.6: + ob1@0.83.5: dependencies: flow-enums-runtime: 0.0.6 @@ -23089,8 +23068,6 @@ snapshots: picomatch@2.3.1: {} - picomatch@2.3.2: {} - picomatch@4.0.3: {} pify@2.3.0: {} @@ -23128,7 +23105,7 @@ snapshots: postcss: 8.5.10 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.11 + resolve: 1.22.8 postcss-import@16.1.0(postcss@8.5.10): dependencies: @@ -23145,7 +23122,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.10): dependencies: lilconfig: 3.1.3 - yaml: 2.7.0 + yaml: 2.8.2 optionalDependencies: postcss: 8.5.10 @@ -23183,9 +23160,9 @@ snapshots: dependencies: xtend: 4.0.2 - posthog-node@5.34.1(rxjs@7.8.1): + posthog-node@5.35.9(rxjs@7.8.1): dependencies: - '@posthog/core': 1.29.1 + '@posthog/core': 1.30.0 optionalDependencies: rxjs: 7.8.1 @@ -23365,7 +23342,7 @@ snapshots: '@types/lodash': 4.17.24 color: 4.2.3 csstype: 3.2.3 - lodash-es: 4.18.1 + lodash-es: 4.17.23 react-chartjs-2@5.2.0(chart.js@4.3.0)(react@19.2.6): dependencies: @@ -23484,8 +23461,8 @@ snapshots: invariant: 2.2.4 jest-environment-node: 29.7.0 memoize-one: 5.2.1 - metro-runtime: 0.83.6 - metro-source-map: 0.83.6 + metro-runtime: 0.83.5 + metro-source-map: 0.83.5 nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -23550,8 +23527,8 @@ snapshots: dependencies: '@babel/runtime': 7.26.10 react: 19.2.6 - use-composed-ref: 1.3.0(react@19.2.6) - use-latest: 1.2.1(@types/react@19.2.14)(react@19.2.6) + use-composed-ref: 1.4.0(@types/react@19.2.14)(react@19.2.6) + use-latest: 1.3.0(@types/react@19.2.14)(react@19.2.6) transitivePeerDependencies: - '@types/react' @@ -23844,9 +23821,9 @@ snapshots: safer-buffer@2.1.2: {} - sas-lib@1.0.8(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)): + sas-lib@1.0.8(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)): dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(ws@8.19.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)) bn.js: 5.2.1 borsh: 0.7.0 borsher: 4.0.0 @@ -24135,7 +24112,7 @@ snapshots: stop-iteration-iterator@1.0.0: dependencies: - internal-slot: 1.0.5 + internal-slot: 1.1.0 stop-iteration-iterator@1.1.0: dependencies: @@ -24304,9 +24281,9 @@ snapshots: dependencies: js-tokens: 9.0.1 - strnum@1.1.2: {} + strnum@1.0.5: {} - strnum@2.2.3: {} + strnum@2.3.0: {} style-to-js@1.1.18: dependencies: @@ -24393,7 +24370,7 @@ snapshots: transitivePeerDependencies: - ts-node - tapable@2.3.2: {} + tapable@2.3.0: {} terser-webpack-plugin@5.4.0(esbuild@0.27.1)(webpack@5.99.5(esbuild@0.27.1)): dependencies: @@ -24414,7 +24391,7 @@ snapshots: test-exclude@6.0.0: dependencies: - '@istanbuljs/schema': 0.1.6 + '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.4 @@ -24579,7 +24556,7 @@ snapshots: tsx@4.21.0: dependencies: esbuild: 0.27.1 - get-tsconfig: 4.14.0 + get-tsconfig: 4.13.6 optionalDependencies: fsevents: 2.3.3 @@ -24701,7 +24678,7 @@ snapshots: undici-types@7.12.0: {} - undici-types@7.25.0: {} + undici-types@7.27.0: {} undici@6.25.0: {} @@ -24762,9 +24739,9 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.2): + update-browserslist-db@1.2.2(browserslist@4.28.1): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -24793,20 +24770,22 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - use-composed-ref@1.3.0(react@19.2.6): + use-composed-ref@1.4.0(@types/react@19.2.14)(react@19.2.6): dependencies: react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.14 - use-isomorphic-layout-effect@1.2.0(@types/react@19.2.14)(react@19.2.6): + use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@19.2.6): dependencies: react: 19.2.6 optionalDependencies: '@types/react': 19.2.14 - use-latest@1.2.1(@types/react@19.2.14)(react@19.2.6): + use-latest@1.3.0(@types/react@19.2.14)(react@19.2.6): dependencies: react: 19.2.6 - use-isomorphic-layout-effect: 1.2.0(@types/react@19.2.14)(react@19.2.6) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@19.2.6) optionalDependencies: '@types/react': 19.2.14 @@ -24860,13 +24839,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@3.2.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): + vite-node@3.2.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -24881,15 +24860,15 @@ snapshots: - tsx - yaml - vite-plugin-node-polyfills@0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + vite-plugin-node-polyfills@0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.50.1) node-stdlib-browser: 1.3.1 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - rollup - vite-plugin-storybook-nextjs@3.1.5(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + vite-plugin-storybook-nextjs@3.1.5(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0))(storybook@10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10))(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@next/env': 16.0.0 image-size: 2.0.2 @@ -24898,24 +24877,24 @@ snapshots: next: 16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.53.0) storybook: 10.1.10(@testing-library/dom@10.4.0)(bufferutil@4.0.7)(prettier@3.5.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(utf-8-validate@5.0.10) ts-dedent: 2.2.0 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-tsconfig-paths: 5.1.4(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite-tsconfig-paths: 5.1.4(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color - typescript - vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.7.3) optionalDependencies: - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): + vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -24931,13 +24910,13 @@ snapshots: sass: 1.53.0 terser: 5.46.1 tsx: 4.21.0 - yaml: 2.8.3 + yaml: 2.8.2 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.4.2)(jsdom@26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24955,13 +24934,13 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-node: 3.2.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.15.3 - '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@3.2.4) + '@vitest/browser': 3.2.4(bufferutil@4.0.7)(msw@2.7.5(@types/node@22.15.3)(typescript@5.7.3))(playwright@1.57.0)(utf-8-validate@5.0.10)(vite@6.3.6(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.53.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) jsdom: 26.0.0(bufferutil@4.0.7)(utf-8-validate@5.0.10) transitivePeerDependencies: - jiti @@ -25030,7 +25009,7 @@ snapshots: '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.16.0 - browserslist: 4.28.2 + browserslist: 4.28.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.20.1 es-module-lexer: 1.7.0 @@ -25043,7 +25022,7 @@ snapshots: mime-types: 2.1.35 neo-async: 2.6.2 schema-utils: 4.3.3 - tapable: 2.3.2 + tapable: 2.3.0 terser-webpack-plugin: 5.4.0(esbuild@0.27.1)(webpack@5.99.5(esbuild@0.27.1)) watchpack: 2.5.1 webpack-sources: 3.3.4 @@ -25218,11 +25197,6 @@ snapshots: bufferutil: 4.0.7 utf-8-validate: 5.0.10 - ws@8.20.0(bufferutil@4.0.7)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.7 - utf-8-validate: 5.0.10 - wsl-utils@0.1.0: dependencies: is-wsl: 3.1.1 @@ -25231,6 +25205,8 @@ snapshots: xml-name-validator@5.0.0: {} + xml-naming@0.1.0: {} + xmlchars@2.2.0: {} xtend@4.0.2: {} @@ -25241,12 +25217,8 @@ snapshots: yallist@3.1.1: {} - yaml@2.7.0: {} - yaml@2.8.2: {} - yaml@2.8.3: {} - yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e0f134841..2d2eb92c6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ -packages: [] +packages: + - 'packages/*' autoInstallPeers: true minimumReleaseAge: 20160 minimumReleaseAgeExclude: @@ -7,3 +8,6 @@ minimumReleaseAgeExclude: - codama - '@solana-program/token' - '@solana/kit' +catalog: + typescript: '5.7.3' + vitest: '3.2.4' From e0fce61c507e8661d8731ded12772b8ec6b17935 Mon Sep 17 00:00:00 2001 From: Sergo Date: Sat, 25 Apr 2026 16:31:31 +0100 Subject: [PATCH 02/17] feat: extract mango decoder --- app/components/account/TokenHistoryCard.tsx | 2 +- app/components/instruction/MangoDetails.tsx | 2 +- .../mango/AddPerpMarketDetailsCard.tsx | 2 +- .../mango/AddSpotMarketDetailsCard.tsx | 2 +- .../mango/CancelPerpOrderDetailsCard.tsx | 2 +- .../mango/CancelSpotOrderDetailsCard.tsx | 2 +- .../ChangePerpMarketParamsDetailsCard.tsx | 2 +- .../mango/ConsumeEventsDetailsCard.tsx | 2 +- .../mango/GenericPerpMngoDetailsCard.tsx | 2 +- .../mango/GenericSpotMngoDetailsCard.tsx | 2 +- .../mango/PlacePerpOrder2DetailsCard.tsx | 2 +- .../mango/PlacePerpOrderDetailsCard.tsx | 2 +- .../mango/PlaceSpotOrderDetailsCard.tsx | 2 +- .../transaction/ui/InstructionsSection.tsx | 9 +- packages/decoder-mango/.gitignore | 2 + packages/decoder-mango/package.json | 32 ++++ .../src/__tests__/config.spec.ts | 38 +++++ .../src/__tests__/decoder.spec.ts | 134 +++++++++++++++++ .../src/__tests__/detection.spec.ts | 52 +++++++ .../decoder-mango/src/__tests__/fixtures.ts | 140 ++++++++++++++++++ .../src/__tests__/market.spec.ts | 105 +++++++++++++ packages/decoder-mango/src/config.ts | 12 ++ .../decoder-mango/src/decoder.ts | 126 +--------------- packages/decoder-mango/src/detection.ts | 13 ++ packages/decoder-mango/src/index.ts | 35 +++++ packages/decoder-mango/src/market.ts | 91 ++++++++++++ packages/decoder-mango/tsconfig.json | 19 +++ packages/decoder-mango/vitest.config.ts | 7 + 28 files changed, 702 insertions(+), 139 deletions(-) create mode 100644 packages/decoder-mango/.gitignore create mode 100644 packages/decoder-mango/package.json create mode 100644 packages/decoder-mango/src/__tests__/config.spec.ts create mode 100644 packages/decoder-mango/src/__tests__/decoder.spec.ts create mode 100644 packages/decoder-mango/src/__tests__/detection.spec.ts create mode 100644 packages/decoder-mango/src/__tests__/fixtures.ts create mode 100644 packages/decoder-mango/src/__tests__/market.spec.ts create mode 100644 packages/decoder-mango/src/config.ts rename app/components/instruction/mango/types.ts => packages/decoder-mango/src/decoder.ts (68%) create mode 100644 packages/decoder-mango/src/detection.ts create mode 100644 packages/decoder-mango/src/index.ts create mode 100644 packages/decoder-mango/src/market.ts create mode 100644 packages/decoder-mango/tsconfig.json create mode 100644 packages/decoder-mango/vitest.config.ts diff --git a/app/components/account/TokenHistoryCard.tsx b/app/components/account/TokenHistoryCard.tsx index 8ea4e9e70..49aba6d25 100644 --- a/app/components/account/TokenHistoryCard.tsx +++ b/app/components/account/TokenHistoryCard.tsx @@ -5,7 +5,7 @@ import { ErrorCard } from '@components/common/ErrorCard'; import { LoadingCard } from '@components/common/LoadingCard'; import { Signature } from '@components/common/Signature'; import { Slot } from '@components/common/Slot'; -import { isMangoInstruction, parseMangoInstructionTitle } from '@components/instruction/mango/types'; +import { isMangoInstruction, parseMangoInstructionTitle } from '@explorer/decoder-mango'; import { isSerumInstruction, parseSerumInstructionTitle } from '@components/instruction/serum/types'; import { isTokenLendingInstruction, diff --git a/app/components/instruction/MangoDetails.tsx b/app/components/instruction/MangoDetails.tsx index 700299cd0..0a789112c 100644 --- a/app/components/instruction/MangoDetails.tsx +++ b/app/components/instruction/MangoDetails.tsx @@ -27,7 +27,7 @@ import { decodePlacePerpOrder2, decodePlaceSpotOrder, parseMangoInstructionTitle, -} from './mango/types'; +} from '@explorer/decoder-mango'; export function MangoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx b/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx index eed6ddd1d..ae4ce7b3b 100644 --- a/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx +++ b/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx @@ -4,7 +4,7 @@ import { formatDuration } from '@utils/date'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { AddPerpMarket } from './types'; +import { AddPerpMarket } from '@explorer/decoder-mango'; export function AddPerpMarketDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx b/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx index 59f6e7407..8d7c3941a 100644 --- a/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx +++ b/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx @@ -3,7 +3,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { AddSpotMarket, spotMarketFromIndex } from './types'; +import { AddSpotMarket, spotMarketFromIndex } from '@explorer/decoder-mango'; export function AddSpotMarketDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx b/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx index 152682a47..76731c93e 100644 --- a/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx +++ b/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx @@ -4,7 +4,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { CancelPerpOrder, getPerpMarketFromInstruction } from './types'; +import { CancelPerpOrder, getPerpMarketFromInstruction } from '@explorer/decoder-mango'; export function CancelPerpOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx b/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx index e9da778d2..d9dec0ab4 100644 --- a/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx +++ b/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx @@ -4,7 +4,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { CancelSpotOrder, getSpotMarketFromInstruction } from './types'; +import { CancelSpotOrder, getSpotMarketFromInstruction } from '@explorer/decoder-mango'; export function CancelSpotOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx b/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx index 5b500fd02..83a42e0c5 100644 --- a/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx +++ b/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { ChangePerpMarketParams, getPerpMarketFromInstruction, getPerpMarketFromPerpMarketConfig } from './types'; +import { ChangePerpMarketParams, getPerpMarketFromInstruction, getPerpMarketFromPerpMarketConfig } from '@explorer/decoder-mango'; export function ChangePerpMarketParamsDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx b/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx index 431b38f19..9b3da122a 100644 --- a/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx +++ b/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx @@ -4,7 +4,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { getPerpMarketFromInstruction } from './types'; +import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; export function ConsumeEventsDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx b/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx index be86edc59..b5608e351 100644 --- a/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx +++ b/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx @@ -4,7 +4,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { getPerpMarketFromInstruction } from './types'; +import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; export function GenericPerpMngoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx b/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx index 363b4e122..a5e8f5a09 100644 --- a/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx +++ b/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx @@ -4,7 +4,7 @@ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; import { InstructionCard } from '../InstructionCard'; -import { getSpotMarketFromInstruction } from './types'; +import { getSpotMarketFromInstruction } from '@explorer/decoder-mango'; export function GenericSpotMngoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx b/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx index f86778307..8eab313a5 100644 --- a/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx +++ b/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx @@ -12,7 +12,7 @@ import { getPerpMarketFromPerpMarketConfig, OrderLotDetails, PlacePerpOrder2, -} from './types'; +} from '@explorer/decoder-mango'; export function PlacePerpOrder2DetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx b/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx index b03180eb6..07acaa0a7 100644 --- a/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx +++ b/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx @@ -12,7 +12,7 @@ import { getPerpMarketFromPerpMarketConfig, OrderLotDetails, PlacePerpOrder, -} from './types'; +} from '@explorer/decoder-mango'; export function PlacePerpOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx b/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx index adc635891..f92605a0f 100644 --- a/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx +++ b/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx @@ -12,7 +12,7 @@ import { getSpotMarketFromSpotMarketConfig, OrderLotDetails, PlaceSpotOrder, -} from './types'; +} from '@explorer/decoder-mango'; export function PlaceSpotOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/features/transaction/ui/InstructionsSection.tsx b/app/features/transaction/ui/InstructionsSection.tsx index 339363341..4019366ab 100644 --- a/app/features/transaction/ui/InstructionsSection.tsx +++ b/app/features/transaction/ui/InstructionsSection.tsx @@ -10,8 +10,6 @@ import { Ed25519DetailsCard } from '@components/instruction/ed25519/Ed25519Detai import { isEd25519Instruction } from '@components/instruction/ed25519/types'; import { LighthouseDetailsCard } from '@components/instruction/lighthouse/LighthouseDetailsCard'; import { isLighthouseInstruction } from '@components/instruction/lighthouse/types'; -import { isMangoInstruction } from '@components/instruction/mango/types'; -import { MangoDetailsCard } from '@components/instruction/MangoDetails'; import { MemoDetailsCard } from '@components/instruction/MemoDetailsCard'; import { ProgramMetadataIdlInstructionDetailsCard } from '@components/instruction/program-metadata-idl/ProgramMetadataIdlInstructionDetailsCard'; import { PythDetailsCard } from '@components/instruction/pyth/PythDetailsCard'; @@ -38,6 +36,7 @@ import { } from '@components/instruction/ZkElGamalProofDetailsCard'; import { useAnchorProgram } from '@entities/idl'; import { useInstructionParser } from '@entities/instruction-parser'; +import { isMangoInstruction } from '@explorer/decoder-mango'; import { MetaplexTokenMetadataDetailsCard } from '@features/mpl-token-metadata'; import { isStakeInstruction, RawStakeDetailsCard, StakeDetailsCard } from '@features/stake'; import { isTokenBatchInstruction, TokenBatchCard } from '@features/token-batch'; @@ -57,6 +56,7 @@ import { import { Cluster } from '@utils/cluster'; import { INNER_INSTRUCTIONS_START_SLOT, SignatureProps } from '@utils/index'; import { intoTransactionInstruction } from '@utils/tx'; +import dynamic from 'next/dynamic'; import React from 'react'; import { ErrorBoundary } from 'react-error-boundary'; @@ -64,6 +64,11 @@ import { useProgramMetadataIdl } from '@/app/entities/program-metadata'; import { CollapsibleSection } from './CollapsibleSection'; +const MangoDetailsCard = dynamic( + () => import('@components/instruction/MangoDetails').then(mod => mod.MangoDetailsCard), + { ssr: false } +); + export type InstructionDetailsProps = { tx: ParsedTransaction; ix: ParsedInstruction; diff --git a/packages/decoder-mango/.gitignore b/packages/decoder-mango/.gitignore new file mode 100644 index 000000000..bd07d4e1b --- /dev/null +++ b/packages/decoder-mango/.gitignore @@ -0,0 +1,2 @@ +node_modules +/dist diff --git a/packages/decoder-mango/package.json b/packages/decoder-mango/package.json new file mode 100644 index 000000000..ae134f947 --- /dev/null +++ b/packages/decoder-mango/package.json @@ -0,0 +1,32 @@ +{ + "name": "@explorer/decoder-mango", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "vitest run", + "test:watch": "vitest", + "types": "tsc --noEmit" + }, + "dependencies": { + "@blockworks-foundation/mango-client": "3.6.7", + "@project-serum/serum": "0.13.61" + }, + "devDependencies": { + "typescript": "catalog:", + "vitest": "catalog:" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0" + } +} diff --git a/packages/decoder-mango/src/__tests__/config.spec.ts b/packages/decoder-mango/src/__tests__/config.spec.ts new file mode 100644 index 000000000..fc5c852ae --- /dev/null +++ b/packages/decoder-mango/src/__tests__/config.spec.ts @@ -0,0 +1,38 @@ +import { PublicKey } from '@solana/web3.js'; +import { describe, expect, it } from 'vitest'; + +import { findGroupConfig, mangoGroups } from '../config'; +import { MANGO_PROGRAM_IDS } from './fixtures'; + +describe('mangoGroups', () => { + it('should not include mainnet.0', () => { + expect(mangoGroups.every(g => g.name !== 'mainnet.0')).toBe(true); + }); + + it('should include mainnet.1', () => { + expect(mangoGroups.some(g => g.name === 'mainnet.1')).toBe(true); + }); + + it.each(['mainnet', 'devnet', 'testnet'] as const)( + 'should contain a group with the %s program ID', + (network) => { + const id = MANGO_PROGRAM_IDS[network].toBase58(); + expect(mangoGroups.some(g => g.mangoProgramId.toBase58() === id)).toBe(true); + } + ); +}); + +describe('findGroupConfig', () => { + it.each(['mainnet', 'devnet', 'testnet'] as const)( + 'should return a group config for %s program ID', + (network) => { + const result = findGroupConfig(MANGO_PROGRAM_IDS[network]); + expect(result).toBeDefined(); + expect(result!.mangoProgramId.equals(MANGO_PROGRAM_IDS[network])).toBe(true); + } + ); + + it('should return undefined for an unknown program ID', () => { + expect(findGroupConfig(PublicKey.default)).toBeUndefined(); + }); +}); diff --git a/packages/decoder-mango/src/__tests__/decoder.spec.ts b/packages/decoder-mango/src/__tests__/decoder.spec.ts new file mode 100644 index 000000000..1748b6de6 --- /dev/null +++ b/packages/decoder-mango/src/__tests__/decoder.spec.ts @@ -0,0 +1,134 @@ +import { describe, expect, it } from 'vitest'; + +import { + decodeAddPerpMarket, + decodeAddSpotMarket, + decodeAddToBasket, + decodeCancelPerpOrder, + decodeCancelSpotOrder, + decodeChangePerpMarketParams, + decodeDeposit, + decodePlacePerpOrder, + decodePlacePerpOrder2, + decodePlaceSpotOrder, + decodeWithdraw, +} from '../decoder'; +import { ENCODED_INSTRUCTIONS, MANGO_PROGRAM_IDS, makeInstruction } from './fixtures'; + +const programId = MANGO_PROGRAM_IDS.mainnet; + +describe('decodeDeposit', () => { + it('should decode quantity', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, programId); + const result = decodeDeposit(ix); + expect(result.quantity).toBe(1_000_000); + }); +}); + +describe('decodeWithdraw', () => { + it('should decode quantity and allowBorrow', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Withdraw, programId); + const result = decodeWithdraw(ix); + expect(result.quantity).toBe(500_000); + expect(result.allowBorrow).toBe('1'); + }); +}); + +describe('decodeAddToBasket', () => { + it('should decode marketIndex', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.AddToBasket, programId); + const result = decodeAddToBasket(ix); + expect(result.marketIndex).toBe(2); + }); +}); + +describe('decodePlaceSpotOrder', () => { + it('should decode all fields', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlaceSpotOrder, programId); + const result = decodePlaceSpotOrder(ix); + expect(result.side).toBe('buy'); + expect(result.limitPrice).toBe(42000); + expect(result.maxBaseQuantity).toBe(100); + expect(result.maxQuoteQuantity).toBe(4_200_000); + expect(result.selfTradeBehavior).toBe('decrementTake'); + expect(result.orderType).toBe('limit'); + expect(result.clientId).toBe('12345'); + expect(result.limit).toBe('65535'); + }); +}); + +describe('decodeCancelSpotOrder', () => { + it('should decode orderId and side', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelSpotOrder, programId); + const result = decodeCancelSpotOrder(ix); + expect(result.side).toBe('sell'); + expect(result.orderId).toBe('42'); + }); +}); + +describe('decodePlacePerpOrder', () => { + it('should decode all fields', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder, programId); + const result = decodePlacePerpOrder(ix); + expect(result.price).toBe(50000); + expect(result.quantity).toBe(10); + expect(result.clientOrderId).toBe('99'); + expect(result.side).toBe('buy'); + expect(result.orderType).toBe('postOnly'); + // reduceOnly is a boolean in the layout, .toString() produces "false" + expect(result.reduceOnly).toBe('false'); + }); +}); + +describe('decodePlacePerpOrder2', () => { + it('should decode all fields', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder2, programId); + const result = decodePlacePerpOrder2(ix); + expect(result.price).toBe(60000); + expect(result.maxBaseQuantity).toBe(5); + expect(result.clientOrderId).toBe('200'); + expect(result.side).toBe('sell'); + expect(result.orderType).toBe('limit'); + // reduceOnly is a boolean in the layout, encoded as true + expect(result.reduceOnly).toBe('true'); + expect(result.expiryTimestamp).toBe(1_700_000_000); + }); +}); + +describe('decodeCancelPerpOrder', () => { + it('should decode orderId and invalidIdOk', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelPerpOrder, programId); + const result = decodeCancelPerpOrder(ix); + expect(result.orderId).toBe('42'); + // invalidIdOk is a boolean in the layout, .toString() produces "false" + expect(result.invalidIdOk).toBe('false'); + }); +}); + +// Both AddSpotMarket and AddPerpMarket layouts lack a marketIndex field (it comes +// from account keys), so the decoder throws when accessing .marketIndex.toNumber(). +describe.each([ + ['decodeAddSpotMarket', decodeAddSpotMarket, ENCODED_INSTRUCTIONS.AddSpotMarket], + ['decodeAddPerpMarket', decodeAddPerpMarket, ENCODED_INSTRUCTIONS.AddPerpMarket], +] as const)('%s', (_name, decodeFn, data) => { + it('should throw because layout does not include marketIndex field', () => { + const ix = makeInstruction(data, programId); + expect(() => decodeFn(ix)).toThrow(); + }); +}); + +describe('decodeChangePerpMarketParams', () => { + it('should decode option flags and values', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.ChangePerpMarketParams, programId); + const result = decodeChangePerpMarketParams(ix); + expect(result.maintLeverageOption).toBe(true); + expect(result.initLeverageOption).toBe(true); + expect(result.liquidationFeeOption).toBe(false); + expect(result.makerFeeOption).toBe(true); + expect(result.takerFeeOption).toBe(true); + expect(result.rateOption).toBe(false); + expect(result.maxDepthBpsOption).toBe(false); + expect(result.targetPeriodLengthOption).toBe(false); + expect(result.mngoPerPeriodOption).toBe(false); + }); +}); diff --git a/packages/decoder-mango/src/__tests__/detection.spec.ts b/packages/decoder-mango/src/__tests__/detection.spec.ts new file mode 100644 index 000000000..6951c36cb --- /dev/null +++ b/packages/decoder-mango/src/__tests__/detection.spec.ts @@ -0,0 +1,52 @@ +import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; +import { describe, expect, it } from 'vitest'; + +import { isMangoInstruction, parseMangoInstructionTitle } from '../detection'; +import { ENCODED_INSTRUCTIONS, MANGO_PROGRAM_IDS, makeInstruction } from './fixtures'; + +describe('isMangoInstruction', () => { + it.each([ + ['mainnet', MANGO_PROGRAM_IDS.mainnet], + ['devnet', MANGO_PROGRAM_IDS.devnet], + ['testnet', MANGO_PROGRAM_IDS.testnet], + ] as const)('should return true for %s mango program ID', (_network, programId) => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, programId); + expect(isMangoInstruction(ix)).toBe(true); + }); + + it('should return false for System Program', () => { + const ix = new TransactionInstruction({ + data: Buffer.alloc(4), + keys: [], + programId: SystemProgram.programId, + }); + expect(isMangoInstruction(ix)).toBe(false); + }); + + it('should return false for unknown program ID', () => { + const ix = makeInstruction( + ENCODED_INSTRUCTIONS.Deposit, + new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), + ); + expect(isMangoInstruction(ix)).toBe(false); + }); +}); + +describe('parseMangoInstructionTitle', () => { + it.each([ + 'Deposit', + 'Withdraw', + 'AddToBasket', + 'PlaceSpotOrder', + 'CancelSpotOrder', + 'PlacePerpOrder', + 'PlacePerpOrder2', + 'CancelPerpOrder', + 'AddSpotMarket', + 'AddPerpMarket', + 'ChangePerpMarketParams', + ] as const)('should parse %s instruction', title => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS[title], MANGO_PROGRAM_IDS.mainnet); + expect(parseMangoInstructionTitle(ix)).toBe(title); + }); +}); diff --git a/packages/decoder-mango/src/__tests__/fixtures.ts b/packages/decoder-mango/src/__tests__/fixtures.ts new file mode 100644 index 000000000..173406f61 --- /dev/null +++ b/packages/decoder-mango/src/__tests__/fixtures.ts @@ -0,0 +1,140 @@ +import { encodeMangoInstruction } from '@blockworks-foundation/mango-client'; +import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import BN from 'bn.js'; + +/** Known Mango v3 program IDs from Config.ids() */ +export const MANGO_PROGRAM_IDS = { + devnet: new PublicKey('4skJ85cdxQAFVKbcGgfun8iZPL7BadVYXG3kGEGkufqA'), + mainnet: new PublicKey('mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68'), + testnet: new PublicKey('BXhdkETgbHrr5QmVBT1xbz3JrMM28u5djbVtmTUfmFTH'), +}; + +/** Known spot market from mainnet.1 group */ +export const SPOT_MARKETS = { + 'MNGO/USDC': { + marketIndex: 0, + publicKey: new PublicKey('3d4rzwpy9iGdCZvgxcu7B1YocYffVLsQXPXkBZKt2zLc'), + }, + 'BTC/USDC': { + marketIndex: 1, + publicKey: new PublicKey('A8YFbxQYFVqKZaoYJLLUVcQiWP7G2MeEgW5wsAQgMvFw'), + }, +}; + +/** Known perp market from mainnet.1 group */ +export const PERP_MARKETS = { + 'MNGO-PERP': { + marketIndex: 0, + publicKey: new PublicKey('4nfmQP3KmUqEJ6qJLsS3offKgE96YUB4Rp7UQvm2Fbi9'), + }, + 'BTC-PERP': { + marketIndex: 1, + publicKey: new PublicKey('DtEcjPLyD4YtTBB4q8xwFZ9q49W89xZCZtJyrGebi5t8'), + }, +}; + +/** + * Pre-encoded instruction data for each Mango instruction type, + * built via encodeMangoInstruction() from @blockworks-foundation/mango-client. + */ +export const ENCODED_INSTRUCTIONS = { + Deposit: encodeMangoInstruction({ Deposit: { quantity: new BN(1_000_000) } }), + Withdraw: encodeMangoInstruction({ Withdraw: { quantity: new BN(500_000), allowBorrow: new BN(1) } }), + AddToBasket: encodeMangoInstruction({ AddToBasket: { marketIndex: new BN(2) } }), + PlaceSpotOrder: encodeMangoInstruction({ + PlaceSpotOrder: { + side: 'buy', + limitPrice: new BN(42_000), + maxBaseQuantity: new BN(100), + maxQuoteQuantity: new BN(4_200_000), + selfTradeBehavior: 'decrementTake', + orderType: 'limit', + clientId: new BN(12_345), + limit: 65_535, + }, + }), + CancelSpotOrder: encodeMangoInstruction({ CancelSpotOrder: { side: 'sell', orderId: new BN(42) } }), + PlacePerpOrder: encodeMangoInstruction({ + PlacePerpOrder: { + price: new BN(50_000), + quantity: new BN(10), + clientOrderId: new BN(99), + side: 'buy', + orderType: 'postOnly', + reduceOnly: false, + }, + }), + PlacePerpOrder2: encodeMangoInstruction({ + PlacePerpOrder2: { + price: new BN(60_000), + maxBaseQuantity: new BN(5), + maxQuoteQuantity: new BN(0), + clientOrderId: new BN(200), + expiryTimestamp: new BN(1_700_000_000), + side: 'sell', + orderType: 'limit', + reduceOnly: true, + limit: 0, + }, + }), + CancelPerpOrder: encodeMangoInstruction({ CancelPerpOrder: { orderId: new BN(42), invalidIdOk: false } }), + AddSpotMarket: encodeMangoInstruction({ + AddSpotMarket: { + maintLeverage: new BN(10), + initLeverage: new BN(5), + liquidationFee: new BN(100), + optimalUtil: new BN(70), + optimalRate: new BN(80), + maxRate: new BN(150), + }, + }), + AddPerpMarket: encodeMangoInstruction({ + AddPerpMarket: { + maintLeverage: new BN(20), + initLeverage: new BN(10), + liquidationFee: new BN(50), + makerFee: new BN(5), + takerFee: new BN(10), + baseLotSize: new BN(100), + quoteLotSize: new BN(10), + rate: new BN(30), + maxDepthBps: new BN(200), + targetPeriodLength: new BN(3600), + mngoPerPeriod: new BN(250), + exp: 0, + }, + }), + ChangePerpMarketParams: encodeMangoInstruction({ + ChangePerpMarketParams: { + maintLeverageOption: true, + maintLeverage: new BN(20), + initLeverageOption: true, + initLeverage: new BN(10), + liquidationFeeOption: false, + liquidationFee: new BN(0), + makerFeeOption: true, + makerFee: new BN(5), + takerFeeOption: true, + takerFee: new BN(10), + rateOption: false, + rate: new BN(0), + maxDepthBpsOption: false, + maxDepthBps: new BN(0), + targetPeriodLengthOption: false, + targetPeriodLength: new BN(0), + mngoPerPeriodOption: false, + mngoPerPeriod: new BN(0), + expOption: false, + exp: 0, + }, + }), +}; + +/** Create a TransactionInstruction from encoded data and a program ID */ +export function makeInstruction(data: Buffer, programId: PublicKey, keys: PublicKey[] = []): TransactionInstruction { + return new TransactionInstruction({ + data, + keys: keys.map(pubkey => ({ isSigner: false, isWritable: false, pubkey })), + programId, + }); +} diff --git a/packages/decoder-mango/src/__tests__/market.spec.ts b/packages/decoder-mango/src/__tests__/market.spec.ts new file mode 100644 index 000000000..598d771c4 --- /dev/null +++ b/packages/decoder-mango/src/__tests__/market.spec.ts @@ -0,0 +1,105 @@ +import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; +import { describe, expect, it } from 'vitest'; + +import { getSpotMarketFromInstruction, getPerpMarketFromInstruction, spotMarketFromIndex } from '../market'; +import { MANGO_PROGRAM_IDS, SPOT_MARKETS, PERP_MARKETS, ENCODED_INSTRUCTIONS, makeInstruction } from './fixtures'; + +describe('getSpotMarketFromInstruction', () => { + it('should return spot market config for known market pubkey', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + const spotMarketMeta = { + isSigner: false, + isWritable: false, + pubkey: SPOT_MARKETS['MNGO/USDC'].publicKey, + }; + + const result = getSpotMarketFromInstruction(ix, spotMarketMeta); + + expect(result).toBeDefined(); + expect(result!.name).toBe('MNGO/USDC'); + expect(result!.publicKey.equals(SPOT_MARKETS['MNGO/USDC'].publicKey)).toBe(true); + }); + + it('should return undefined for unknown spot market pubkey', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + const unknownMarket = { + isSigner: false, + isWritable: false, + pubkey: PublicKey.default, + }; + + const result = getSpotMarketFromInstruction(ix, unknownMarket); + expect(result).toBeUndefined(); + }); + + it('should return undefined for non-mango program ID', () => { + const ix = new TransactionInstruction({ + data: Buffer.alloc(12), + keys: [], + programId: SystemProgram.programId, + }); + const spotMarketMeta = { + isSigner: false, + isWritable: false, + pubkey: SPOT_MARKETS['MNGO/USDC'].publicKey, + }; + + const result = getSpotMarketFromInstruction(ix, spotMarketMeta); + expect(result).toBeUndefined(); + }); +}); + +describe('getPerpMarketFromInstruction', () => { + it('should return perp market config for known market pubkey', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + const perpMarketMeta = { + isSigner: false, + isWritable: false, + pubkey: PERP_MARKETS['BTC-PERP'].publicKey, + }; + + const result = getPerpMarketFromInstruction(ix, perpMarketMeta); + + expect(result).toBeDefined(); + expect(result!.name).toBe('BTC-PERP'); + expect(result!.publicKey.equals(PERP_MARKETS['BTC-PERP'].publicKey)).toBe(true); + }); + + it('should return undefined for unknown perp market pubkey', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + const unknownMarket = { + isSigner: false, + isWritable: false, + pubkey: PublicKey.default, + }; + + const result = getPerpMarketFromInstruction(ix, unknownMarket); + expect(result).toBeUndefined(); + }); +}); + +describe('spotMarketFromIndex', () => { + it.each([ + [0, 'MNGO/USDC'], + [1, 'BTC/USDC'], + ] as const)('should return %s market name for index %i', (index, expectedName) => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + expect(spotMarketFromIndex(ix, index)).toBe(expectedName); + }); + + it('should return UNKNOWN for non-existent market index', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, MANGO_PROGRAM_IDS.mainnet); + const result = spotMarketFromIndex(ix, 999); + expect(result).toBe('UNKNOWN'); + }); + + it('should return undefined for non-mango program ID', () => { + const ix = new TransactionInstruction({ + data: Buffer.alloc(12), + keys: [], + programId: SystemProgram.programId, + }); + const result = spotMarketFromIndex(ix, 0); + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/decoder-mango/src/config.ts b/packages/decoder-mango/src/config.ts new file mode 100644 index 000000000..334a1458d --- /dev/null +++ b/packages/decoder-mango/src/config.ts @@ -0,0 +1,12 @@ +import { Config, GroupConfig } from '@blockworks-foundation/mango-client'; +import { PublicKey } from '@solana/web3.js'; + +// note: mainnet.1 suffices since its a superset of mainnet.0 +export const mangoGroups = Config.ids().groups.filter(group => group.name !== 'mainnet.0'); + +export function findGroupConfig(programId: PublicKey): GroupConfig | undefined { + const filtered = mangoGroups.filter(group => group.mangoProgramId.equals(programId)); + if (filtered.length) { + return filtered[0]; + } +} diff --git a/app/components/instruction/mango/types.ts b/packages/decoder-mango/src/decoder.ts similarity index 68% rename from app/components/instruction/mango/types.ts rename to packages/decoder-mango/src/decoder.ts index 8752d4632..d81f82abb 100644 --- a/app/components/instruction/mango/types.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -1,47 +1,5 @@ -import { - Config, - GroupConfig, - MangoInstructionLayout, - PerpMarket, - PerpMarketConfig, - PerpMarketLayout, - SpotMarketConfig, -} from '@blockworks-foundation/mango-client'; -import { Market } from '@project-serum/serum'; -import { AccountInfo, AccountMeta, Connection, PublicKey, TransactionInstruction } from '@solana/web3.js'; - -import { Logger } from '@/app/shared/lib/logger'; - -// note: mainnet.1 suffices since its a superset of mainnet.0 -const mangoGroups = Config.ids().groups.filter(group => group.name !== 'mainnet.0'); - -// caching of account info's by public keys -const accountInfoCache: Record | null>> = {}; -function getAccountInfo(clusterUrl: string, publicKey: PublicKey): Promise | null> { - if (publicKey.toBase58() in accountInfoCache) { - return accountInfoCache[publicKey.toBase58()]; - } - const connection = new Connection(clusterUrl); - const accountInfoPromise = connection.getAccountInfo(publicKey); - accountInfoCache[publicKey.toBase58()] = accountInfoPromise; - return accountInfoPromise; -} - -function findGroupConfig(programId: PublicKey): GroupConfig | undefined { - const filtered = mangoGroups.filter(group => group.mangoProgramId.equals(programId)); - if (filtered.length) { - return filtered[0]; - } -} - -export const isMangoInstruction = (instruction: TransactionInstruction) => { - return mangoGroups.map(group => group.mangoProgramId.toBase58()).includes(instruction.programId.toBase58()); -}; - -export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { - const decodedInstruction = MangoInstructionLayout.decode(instruction.data, 0); - return Object.keys(decodedInstruction)[0]; -}; +import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; +import { TransactionInstruction } from '@solana/web3.js'; export type Deposit = { quantity: number; @@ -292,83 +250,3 @@ export type OrderLotDetails = { price: number; size: number; }; - -//// - -export function logAllKeys(keys: AccountMeta[]) { - Logger.debug('[components:ix-mango] Account keys', { keys: keys.map(k => k.pubkey.toBase58()) }); -} - -export function getSpotMarketFromInstruction( - ix: TransactionInstruction, - spotMarket: AccountMeta, -): SpotMarketConfig | undefined { - const groupConfig = findGroupConfig(ix.programId); - if (groupConfig === undefined) { - return; - } - const spotMarketConfigs = groupConfig.spotMarkets.filter(mangoSpotMarket => - spotMarket.pubkey.equals(mangoSpotMarket.publicKey), - ); - if (spotMarketConfigs.length) { - return spotMarketConfigs[0]; - } -} - -export async function getSpotMarketFromSpotMarketConfig( - programId: PublicKey, - clusterUrl: string, - mangoSpotMarketConfig: SpotMarketConfig, -): Promise { - const connection = new Connection(clusterUrl); - const groupConfig = findGroupConfig(programId); - if (groupConfig === undefined) { - return; - } - return await Market.load(connection, mangoSpotMarketConfig.publicKey, undefined, groupConfig.serumProgramId); -} - -export function getPerpMarketFromInstruction( - ix: TransactionInstruction, - perpMarket: AccountMeta, -): PerpMarketConfig | undefined { - const groupConfig = findGroupConfig(ix.programId); - if (groupConfig === undefined) { - return; - } - const perpMarketConfigs = groupConfig.perpMarkets.filter(mangoPerpMarket => - perpMarket.pubkey.equals(mangoPerpMarket.publicKey), - ); - if (perpMarketConfigs.length) { - return perpMarketConfigs[0]; - } -} - -export async function getPerpMarketFromPerpMarketConfig( - clusterUrl: string, - mangoPerpMarketConfig: PerpMarketConfig, -): Promise { - const acc = await getAccountInfo(clusterUrl, mangoPerpMarketConfig.publicKey); - const decoded = PerpMarketLayout.decode(acc?.data); - - return new PerpMarket( - mangoPerpMarketConfig.publicKey, - mangoPerpMarketConfig.baseDecimals, - mangoPerpMarketConfig.quoteDecimals, - decoded, - ); -} - -export function spotMarketFromIndex(ix: TransactionInstruction, marketIndex: number): string | undefined { - const groupConfig = findGroupConfig(ix.programId); - if (groupConfig === undefined) { - return; - } - const spotMarketConfigs = groupConfig.spotMarkets.filter( - spotMarketConfig => spotMarketConfig.marketIndex === marketIndex, - ); - if (!spotMarketConfigs.length) { - return 'UNKNOWN'; - } - return spotMarketConfigs[0].name; -} diff --git a/packages/decoder-mango/src/detection.ts b/packages/decoder-mango/src/detection.ts new file mode 100644 index 000000000..ed05c8042 --- /dev/null +++ b/packages/decoder-mango/src/detection.ts @@ -0,0 +1,13 @@ +import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; +import { TransactionInstruction } from '@solana/web3.js'; + +import { mangoGroups } from './config'; + +export const isMangoInstruction = (instruction: TransactionInstruction) => { + return mangoGroups.map(group => group.mangoProgramId.toBase58()).includes(instruction.programId.toBase58()); +}; + +export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { + const decodedInstruction = MangoInstructionLayout.decode(instruction.data, 0); + return Object.keys(decodedInstruction)[0]; +}; diff --git a/packages/decoder-mango/src/index.ts b/packages/decoder-mango/src/index.ts new file mode 100644 index 000000000..f422733be --- /dev/null +++ b/packages/decoder-mango/src/index.ts @@ -0,0 +1,35 @@ +export { isMangoInstruction, parseMangoInstructionTitle } from './detection'; +export { + decodeAddPerpMarket, + decodeAddSpotMarket, + decodeAddToBasket, + decodeCancelPerpOrder, + decodeCancelSpotOrder, + decodeChangePerpMarketParams, + decodeDeposit, + decodePlacePerpOrder, + decodePlacePerpOrder2, + decodePlaceSpotOrder, + decodeWithdraw, +} from './decoder'; +export type { + AddPerpMarket, + AddSpotMarket, + AddToBasket, + CancelPerpOrder, + CancelSpotOrder, + ChangePerpMarketParams, + Deposit, + OrderLotDetails, + PlacePerpOrder, + PlacePerpOrder2, + PlaceSpotOrder, + Withdraw, +} from './decoder'; +export { + getPerpMarketFromInstruction, + getPerpMarketFromPerpMarketConfig, + getSpotMarketFromInstruction, + getSpotMarketFromSpotMarketConfig, + spotMarketFromIndex, +} from './market'; diff --git a/packages/decoder-mango/src/market.ts b/packages/decoder-mango/src/market.ts new file mode 100644 index 000000000..f0ad679bb --- /dev/null +++ b/packages/decoder-mango/src/market.ts @@ -0,0 +1,91 @@ +import { PerpMarket, PerpMarketConfig, PerpMarketLayout, SpotMarketConfig } from '@blockworks-foundation/mango-client'; +import { Market } from '@project-serum/serum'; +import { AccountInfo, AccountMeta, Connection, PublicKey, TransactionInstruction } from '@solana/web3.js'; + +import { findGroupConfig } from './config'; + +// caching of account info's by public keys +const accountInfoCache: Record | null>> = {}; +function getAccountInfo(clusterUrl: string, publicKey: PublicKey): Promise | null> { + if (publicKey.toBase58() in accountInfoCache) { + return accountInfoCache[publicKey.toBase58()]; + } + const connection = new Connection(clusterUrl); + const accountInfoPromise = connection.getAccountInfo(publicKey); + accountInfoCache[publicKey.toBase58()] = accountInfoPromise; + return accountInfoPromise; +} + +export function getSpotMarketFromInstruction( + ix: TransactionInstruction, + spotMarket: AccountMeta, +): SpotMarketConfig | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const spotMarketConfigs = groupConfig.spotMarkets.filter(mangoSpotMarket => + spotMarket.pubkey.equals(mangoSpotMarket.publicKey), + ); + if (spotMarketConfigs.length) { + return spotMarketConfigs[0]; + } +} + +export async function getSpotMarketFromSpotMarketConfig( + programId: PublicKey, + clusterUrl: string, + mangoSpotMarketConfig: SpotMarketConfig, +): Promise { + const connection = new Connection(clusterUrl); + const groupConfig = findGroupConfig(programId); + if (groupConfig === undefined) { + return; + } + return await Market.load(connection, mangoSpotMarketConfig.publicKey, undefined, groupConfig.serumProgramId); +} + +export function getPerpMarketFromInstruction( + ix: TransactionInstruction, + perpMarket: AccountMeta, +): PerpMarketConfig | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const perpMarketConfigs = groupConfig.perpMarkets.filter(mangoPerpMarket => + perpMarket.pubkey.equals(mangoPerpMarket.publicKey), + ); + if (perpMarketConfigs.length) { + return perpMarketConfigs[0]; + } +} + +export async function getPerpMarketFromPerpMarketConfig( + clusterUrl: string, + mangoPerpMarketConfig: PerpMarketConfig, +): Promise { + const acc = await getAccountInfo(clusterUrl, mangoPerpMarketConfig.publicKey); + const decoded = PerpMarketLayout.decode(acc?.data); + + return new PerpMarket( + mangoPerpMarketConfig.publicKey, + mangoPerpMarketConfig.baseDecimals, + mangoPerpMarketConfig.quoteDecimals, + decoded, + ); +} + +export function spotMarketFromIndex(ix: TransactionInstruction, marketIndex: number): string | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const spotMarketConfigs = groupConfig.spotMarkets.filter( + spotMarketConfig => spotMarketConfig.marketIndex === marketIndex, + ); + if (!spotMarketConfigs.length) { + return 'UNKNOWN'; + } + return spotMarketConfigs[0].name; +} diff --git a/packages/decoder-mango/tsconfig.json b/packages/decoder-mango/tsconfig.json new file mode 100644 index 000000000..f1489fdbc --- /dev/null +++ b/packages/decoder-mango/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "es2022", + "moduleResolution": "bundler", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/decoder-mango/vitest.config.ts b/packages/decoder-mango/vitest.config.ts new file mode 100644 index 000000000..d254ac481 --- /dev/null +++ b/packages/decoder-mango/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.spec.ts'], + }, +}); From 834c46e6402ac9dc8d14df91074ae1a46be2697d Mon Sep 17 00:00:00 2001 From: Sergo Date: Sat, 25 Apr 2026 16:51:37 +0100 Subject: [PATCH 03/17] feat: extract mango cards into separate feature --- app/components/account/TokenHistoryCard.tsx | 2 +- .../instruction-program-mango/index.ts | 1 + .../ui}/AddOracleDetailsCard.tsx | 3 +- .../ui}/AddPerpMarketDetailsCard.tsx | 5 +- .../ui}/AddSpotMarketDetailsCard.tsx | 5 +- .../ui}/CancelPerpOrderDetailsCard.tsx | 5 +- .../ui}/CancelSpotOrderDetailsCard.tsx | 5 +- .../ui}/ChangePerpMarketParamsDetailsCard.tsx | 10 +- .../ui}/ConsumeEventsDetailsCard.tsx | 5 +- .../ui}/GenericMngoAccountDetailsCard.tsx | 4 +- .../ui}/GenericPerpMngoDetailsCard.tsx | 5 +- .../ui}/GenericSpotMngoDetailsCard.tsx | 5 +- .../ui/MangoDetailsCard.tsx} | 38 ++--- .../ui}/PlacePerpOrder2DetailsCard.tsx | 11 +- .../ui}/PlacePerpOrderDetailsCard.tsx | 11 +- .../ui}/PlaceSpotOrderDetailsCard.tsx | 11 +- .../transaction/ui/InstructionsSection.tsx | 4 +- .../src/__tests__/config.spec.ts | 24 +-- .../src/__tests__/decoder.spec.ts | 2 +- .../src/__tests__/detection.spec.ts | 2 +- .../decoder-mango/src/__tests__/fixtures.ts | 156 +++++++++--------- .../src/__tests__/market.spec.ts | 4 +- 22 files changed, 151 insertions(+), 167 deletions(-) create mode 100644 app/features/instruction-program-mango/index.ts rename app/{components/instruction/mango => features/instruction-program-mango/ui}/AddOracleDetailsCard.tsx (88%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/AddPerpMarketDetailsCard.tsx (97%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/AddSpotMarketDetailsCard.tsx (97%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/CancelPerpOrderDetailsCard.tsx (96%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/CancelSpotOrderDetailsCard.tsx (96%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/ChangePerpMarketParamsDetailsCard.tsx (95%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/ConsumeEventsDetailsCard.tsx (95%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/GenericMngoAccountDetailsCard.tsx (93%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/GenericPerpMngoDetailsCard.tsx (96%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/GenericSpotMngoDetailsCard.tsx (96%) rename app/{components/instruction/MangoDetails.tsx => features/instruction-program-mango/ui/MangoDetailsCard.tsx} (77%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/PlacePerpOrder2DetailsCard.tsx (98%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/PlacePerpOrderDetailsCard.tsx (98%) rename app/{components/instruction/mango => features/instruction-program-mango/ui}/PlaceSpotOrderDetailsCard.tsx (98%) diff --git a/app/components/account/TokenHistoryCard.tsx b/app/components/account/TokenHistoryCard.tsx index 49aba6d25..0749f4f6b 100644 --- a/app/components/account/TokenHistoryCard.tsx +++ b/app/components/account/TokenHistoryCard.tsx @@ -5,13 +5,13 @@ import { ErrorCard } from '@components/common/ErrorCard'; import { LoadingCard } from '@components/common/LoadingCard'; import { Signature } from '@components/common/Signature'; import { Slot } from '@components/common/Slot'; -import { isMangoInstruction, parseMangoInstructionTitle } from '@explorer/decoder-mango'; import { isSerumInstruction, parseSerumInstructionTitle } from '@components/instruction/serum/types'; import { isTokenLendingInstruction, parseTokenLendingInstructionTitle, } from '@components/instruction/token-lending/types'; import { isTokenSwapInstruction, parseTokenSwapInstructionTitle } from '@components/instruction/token-swap/types'; +import { isMangoInstruction, parseMangoInstructionTitle } from '@explorer/decoder-mango'; import { isTokenProgramData } from '@providers/accounts'; import { useAccountHistories, useFetchAccountHistory } from '@providers/accounts/history'; import { isTokenProgramId, TokenInfoWithPubkey, useAccountOwnedTokens } from '@providers/accounts/tokens'; diff --git a/app/features/instruction-program-mango/index.ts b/app/features/instruction-program-mango/index.ts new file mode 100644 index 000000000..8fcca675c --- /dev/null +++ b/app/features/instruction-program-mango/index.ts @@ -0,0 +1 @@ +export { MangoDetailsCard } from './ui/MangoDetailsCard'; diff --git a/app/components/instruction/mango/AddOracleDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddOracleDetailsCard.tsx similarity index 88% rename from app/components/instruction/mango/AddOracleDetailsCard.tsx rename to app/features/instruction-program-mango/ui/AddOracleDetailsCard.tsx index 31130be08..c4ce158ca 100644 --- a/app/components/instruction/mango/AddOracleDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddOracleDetailsCard.tsx @@ -1,7 +1,6 @@ +import { InstructionCard } from '@components/instruction/InstructionCard'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -import { InstructionCard } from '../InstructionCard'; - export function AddOracleDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx similarity index 97% rename from app/components/instruction/mango/AddPerpMarketDetailsCard.tsx rename to app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx index ae4ce7b3b..a16a9ffb5 100644 --- a/app/components/instruction/mango/AddPerpMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx @@ -1,11 +1,10 @@ +import { InstructionCard } from '@components/instruction/InstructionCard'; +import { AddPerpMarket } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { formatDuration } from '@utils/date'; import { BaseTable } from '@/app/shared/ui/Table'; -import { InstructionCard } from '../InstructionCard'; -import { AddPerpMarket } from '@explorer/decoder-mango'; - export function AddPerpMarketDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx similarity index 97% rename from app/components/instruction/mango/AddSpotMarketDetailsCard.tsx rename to app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx index 8d7c3941a..22d65898b 100644 --- a/app/components/instruction/mango/AddSpotMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx @@ -1,9 +1,8 @@ -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { AddSpotMarket, spotMarketFromIndex } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function AddSpotMarketDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx similarity index 96% rename from app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx rename to app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx index 76731c93e..688ff68be 100644 --- a/app/components/instruction/mango/CancelPerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx @@ -1,10 +1,9 @@ import { Address } from '@components/common/Address'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { CancelPerpOrder, getPerpMarketFromInstruction } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function CancelPerpOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx similarity index 96% rename from app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx rename to app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx index d9dec0ab4..8200eb7cc 100644 --- a/app/components/instruction/mango/CancelSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx @@ -1,10 +1,9 @@ import { Address } from '@components/common/Address'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { CancelSpotOrder, getSpotMarketFromInstruction } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function CancelSpotOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx similarity index 95% rename from app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx rename to app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx index 83a42e0c5..447fdeb59 100644 --- a/app/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx @@ -1,13 +1,15 @@ +import { InstructionCard } from '@components/instruction/InstructionCard'; +import { + ChangePerpMarketParams, + getPerpMarketFromInstruction, + getPerpMarketFromPerpMarketConfig, +} from '@explorer/decoder-mango'; import { useCluster } from '@providers/cluster'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { formatDuration } from '@utils/date'; import { useEffect, useState } from 'react'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; -import { ChangePerpMarketParams, getPerpMarketFromInstruction, getPerpMarketFromPerpMarketConfig } from '@explorer/decoder-mango'; - export function ChangePerpMarketParamsDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx b/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx similarity index 95% rename from app/components/instruction/mango/ConsumeEventsDetailsCard.tsx rename to app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx index 9b3da122a..d33fecfed 100644 --- a/app/components/instruction/mango/ConsumeEventsDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx @@ -1,10 +1,9 @@ import { Address } from '@components/common/Address'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function ConsumeEventsDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/GenericMngoAccountDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx similarity index 93% rename from app/components/instruction/mango/GenericMngoAccountDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx index 35555ceae..e82593db6 100644 --- a/app/components/instruction/mango/GenericMngoAccountDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx @@ -1,10 +1,8 @@ import { Address } from '@components/common/Address'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; - export function GenericMngoAccountDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx similarity index 96% rename from app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx index b5608e351..f08f7e4dd 100644 --- a/app/components/instruction/mango/GenericPerpMngoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx @@ -1,10 +1,9 @@ import { Address } from '@components/common/Address'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function GenericPerpMngoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx similarity index 96% rename from app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx index a5e8f5a09..1c3d8f919 100644 --- a/app/components/instruction/mango/GenericSpotMngoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx @@ -1,10 +1,9 @@ import { Address } from '@components/common/Address'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getSpotMarketFromInstruction } from '@explorer/decoder-mango'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; export function GenericSpotMngoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/MangoDetails.tsx b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx similarity index 77% rename from app/components/instruction/MangoDetails.tsx rename to app/features/instruction-program-mango/ui/MangoDetailsCard.tsx index 0a789112c..05e78ad21 100644 --- a/app/components/instruction/MangoDetails.tsx +++ b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx @@ -1,22 +1,4 @@ -import { useCluster } from '@providers/cluster'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; - -import { Logger } from '@/app/shared/lib/logger'; - -import { InstructionCard } from './InstructionCard'; -import { AddOracleDetailsCard } from './mango/AddOracleDetailsCard'; -import { AddPerpMarketDetailsCard } from './mango/AddPerpMarketDetailsCard'; -import { AddSpotMarketDetailsCard } from './mango/AddSpotMarketDetailsCard'; -import { CancelPerpOrderDetailsCard } from './mango/CancelPerpOrderDetailsCard'; -import { CancelSpotOrderDetailsCard } from './mango/CancelSpotOrderDetailsCard'; -import { ChangePerpMarketParamsDetailsCard } from './mango/ChangePerpMarketParamsDetailsCard'; -import { ConsumeEventsDetailsCard } from './mango/ConsumeEventsDetailsCard'; -import { GenericMngoAccountDetailsCard } from './mango/GenericMngoAccountDetailsCard'; -import { GenericPerpMngoDetailsCard } from './mango/GenericPerpMngoDetailsCard'; -import { GenericSpotMngoDetailsCard } from './mango/GenericSpotMngoDetailsCard'; -import { PlacePerpOrder2DetailsCard } from './mango/PlacePerpOrder2DetailsCard'; -import { PlacePerpOrderDetailsCard } from './mango/PlacePerpOrderDetailsCard'; -import { PlaceSpotOrderDetailsCard } from './mango/PlaceSpotOrderDetailsCard'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { decodeAddPerpMarket, decodeAddSpotMarket, @@ -28,6 +10,24 @@ import { decodePlaceSpotOrder, parseMangoInstructionTitle, } from '@explorer/decoder-mango'; +import { useCluster } from '@providers/cluster'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; + +import { Logger } from '@/app/shared/lib/logger'; + +import { AddOracleDetailsCard } from './AddOracleDetailsCard'; +import { AddPerpMarketDetailsCard } from './AddPerpMarketDetailsCard'; +import { AddSpotMarketDetailsCard } from './AddSpotMarketDetailsCard'; +import { CancelPerpOrderDetailsCard } from './CancelPerpOrderDetailsCard'; +import { CancelSpotOrderDetailsCard } from './CancelSpotOrderDetailsCard'; +import { ChangePerpMarketParamsDetailsCard } from './ChangePerpMarketParamsDetailsCard'; +import { ConsumeEventsDetailsCard } from './ConsumeEventsDetailsCard'; +import { GenericMngoAccountDetailsCard } from './GenericMngoAccountDetailsCard'; +import { GenericPerpMngoDetailsCard } from './GenericPerpMngoDetailsCard'; +import { GenericSpotMngoDetailsCard } from './GenericSpotMngoDetailsCard'; +import { PlacePerpOrder2DetailsCard } from './PlacePerpOrder2DetailsCard'; +import { PlacePerpOrderDetailsCard } from './PlacePerpOrderDetailsCard'; +import { PlaceSpotOrderDetailsCard } from './PlaceSpotOrderDetailsCard'; export function MangoDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx similarity index 98% rename from app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx rename to app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx index 8eab313a5..46b53ba57 100644 --- a/app/components/instruction/mango/PlacePerpOrder2DetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx @@ -1,18 +1,17 @@ import { Address } from '@components/common/Address'; -import { useCluster } from '@providers/cluster'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getPerpMarketFromInstruction, getPerpMarketFromPerpMarketConfig, OrderLotDetails, PlacePerpOrder2, } from '@explorer/decoder-mango'; +import { useCluster } from '@providers/cluster'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import BN from 'bn.js'; +import { useEffect, useState } from 'react'; export function PlacePerpOrder2DetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx similarity index 98% rename from app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx rename to app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx index 07acaa0a7..1bd80717a 100644 --- a/app/components/instruction/mango/PlacePerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx @@ -1,18 +1,17 @@ import { Address } from '@components/common/Address'; -import { useCluster } from '@providers/cluster'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getPerpMarketFromInstruction, getPerpMarketFromPerpMarketConfig, OrderLotDetails, PlacePerpOrder, } from '@explorer/decoder-mango'; +import { useCluster } from '@providers/cluster'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import BN from 'bn.js'; +import { useEffect, useState } from 'react'; export function PlacePerpOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx similarity index 98% rename from app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx rename to app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx index f92605a0f..bb30d4375 100644 --- a/app/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx @@ -1,18 +1,17 @@ import { Address } from '@components/common/Address'; -import { useCluster } from '@providers/cluster'; -import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; - -import { InstructionCard } from '../InstructionCard'; import { getSpotMarketFromInstruction, getSpotMarketFromSpotMarketConfig, OrderLotDetails, PlaceSpotOrder, } from '@explorer/decoder-mango'; +import { useCluster } from '@providers/cluster'; +import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import BN from 'bn.js'; +import { useEffect, useState } from 'react'; export function PlaceSpotOrderDetailsCard(props: { ix: TransactionInstruction; diff --git a/app/features/transaction/ui/InstructionsSection.tsx b/app/features/transaction/ui/InstructionsSection.tsx index 4019366ab..b80122db6 100644 --- a/app/features/transaction/ui/InstructionsSection.tsx +++ b/app/features/transaction/ui/InstructionsSection.tsx @@ -65,8 +65,8 @@ import { useProgramMetadataIdl } from '@/app/entities/program-metadata'; import { CollapsibleSection } from './CollapsibleSection'; const MangoDetailsCard = dynamic( - () => import('@components/instruction/MangoDetails').then(mod => mod.MangoDetailsCard), - { ssr: false } + () => import('@features/instruction-program-mango').then(mod => mod.MangoDetailsCard), + { ssr: false }, ); export type InstructionDetailsProps = { diff --git a/packages/decoder-mango/src/__tests__/config.spec.ts b/packages/decoder-mango/src/__tests__/config.spec.ts index fc5c852ae..3f08834eb 100644 --- a/packages/decoder-mango/src/__tests__/config.spec.ts +++ b/packages/decoder-mango/src/__tests__/config.spec.ts @@ -13,24 +13,18 @@ describe('mangoGroups', () => { expect(mangoGroups.some(g => g.name === 'mainnet.1')).toBe(true); }); - it.each(['mainnet', 'devnet', 'testnet'] as const)( - 'should contain a group with the %s program ID', - (network) => { - const id = MANGO_PROGRAM_IDS[network].toBase58(); - expect(mangoGroups.some(g => g.mangoProgramId.toBase58() === id)).toBe(true); - } - ); + it.each(['mainnet', 'devnet', 'testnet'] as const)('should contain a group with the %s program ID', network => { + const id = MANGO_PROGRAM_IDS[network].toBase58(); + expect(mangoGroups.some(g => g.mangoProgramId.toBase58() === id)).toBe(true); + }); }); describe('findGroupConfig', () => { - it.each(['mainnet', 'devnet', 'testnet'] as const)( - 'should return a group config for %s program ID', - (network) => { - const result = findGroupConfig(MANGO_PROGRAM_IDS[network]); - expect(result).toBeDefined(); - expect(result!.mangoProgramId.equals(MANGO_PROGRAM_IDS[network])).toBe(true); - } - ); + it.each(['mainnet', 'devnet', 'testnet'] as const)('should return a group config for %s program ID', network => { + const result = findGroupConfig(MANGO_PROGRAM_IDS[network]); + expect(result).toBeDefined(); + expect(result!.mangoProgramId.equals(MANGO_PROGRAM_IDS[network])).toBe(true); + }); it('should return undefined for an unknown program ID', () => { expect(findGroupConfig(PublicKey.default)).toBeUndefined(); diff --git a/packages/decoder-mango/src/__tests__/decoder.spec.ts b/packages/decoder-mango/src/__tests__/decoder.spec.ts index 1748b6de6..39ba4dc65 100644 --- a/packages/decoder-mango/src/__tests__/decoder.spec.ts +++ b/packages/decoder-mango/src/__tests__/decoder.spec.ts @@ -13,7 +13,7 @@ import { decodePlaceSpotOrder, decodeWithdraw, } from '../decoder'; -import { ENCODED_INSTRUCTIONS, MANGO_PROGRAM_IDS, makeInstruction } from './fixtures'; +import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS } from './fixtures'; const programId = MANGO_PROGRAM_IDS.mainnet; diff --git a/packages/decoder-mango/src/__tests__/detection.spec.ts b/packages/decoder-mango/src/__tests__/detection.spec.ts index 6951c36cb..57c721692 100644 --- a/packages/decoder-mango/src/__tests__/detection.spec.ts +++ b/packages/decoder-mango/src/__tests__/detection.spec.ts @@ -2,7 +2,7 @@ import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.j import { describe, expect, it } from 'vitest'; import { isMangoInstruction, parseMangoInstructionTitle } from '../detection'; -import { ENCODED_INSTRUCTIONS, MANGO_PROGRAM_IDS, makeInstruction } from './fixtures'; +import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS } from './fixtures'; describe('isMangoInstruction', () => { it.each([ diff --git a/packages/decoder-mango/src/__tests__/fixtures.ts b/packages/decoder-mango/src/__tests__/fixtures.ts index 173406f61..3f8f5cae0 100644 --- a/packages/decoder-mango/src/__tests__/fixtures.ts +++ b/packages/decoder-mango/src/__tests__/fixtures.ts @@ -11,26 +11,26 @@ export const MANGO_PROGRAM_IDS = { /** Known spot market from mainnet.1 group */ export const SPOT_MARKETS = { - 'MNGO/USDC': { - marketIndex: 0, - publicKey: new PublicKey('3d4rzwpy9iGdCZvgxcu7B1YocYffVLsQXPXkBZKt2zLc'), - }, 'BTC/USDC': { marketIndex: 1, publicKey: new PublicKey('A8YFbxQYFVqKZaoYJLLUVcQiWP7G2MeEgW5wsAQgMvFw'), }, + 'MNGO/USDC': { + marketIndex: 0, + publicKey: new PublicKey('3d4rzwpy9iGdCZvgxcu7B1YocYffVLsQXPXkBZKt2zLc'), + }, }; /** Known perp market from mainnet.1 group */ export const PERP_MARKETS = { - 'MNGO-PERP': { - marketIndex: 0, - publicKey: new PublicKey('4nfmQP3KmUqEJ6qJLsS3offKgE96YUB4Rp7UQvm2Fbi9'), - }, 'BTC-PERP': { marketIndex: 1, publicKey: new PublicKey('DtEcjPLyD4YtTBB4q8xwFZ9q49W89xZCZtJyrGebi5t8'), }, + 'MNGO-PERP': { + marketIndex: 0, + publicKey: new PublicKey('4nfmQP3KmUqEJ6qJLsS3offKgE96YUB4Rp7UQvm2Fbi9'), + }, }; /** @@ -38,96 +38,96 @@ export const PERP_MARKETS = { * built via encodeMangoInstruction() from @blockworks-foundation/mango-client. */ export const ENCODED_INSTRUCTIONS = { - Deposit: encodeMangoInstruction({ Deposit: { quantity: new BN(1_000_000) } }), - Withdraw: encodeMangoInstruction({ Withdraw: { quantity: new BN(500_000), allowBorrow: new BN(1) } }), + AddPerpMarket: encodeMangoInstruction({ + AddPerpMarket: { + baseLotSize: new BN(100), + exp: 0, + initLeverage: new BN(10), + liquidationFee: new BN(50), + maintLeverage: new BN(20), + makerFee: new BN(5), + maxDepthBps: new BN(200), + mngoPerPeriod: new BN(250), + quoteLotSize: new BN(10), + rate: new BN(30), + takerFee: new BN(10), + targetPeriodLength: new BN(3600), + }, + }), + AddSpotMarket: encodeMangoInstruction({ + AddSpotMarket: { + initLeverage: new BN(5), + liquidationFee: new BN(100), + maintLeverage: new BN(10), + maxRate: new BN(150), + optimalRate: new BN(80), + optimalUtil: new BN(70), + }, + }), AddToBasket: encodeMangoInstruction({ AddToBasket: { marketIndex: new BN(2) } }), - PlaceSpotOrder: encodeMangoInstruction({ - PlaceSpotOrder: { - side: 'buy', - limitPrice: new BN(42_000), - maxBaseQuantity: new BN(100), - maxQuoteQuantity: new BN(4_200_000), - selfTradeBehavior: 'decrementTake', - orderType: 'limit', - clientId: new BN(12_345), - limit: 65_535, + CancelPerpOrder: encodeMangoInstruction({ CancelPerpOrder: { invalidIdOk: false, orderId: new BN(42) } }), + CancelSpotOrder: encodeMangoInstruction({ CancelSpotOrder: { orderId: new BN(42), side: 'sell' } }), + ChangePerpMarketParams: encodeMangoInstruction({ + ChangePerpMarketParams: { + exp: 0, + expOption: false, + initLeverage: new BN(10), + initLeverageOption: true, + liquidationFee: new BN(0), + liquidationFeeOption: false, + maintLeverage: new BN(20), + maintLeverageOption: true, + makerFee: new BN(5), + makerFeeOption: true, + maxDepthBps: new BN(0), + maxDepthBpsOption: false, + mngoPerPeriod: new BN(0), + mngoPerPeriodOption: false, + rate: new BN(0), + rateOption: false, + takerFee: new BN(10), + takerFeeOption: true, + targetPeriodLength: new BN(0), + targetPeriodLengthOption: false, }, }), - CancelSpotOrder: encodeMangoInstruction({ CancelSpotOrder: { side: 'sell', orderId: new BN(42) } }), + Deposit: encodeMangoInstruction({ Deposit: { quantity: new BN(1_000_000) } }), PlacePerpOrder: encodeMangoInstruction({ PlacePerpOrder: { - price: new BN(50_000), - quantity: new BN(10), clientOrderId: new BN(99), - side: 'buy', orderType: 'postOnly', + price: new BN(50_000), + quantity: new BN(10), reduceOnly: false, + side: 'buy', }, }), PlacePerpOrder2: encodeMangoInstruction({ PlacePerpOrder2: { - price: new BN(60_000), - maxBaseQuantity: new BN(5), - maxQuoteQuantity: new BN(0), clientOrderId: new BN(200), expiryTimestamp: new BN(1_700_000_000), - side: 'sell', + limit: 0, + maxBaseQuantity: new BN(5), + maxQuoteQuantity: new BN(0), orderType: 'limit', + price: new BN(60_000), reduceOnly: true, - limit: 0, - }, - }), - CancelPerpOrder: encodeMangoInstruction({ CancelPerpOrder: { orderId: new BN(42), invalidIdOk: false } }), - AddSpotMarket: encodeMangoInstruction({ - AddSpotMarket: { - maintLeverage: new BN(10), - initLeverage: new BN(5), - liquidationFee: new BN(100), - optimalUtil: new BN(70), - optimalRate: new BN(80), - maxRate: new BN(150), - }, - }), - AddPerpMarket: encodeMangoInstruction({ - AddPerpMarket: { - maintLeverage: new BN(20), - initLeverage: new BN(10), - liquidationFee: new BN(50), - makerFee: new BN(5), - takerFee: new BN(10), - baseLotSize: new BN(100), - quoteLotSize: new BN(10), - rate: new BN(30), - maxDepthBps: new BN(200), - targetPeriodLength: new BN(3600), - mngoPerPeriod: new BN(250), - exp: 0, + side: 'sell', }, }), - ChangePerpMarketParams: encodeMangoInstruction({ - ChangePerpMarketParams: { - maintLeverageOption: true, - maintLeverage: new BN(20), - initLeverageOption: true, - initLeverage: new BN(10), - liquidationFeeOption: false, - liquidationFee: new BN(0), - makerFeeOption: true, - makerFee: new BN(5), - takerFeeOption: true, - takerFee: new BN(10), - rateOption: false, - rate: new BN(0), - maxDepthBpsOption: false, - maxDepthBps: new BN(0), - targetPeriodLengthOption: false, - targetPeriodLength: new BN(0), - mngoPerPeriodOption: false, - mngoPerPeriod: new BN(0), - expOption: false, - exp: 0, + PlaceSpotOrder: encodeMangoInstruction({ + PlaceSpotOrder: { + clientId: new BN(12_345), + limit: 65_535, + limitPrice: new BN(42_000), + maxBaseQuantity: new BN(100), + maxQuoteQuantity: new BN(4_200_000), + orderType: 'limit', + selfTradeBehavior: 'decrementTake', + side: 'buy', }, }), + Withdraw: encodeMangoInstruction({ Withdraw: { allowBorrow: new BN(1), quantity: new BN(500_000) } }), }; /** Create a TransactionInstruction from encoded data and a program ID */ diff --git a/packages/decoder-mango/src/__tests__/market.spec.ts b/packages/decoder-mango/src/__tests__/market.spec.ts index 598d771c4..e7dc84ae3 100644 --- a/packages/decoder-mango/src/__tests__/market.spec.ts +++ b/packages/decoder-mango/src/__tests__/market.spec.ts @@ -1,8 +1,8 @@ import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; import { describe, expect, it } from 'vitest'; -import { getSpotMarketFromInstruction, getPerpMarketFromInstruction, spotMarketFromIndex } from '../market'; -import { MANGO_PROGRAM_IDS, SPOT_MARKETS, PERP_MARKETS, ENCODED_INSTRUCTIONS, makeInstruction } from './fixtures'; +import { getPerpMarketFromInstruction, getSpotMarketFromInstruction, spotMarketFromIndex } from '../market'; +import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS, PERP_MARKETS, SPOT_MARKETS } from './fixtures'; describe('getSpotMarketFromInstruction', () => { it('should return spot market config for known market pubkey', () => { From fa7e2a60ecca976f22776668ce9b427aeeb5cb37 Mon Sep 17 00:00:00 2001 From: Sergo Date: Sat, 25 Apr 2026 17:19:06 +0100 Subject: [PATCH 04/17] feat: extract hooks and move data-specific fields into decoder package --- .../instruction-program-mango/const.ts | 2 + .../model/__tests__/use-mango-market.spec.tsx | 135 ++++++++++++++++++ .../model/use-mango-market.ts | 58 ++++++++ .../ui/CancelPerpOrderDetailsCard.tsx | 8 +- .../ui/CancelSpotOrderDetailsCard.tsx | 8 +- .../ui/ChangePerpMarketParamsDetailsCard.tsx | 30 +--- .../ui/ConsumeEventsDetailsCard.tsx | 10 +- .../ui/MangoDetailsCard.tsx | 3 +- .../ui/PlacePerpOrder2DetailsCard.tsx | 45 ++---- .../ui/PlacePerpOrderDetailsCard.tsx | 45 ++---- .../ui/PlaceSpotOrderDetailsCard.tsx | 52 ++----- .../src/__tests__/decoder.spec.ts | 40 ++++-- .../decoder-mango/src/__tests__/fixtures.ts | 4 +- packages/decoder-mango/src/decoder.ts | 32 ++++- packages/decoder-mango/src/index.ts | 4 + 15 files changed, 327 insertions(+), 149 deletions(-) create mode 100644 app/features/instruction-program-mango/const.ts create mode 100644 app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx create mode 100644 app/features/instruction-program-mango/model/use-mango-market.ts diff --git a/app/features/instruction-program-mango/const.ts b/app/features/instruction-program-mango/const.ts new file mode 100644 index 000000000..b06d87690 --- /dev/null +++ b/app/features/instruction-program-mango/const.ts @@ -0,0 +1,2 @@ +/** Mango Markets v3 program ID on mainnet-beta */ +export const MANGO_V3_PROGRAM_ID = 'mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68'; diff --git a/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx b/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx new file mode 100644 index 000000000..5bb3de389 --- /dev/null +++ b/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx @@ -0,0 +1,135 @@ +import { + getPerpMarketFromPerpMarketConfig, + getSpotMarketFromSpotMarketConfig, + Market, + PerpMarket, + PerpMarketConfig, + SpotMarketConfig, +} from '@explorer/decoder-mango'; +import { PublicKey } from '@solana/web3.js'; +import { renderHook, waitFor } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { MANGO_V3_PROGRAM_ID } from '../../const'; +import { useMangoPerpMarket, useMangoSpotMarket } from '../use-mango-market'; + +vi.mock('@providers/cluster', () => ({ + useCluster: vi.fn(() => ({ + url: 'https://mainnet.rpc.address', + })), +})); + +vi.mock('@explorer/decoder-mango', () => ({ + getPerpMarketFromPerpMarketConfig: vi.fn(), + getSpotMarketFromSpotMarketConfig: vi.fn(), +})); + +const perpMarketConfig = { publicKey: PublicKey.default } as unknown as PerpMarketConfig; +const spotMarketConfig = { publicKey: PublicKey.default } as unknown as SpotMarketConfig; +const programId = new PublicKey(MANGO_V3_PROGRAM_ID); + +describe('useMangoPerpMarket', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('returns null when config is undefined', () => { + const { result } = renderHook(() => useMangoPerpMarket(undefined)); + + expect(result.current).toBeNull(); + expect(getPerpMarketFromPerpMarketConfig).not.toHaveBeenCalled(); + }); + + it('returns the resolved perp market when config is provided', async () => { + const resolved = { name: 'BTC-PERP' } as unknown as PerpMarket; + vi.mocked(getPerpMarketFromPerpMarketConfig).mockResolvedValueOnce(resolved); + + const { result } = renderHook(() => useMangoPerpMarket(perpMarketConfig)); + + await waitFor(() => { + expect(result.current).toBe(resolved); + }); + expect(getPerpMarketFromPerpMarketConfig).toHaveBeenCalledWith('https://mainnet.rpc.address', perpMarketConfig); + }); + + it('returns null when resolution throws', async () => { + vi.mocked(getPerpMarketFromPerpMarketConfig).mockRejectedValueOnce(new Error('rpc failed')); + + const { result } = renderHook(() => useMangoPerpMarket(perpMarketConfig)); + + await waitFor(() => { + expect(getPerpMarketFromPerpMarketConfig).toHaveBeenCalled(); + }); + expect(result.current).toBeNull(); + }); + + it('resets to null when config transitions from defined to undefined', async () => { + const resolved = { name: 'BTC-PERP' } as unknown as PerpMarket; + vi.mocked(getPerpMarketFromPerpMarketConfig).mockResolvedValueOnce(resolved); + + const { result, rerender } = renderHook(({ config }) => useMangoPerpMarket(config), { + initialProps: { config: perpMarketConfig as PerpMarketConfig | undefined }, + }); + + await waitFor(() => { + expect(result.current).toBe(resolved); + }); + + rerender({ config: undefined }); + + await waitFor(() => { + expect(result.current).toBeNull(); + }); + }); +}); + +describe('useMangoSpotMarket', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('returns null when config is undefined', () => { + const { result } = renderHook(() => useMangoSpotMarket(programId, undefined)); + + expect(result.current).toBeNull(); + expect(getSpotMarketFromSpotMarketConfig).not.toHaveBeenCalled(); + }); + + it('returns the resolved spot market when config is provided', async () => { + const resolved = { address: 'spot' } as unknown as Market; + vi.mocked(getSpotMarketFromSpotMarketConfig).mockResolvedValueOnce(resolved); + + const { result } = renderHook(() => useMangoSpotMarket(programId, spotMarketConfig)); + + await waitFor(() => { + expect(result.current).toBe(resolved); + }); + expect(getSpotMarketFromSpotMarketConfig).toHaveBeenCalledWith( + programId, + 'https://mainnet.rpc.address', + spotMarketConfig, + ); + }); + + it('returns null when resolver yields undefined', async () => { + vi.mocked(getSpotMarketFromSpotMarketConfig).mockResolvedValueOnce(undefined); + + const { result } = renderHook(() => useMangoSpotMarket(programId, spotMarketConfig)); + + await waitFor(() => { + expect(getSpotMarketFromSpotMarketConfig).toHaveBeenCalled(); + }); + expect(result.current).toBeNull(); + }); + + it('returns null when resolution throws', async () => { + vi.mocked(getSpotMarketFromSpotMarketConfig).mockRejectedValueOnce(new Error('rpc failed')); + + const { result } = renderHook(() => useMangoSpotMarket(programId, spotMarketConfig)); + + await waitFor(() => { + expect(getSpotMarketFromSpotMarketConfig).toHaveBeenCalled(); + }); + expect(result.current).toBeNull(); + }); +}); diff --git a/app/features/instruction-program-mango/model/use-mango-market.ts b/app/features/instruction-program-mango/model/use-mango-market.ts new file mode 100644 index 000000000..e43158926 --- /dev/null +++ b/app/features/instruction-program-mango/model/use-mango-market.ts @@ -0,0 +1,58 @@ +import { + getPerpMarketFromPerpMarketConfig, + getSpotMarketFromSpotMarketConfig, + Market, + PerpMarket, + PerpMarketConfig, + SpotMarketConfig, +} from '@explorer/decoder-mango'; +import { useCluster } from '@providers/cluster'; +import { PublicKey } from '@solana/web3.js'; +import { useState } from 'react'; +import useAsyncEffect from 'use-async-effect'; + +export function useMangoPerpMarket(config: PerpMarketConfig | undefined): PerpMarket | null { + const { url } = useCluster(); + const [market, setMarket] = useState(null); + + useAsyncEffect( + async isMounted => { + if (config === undefined) { + if (isMounted()) setMarket(null); + return; + } + try { + const resolved = await getPerpMarketFromPerpMarketConfig(url, config); + if (isMounted()) setMarket(resolved); + } catch { + if (isMounted()) setMarket(null); + } + }, + [url, config], + ); + + return market; +} + +export function useMangoSpotMarket(programId: PublicKey, config: SpotMarketConfig | undefined): Market | null { + const { url } = useCluster(); + const [market, setMarket] = useState(null); + + useAsyncEffect( + async isMounted => { + if (config === undefined) { + if (isMounted()) setMarket(null); + return; + } + try { + const resolved = await getSpotMarketFromSpotMarketConfig(programId, url, config); + if (isMounted()) setMarket(resolved ?? null); + } catch { + if (isMounted()) setMarket(null); + } + }, + [url, programId, config], + ); + + return market; +} diff --git a/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx index 688ff68be..0c2c06eba 100644 --- a/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx @@ -14,9 +14,7 @@ export function CancelPerpOrderDetailsCard(props: { childIndex?: number; }) { const { ix, index, result, info, innerCards, childIndex } = props; - const mangoAccount = ix.keys[1]; - const perpMarketAccountMeta = ix.keys[3]; - const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, perpMarketAccountMeta); + const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); return ( Mango account -
+
@@ -44,7 +42,7 @@ export function CancelPerpOrderDetailsCard(props: { Perp market address -
+
diff --git a/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx index 8200eb7cc..a8628a99e 100644 --- a/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx @@ -14,9 +14,7 @@ export function CancelSpotOrderDetailsCard(props: { childIndex?: number; }) { const { ix, index, result, info, innerCards, childIndex } = props; - const mangoAccount = ix.keys[2]; - const spotMarketAccountMeta = ix.keys[4]; - const mangoSpotMarketConfig = getSpotMarketFromInstruction(ix, spotMarketAccountMeta); + const mangoSpotMarketConfig = getSpotMarketFromInstruction(ix, info.spotMarket); return ( Mango account -
+
@@ -44,7 +42,7 @@ export function CancelSpotOrderDetailsCard(props: { Spot market address -
+
diff --git a/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx index 447fdeb59..e6c49c755 100644 --- a/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx @@ -1,13 +1,9 @@ import { InstructionCard } from '@components/instruction/InstructionCard'; -import { - ChangePerpMarketParams, - getPerpMarketFromInstruction, - getPerpMarketFromPerpMarketConfig, -} from '@explorer/decoder-mango'; -import { useCluster } from '@providers/cluster'; +import { ChangePerpMarketParams, getPerpMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { formatDuration } from '@utils/date'; -import { useEffect, useState } from 'react'; + +import { useMangoPerpMarket } from '../model/use-mango-market'; import { BaseTable } from '@/app/shared/ui/Table'; export function ChangePerpMarketParamsDetailsCard(props: { @@ -20,23 +16,9 @@ export function ChangePerpMarketParamsDetailsCard(props: { }) { const { ix, index, result, info, innerCards, childIndex } = props; - const perpMarketAccountMeta = ix.keys[1]; - const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, perpMarketAccountMeta); - - const cluster = useCluster(); - const [targetPeriodLength, setTargetPeriodLength] = useState(null); - useEffect(() => { - async function getTargetPeriodLength() { - if (mangoPerpMarketConfig === undefined) { - return; - } - const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig(cluster.url, mangoPerpMarketConfig); - - setTargetPeriodLength(mangoPerpMarket.liquidityMiningInfo.targetPeriodLength.toNumber()); - } - - getTargetPeriodLength(); - }, [cluster.url, mangoPerpMarketConfig]); + const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); + const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); + const targetPeriodLength = perpMarket?.liquidityMiningInfo.targetPeriodLength.toNumber() ?? null; return ( Perp market address -
+
diff --git a/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx index 05e78ad21..aaa0a85b1 100644 --- a/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx @@ -5,6 +5,7 @@ import { decodeCancelPerpOrder, decodeCancelSpotOrder, decodeChangePerpMarketParams, + decodeConsumeEvents, decodePlacePerpOrder, decodePlacePerpOrder2, decodePlaceSpotOrder, @@ -65,7 +66,7 @@ export function MangoDetailsCard(props: { case 'PlacePerpOrder2': return ; case 'ConsumeEvents': - return ; + return ; case 'CancelPerpOrder': return ; case 'SettleFunds': diff --git a/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx index 46b53ba57..85948db30 100644 --- a/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx @@ -2,16 +2,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; -import { - getPerpMarketFromInstruction, - getPerpMarketFromPerpMarketConfig, - OrderLotDetails, - PlacePerpOrder2, -} from '@explorer/decoder-mango'; -import { useCluster } from '@providers/cluster'; +import { getPerpMarketFromInstruction, OrderLotDetails, PlacePerpOrder2 } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; + +import { useMangoPerpMarket } from '../model/use-mango-market'; export function PlacePerpOrder2DetailsCard(props: { ix: TransactionInstruction; @@ -22,27 +18,16 @@ export function PlacePerpOrder2DetailsCard(props: { childIndex?: number; }) { const { ix, index, result, info, innerCards, childIndex } = props; - const mangoAccount = ix.keys[1]; - const perpMarketAccountMeta = ix.keys[4]; - const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, perpMarketAccountMeta); + const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); + const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); - const cluster = useCluster(); - const [orderLotDetails, setOrderLotDetails] = useState(null); - useEffect(() => { - async function getOrderLotDetails() { - if (mangoPerpMarketConfig === undefined) { - return; - } - const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig(cluster.url, mangoPerpMarketConfig); - const maxBaseQuantity = mangoPerpMarket.baseLotsToNumber(new BN(info.maxBaseQuantity.toString())); - const limitPrice = mangoPerpMarket.priceLotsToNumber(new BN(info.price.toString())); - setOrderLotDetails({ - price: limitPrice, - size: maxBaseQuantity, - } as OrderLotDetails); - } - getOrderLotDetails(); - }, [cluster.url, info.maxBaseQuantity, info.price, mangoPerpMarketConfig]); + const orderLotDetails = useMemo(() => { + if (!perpMarket) return null; + return { + price: perpMarket.priceLotsToNumber(new BN(info.price.toString())), + size: perpMarket.baseLotsToNumber(new BN(info.maxBaseQuantity.toString())), + }; + }, [perpMarket, info.price, info.maxBaseQuantity]); return ( Mango account {' '} -
+
{mangoPerpMarketConfig !== undefined && ( @@ -69,7 +54,7 @@ export function PlacePerpOrder2DetailsCard(props: { Perp market address -
+
{info.clientOrderId !== '0' && ( diff --git a/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx index 1bd80717a..0ed4f235d 100644 --- a/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx @@ -2,16 +2,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; -import { - getPerpMarketFromInstruction, - getPerpMarketFromPerpMarketConfig, - OrderLotDetails, - PlacePerpOrder, -} from '@explorer/decoder-mango'; -import { useCluster } from '@providers/cluster'; +import { getPerpMarketFromInstruction, OrderLotDetails, PlacePerpOrder } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; + +import { useMangoPerpMarket } from '../model/use-mango-market'; export function PlacePerpOrderDetailsCard(props: { ix: TransactionInstruction; @@ -22,27 +18,16 @@ export function PlacePerpOrderDetailsCard(props: { childIndex?: number; }) { const { ix, index, result, info, innerCards, childIndex } = props; - const mangoAccount = ix.keys[1]; - const perpMarketAccountMeta = ix.keys[4]; - const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, perpMarketAccountMeta); + const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); + const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); - const cluster = useCluster(); - const [orderLotDetails, setOrderLotDetails] = useState(null); - useEffect(() => { - async function getOrderLotDetails() { - if (mangoPerpMarketConfig === undefined) { - return; - } - const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig(cluster.url, mangoPerpMarketConfig); - const maxBaseQuantity = mangoPerpMarket.baseLotsToNumber(new BN(info.quantity.toString())); - const limitPrice = mangoPerpMarket.priceLotsToNumber(new BN(info.price.toString())); - setOrderLotDetails({ - price: limitPrice, - size: maxBaseQuantity, - } as OrderLotDetails); - } - getOrderLotDetails(); - }, [cluster.url, info.quantity, info.price, mangoPerpMarketConfig]); + const orderLotDetails = useMemo(() => { + if (!perpMarket) return null; + return { + price: perpMarket.priceLotsToNumber(new BN(info.price.toString())), + size: perpMarket.baseLotsToNumber(new BN(info.quantity.toString())), + }; + }, [perpMarket, info.price, info.quantity]); return ( Mango account {' '} -
+
@@ -71,7 +56,7 @@ export function PlacePerpOrderDetailsCard(props: { Perp market address -
+
diff --git a/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx index bb30d4375..63738ddd7 100644 --- a/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx @@ -2,16 +2,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; import { BaseTable } from '@/app/shared/ui/Table'; -import { - getSpotMarketFromInstruction, - getSpotMarketFromSpotMarketConfig, - OrderLotDetails, - PlaceSpotOrder, -} from '@explorer/decoder-mango'; -import { useCluster } from '@providers/cluster'; +import { getSpotMarketFromInstruction, OrderLotDetails, PlaceSpotOrder } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; + +import { useMangoSpotMarket } from '../model/use-mango-market'; export function PlaceSpotOrderDetailsCard(props: { ix: TransactionInstruction; @@ -22,34 +18,16 @@ export function PlaceSpotOrderDetailsCard(props: { childIndex?: number; }) { const { ix, index, result, info, innerCards, childIndex } = props; - const mangoAccount = ix.keys[1]; - const spotMarketAccountMeta = ix.keys[5]; - const mangoSpotMarketConfig = getSpotMarketFromInstruction(ix, spotMarketAccountMeta); + const mangoSpotMarketConfig = getSpotMarketFromInstruction(ix, info.spotMarket); + const spotMarket = useMangoSpotMarket(ix.programId, mangoSpotMarketConfig); - const cluster = useCluster(); - const [orderLotDetails, setOrderLotDetails] = useState(null); - useEffect(() => { - async function getOrderLotDetails() { - if (mangoSpotMarketConfig === undefined) { - return; - } - const mangoSpotMarket = await getSpotMarketFromSpotMarketConfig( - ix.programId, - cluster.url, - mangoSpotMarketConfig, - ); - if (mangoSpotMarket === undefined) { - return; - } - const maxBaseQuantity = mangoSpotMarket.baseSizeLotsToNumber(new BN(info.maxBaseQuantity.toString())); - const limitPrice = mangoSpotMarket.priceLotsToNumber(new BN(info.limitPrice.toString())); - setOrderLotDetails({ - price: limitPrice, - size: maxBaseQuantity, - } as OrderLotDetails); - } - getOrderLotDetails(); - }, [cluster.url, info.maxBaseQuantity, info.limitPrice, ix.programId, mangoSpotMarketConfig]); + const orderLotDetails = useMemo(() => { + if (!spotMarket) return null; + return { + price: spotMarket.priceLotsToNumber(new BN(info.limitPrice.toString())), + size: spotMarket.baseSizeLotsToNumber(new BN(info.maxBaseQuantity.toString())), + }; + }, [spotMarket, info.limitPrice, info.maxBaseQuantity]); return ( Mango account {' '} -
+
@@ -78,7 +56,7 @@ export function PlaceSpotOrderDetailsCard(props: { Spot market address -
+
diff --git a/packages/decoder-mango/src/__tests__/decoder.spec.ts b/packages/decoder-mango/src/__tests__/decoder.spec.ts index 39ba4dc65..bb865abf2 100644 --- a/packages/decoder-mango/src/__tests__/decoder.spec.ts +++ b/packages/decoder-mango/src/__tests__/decoder.spec.ts @@ -7,13 +7,14 @@ import { decodeCancelPerpOrder, decodeCancelSpotOrder, decodeChangePerpMarketParams, + decodeConsumeEvents, decodeDeposit, decodePlacePerpOrder, decodePlacePerpOrder2, decodePlaceSpotOrder, decodeWithdraw, } from '../decoder'; -import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS } from './fixtures'; +import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS, TEST_KEYS } from './fixtures'; const programId = MANGO_PROGRAM_IDS.mainnet; @@ -44,7 +45,7 @@ describe('decodeAddToBasket', () => { describe('decodePlaceSpotOrder', () => { it('should decode all fields', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlaceSpotOrder, programId); + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlaceSpotOrder, programId, TEST_KEYS); const result = decodePlaceSpotOrder(ix); expect(result.side).toBe('buy'); expect(result.limitPrice).toBe(42000); @@ -54,21 +55,25 @@ describe('decodePlaceSpotOrder', () => { expect(result.orderType).toBe('limit'); expect(result.clientId).toBe('12345'); expect(result.limit).toBe('65535'); + expect(result.mangoAccount.pubkey).toEqual(TEST_KEYS[1]); + expect(result.spotMarket.pubkey).toEqual(TEST_KEYS[5]); }); }); describe('decodeCancelSpotOrder', () => { - it('should decode orderId and side', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelSpotOrder, programId); + it('should decode orderId, side, and account refs', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelSpotOrder, programId, TEST_KEYS); const result = decodeCancelSpotOrder(ix); expect(result.side).toBe('sell'); expect(result.orderId).toBe('42'); + expect(result.mangoAccount.pubkey).toEqual(TEST_KEYS[2]); + expect(result.spotMarket.pubkey).toEqual(TEST_KEYS[4]); }); }); describe('decodePlacePerpOrder', () => { it('should decode all fields', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder, programId); + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder, programId, TEST_KEYS); const result = decodePlacePerpOrder(ix); expect(result.price).toBe(50000); expect(result.quantity).toBe(10); @@ -77,12 +82,14 @@ describe('decodePlacePerpOrder', () => { expect(result.orderType).toBe('postOnly'); // reduceOnly is a boolean in the layout, .toString() produces "false" expect(result.reduceOnly).toBe('false'); + expect(result.mangoAccount.pubkey).toEqual(TEST_KEYS[1]); + expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[4]); }); }); describe('decodePlacePerpOrder2', () => { it('should decode all fields', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder2, programId); + const ix = makeInstruction(ENCODED_INSTRUCTIONS.PlacePerpOrder2, programId, TEST_KEYS); const result = decodePlacePerpOrder2(ix); expect(result.price).toBe(60000); expect(result.maxBaseQuantity).toBe(5); @@ -92,16 +99,20 @@ describe('decodePlacePerpOrder2', () => { // reduceOnly is a boolean in the layout, encoded as true expect(result.reduceOnly).toBe('true'); expect(result.expiryTimestamp).toBe(1_700_000_000); + expect(result.mangoAccount.pubkey).toEqual(TEST_KEYS[1]); + expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[4]); }); }); describe('decodeCancelPerpOrder', () => { - it('should decode orderId and invalidIdOk', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelPerpOrder, programId); + it('should decode orderId, invalidIdOk, and account refs', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.CancelPerpOrder, programId, TEST_KEYS); const result = decodeCancelPerpOrder(ix); expect(result.orderId).toBe('42'); // invalidIdOk is a boolean in the layout, .toString() produces "false" expect(result.invalidIdOk).toBe('false'); + expect(result.mangoAccount.pubkey).toEqual(TEST_KEYS[1]); + expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[3]); }); }); @@ -118,8 +129,8 @@ describe.each([ }); describe('decodeChangePerpMarketParams', () => { - it('should decode option flags and values', () => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS.ChangePerpMarketParams, programId); + it('should decode option flags, values, and perpMarket ref', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.ChangePerpMarketParams, programId, TEST_KEYS); const result = decodeChangePerpMarketParams(ix); expect(result.maintLeverageOption).toBe(true); expect(result.initLeverageOption).toBe(true); @@ -130,5 +141,14 @@ describe('decodeChangePerpMarketParams', () => { expect(result.maxDepthBpsOption).toBe(false); expect(result.targetPeriodLengthOption).toBe(false); expect(result.mngoPerPeriodOption).toBe(false); + expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[1]); + }); +}); + +describe('decodeConsumeEvents', () => { + it('should extract perpMarket account ref', () => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS.Deposit, programId, TEST_KEYS); + const result = decodeConsumeEvents(ix); + expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[2]); }); }); diff --git a/packages/decoder-mango/src/__tests__/fixtures.ts b/packages/decoder-mango/src/__tests__/fixtures.ts index 3f8f5cae0..1bf0f1705 100644 --- a/packages/decoder-mango/src/__tests__/fixtures.ts +++ b/packages/decoder-mango/src/__tests__/fixtures.ts @@ -1,5 +1,5 @@ import { encodeMangoInstruction } from '@blockworks-foundation/mango-client'; -import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; /** Known Mango v3 program IDs from Config.ids() */ @@ -138,3 +138,5 @@ export function makeInstruction(data: Buffer, programId: PublicKey, keys: Public programId, }); } + +export const TEST_KEYS: PublicKey[] = Array.from({ length: 7 }, () => Keypair.generate().publicKey); diff --git a/packages/decoder-mango/src/decoder.ts b/packages/decoder-mango/src/decoder.ts index d81f82abb..acffd44a8 100644 --- a/packages/decoder-mango/src/decoder.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -1,5 +1,5 @@ import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; -import { TransactionInstruction } from '@solana/web3.js'; +import { AccountMeta, TransactionInstruction } from '@solana/web3.js'; export type Deposit = { quantity: number; @@ -48,6 +48,8 @@ export type PlaceSpotOrder = { orderType: string; clientId: string; limit: string; + mangoAccount: AccountMeta; + spotMarket: AccountMeta; }; export const decodePlaceSpotOrder = (ix: TransactionInstruction): PlaceSpotOrder => { @@ -56,11 +58,13 @@ export const decodePlaceSpotOrder = (ix: TransactionInstruction): PlaceSpotOrder clientId: decoded.PlaceSpotOrder.clientId.toString(), limit: decoded.PlaceSpotOrder.limit.toString(), limitPrice: decoded.PlaceSpotOrder.limitPrice.toNumber(), + mangoAccount: ix.keys[1], maxBaseQuantity: decoded.PlaceSpotOrder.maxBaseQuantity.toNumber(), maxQuoteQuantity: decoded.PlaceSpotOrder.maxQuoteQuantity.toNumber(), orderType: decoded.PlaceSpotOrder.orderType.toString(), selfTradeBehavior: decoded.PlaceSpotOrder.selfTradeBehavior, side: decoded.PlaceSpotOrder.side.toString(), + spotMarket: ix.keys[5], }; return placeSpotOrder; @@ -69,13 +73,17 @@ export const decodePlaceSpotOrder = (ix: TransactionInstruction): PlaceSpotOrder export type CancelSpotOrder = { orderId: string; side: string; + mangoAccount: AccountMeta; + spotMarket: AccountMeta; }; export const decodeCancelSpotOrder = (ix: TransactionInstruction): CancelSpotOrder => { const decoded = MangoInstructionLayout.decode(ix.data, 0); const cancelSpotOrder: CancelSpotOrder = { + mangoAccount: ix.keys[2], orderId: decoded.CancelSpotOrder.orderId.toString(), side: decoded.CancelSpotOrder.side.toString(), + spotMarket: ix.keys[4], }; return cancelSpotOrder; }; @@ -87,13 +95,17 @@ export type PlacePerpOrder = { side: string; orderType: string; reduceOnly: string; + mangoAccount: AccountMeta; + perpMarket: AccountMeta; }; export const decodePlacePerpOrder = (ix: TransactionInstruction): PlacePerpOrder => { const decoded = MangoInstructionLayout.decode(ix.data, 0); const placePerpOrder: PlacePerpOrder = { clientOrderId: decoded.PlacePerpOrder.clientOrderId.toString(), + mangoAccount: ix.keys[1], orderType: decoded.PlacePerpOrder.orderType.toString(), + perpMarket: ix.keys[4], price: decoded.PlacePerpOrder.price.toNumber(), quantity: decoded.PlacePerpOrder.quantity.toNumber(), reduceOnly: decoded.PlacePerpOrder.reduceOnly.toString(), @@ -111,6 +123,8 @@ export type PlacePerpOrder2 = { orderType: string; reduceOnly: string; expiryTimestamp: number; + mangoAccount: AccountMeta; + perpMarket: AccountMeta; }; export const decodePlacePerpOrder2 = (ix: TransactionInstruction): PlacePerpOrder2 => { @@ -118,8 +132,10 @@ export const decodePlacePerpOrder2 = (ix: TransactionInstruction): PlacePerpOrde const placePerpOrder2: PlacePerpOrder2 = { clientOrderId: decoded.PlacePerpOrder2.clientOrderId.toString(), expiryTimestamp: decoded.PlacePerpOrder2.expiryTimestamp.toNumber(), + mangoAccount: ix.keys[1], maxBaseQuantity: decoded.PlacePerpOrder2.maxBaseQuantity.toNumber(), orderType: decoded.PlacePerpOrder2.orderType.toString(), + perpMarket: ix.keys[4], price: decoded.PlacePerpOrder2.price.toNumber(), reduceOnly: decoded.PlacePerpOrder2.reduceOnly.toString(), side: decoded.PlacePerpOrder2.side.toString(), @@ -131,13 +147,17 @@ export const decodePlacePerpOrder2 = (ix: TransactionInstruction): PlacePerpOrde export type CancelPerpOrder = { orderId: string; invalidIdOk: string; + mangoAccount: AccountMeta; + perpMarket: AccountMeta; }; export const decodeCancelPerpOrder = (ix: TransactionInstruction): CancelPerpOrder => { const decoded = MangoInstructionLayout.decode(ix.data, 0); const cancelPerpOrder: CancelPerpOrder = { invalidIdOk: decoded.CancelPerpOrder.invalidIdOk.toString(), + mangoAccount: ix.keys[1], orderId: decoded.CancelPerpOrder.orderId.toString(), + perpMarket: ix.keys[3], }; return cancelPerpOrder; }; @@ -161,6 +181,7 @@ export type ChangePerpMarketParams = { targetPeriodLength: number; mngoPerPeriodOption: boolean; mngoPerPeriod: number; + perpMarket: AccountMeta; }; export const decodeChangePerpMarketParams = (ix: TransactionInstruction): ChangePerpMarketParams => { @@ -178,6 +199,7 @@ export const decodeChangePerpMarketParams = (ix: TransactionInstruction): Change maxDepthBpsOption: decoded.ChangePerpMarketParams.maxDepthBpsOption, mngoPerPeriod: decoded.ChangePerpMarketParams.mngoPerPeriod.toString(), mngoPerPeriodOption: decoded.ChangePerpMarketParams.mngoPerPeriodOption, + perpMarket: ix.keys[1], rate: decoded.ChangePerpMarketParams.rate.toString(), rateOption: decoded.ChangePerpMarketParams.rateOption, takerFee: decoded.ChangePerpMarketParams.takerFee.toString(), @@ -250,3 +272,11 @@ export type OrderLotDetails = { price: number; size: number; }; + +export type ConsumeEvents = { + perpMarket: AccountMeta; +}; + +export const decodeConsumeEvents = (ix: TransactionInstruction): ConsumeEvents => ({ + perpMarket: ix.keys[2], +}); diff --git a/packages/decoder-mango/src/index.ts b/packages/decoder-mango/src/index.ts index f422733be..8a2533210 100644 --- a/packages/decoder-mango/src/index.ts +++ b/packages/decoder-mango/src/index.ts @@ -6,6 +6,7 @@ export { decodeCancelPerpOrder, decodeCancelSpotOrder, decodeChangePerpMarketParams, + decodeConsumeEvents, decodeDeposit, decodePlacePerpOrder, decodePlacePerpOrder2, @@ -19,6 +20,7 @@ export type { CancelPerpOrder, CancelSpotOrder, ChangePerpMarketParams, + ConsumeEvents, Deposit, OrderLotDetails, PlacePerpOrder, @@ -33,3 +35,5 @@ export { getSpotMarketFromSpotMarketConfig, spotMarketFromIndex, } from './market'; +export type { PerpMarket, PerpMarketConfig, SpotMarketConfig } from '@blockworks-foundation/mango-client'; +export type { Market } from '@project-serum/serum'; From 61d7f00178dbfdcc910a2df4bcda153c63b27dba Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 27 Apr 2026 15:29:00 +0100 Subject: [PATCH 05/17] fix: rename components --- ....tsx => GenericMangoAccountDetailsCard.tsx} | 3 ++- ...ard.tsx => GenericPerpMangoDetailsCard.tsx} | 2 +- ...ard.tsx => GenericSpotMangoDetailsCard.tsx} | 2 +- .../ui/MangoDetailsCard.tsx | 18 +++++++++--------- 4 files changed, 13 insertions(+), 12 deletions(-) rename app/features/instruction-program-mango/ui/{GenericMngoAccountDetailsCard.tsx => GenericMangoAccountDetailsCard.tsx} (95%) rename app/features/instruction-program-mango/ui/{GenericPerpMngoDetailsCard.tsx => GenericPerpMangoDetailsCard.tsx} (97%) rename app/features/instruction-program-mango/ui/{GenericSpotMngoDetailsCard.tsx => GenericSpotMangoDetailsCard.tsx} (97%) diff --git a/app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericMangoAccountDetailsCard.tsx similarity index 95% rename from app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericMangoAccountDetailsCard.tsx index e82593db6..416a661bd 100644 --- a/app/features/instruction-program-mango/ui/GenericMngoAccountDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericMangoAccountDetailsCard.tsx @@ -3,7 +3,8 @@ import { InstructionCard } from '@components/instruction/InstructionCard'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { BaseTable } from '@/app/shared/ui/Table'; -export function GenericMngoAccountDetailsCard(props: { + +export function GenericMangoAccountDetailsCard(props: { ix: TransactionInstruction; index: number; result: SignatureResult; diff --git a/app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx similarity index 97% rename from app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx index f08f7e4dd..d18b2dcbb 100644 --- a/app/features/instruction-program-mango/ui/GenericPerpMngoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx @@ -5,7 +5,7 @@ import { BaseTable } from '@/app/shared/ui/Table'; import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -export function GenericPerpMngoDetailsCard(props: { +export function GenericPerpMangoDetailsCard(props: { ix: TransactionInstruction; index: number; result: SignatureResult; diff --git a/app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx similarity index 97% rename from app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx rename to app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx index 1c3d8f919..8dbb3094a 100644 --- a/app/features/instruction-program-mango/ui/GenericSpotMngoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx @@ -5,7 +5,7 @@ import { BaseTable } from '@/app/shared/ui/Table'; import { getSpotMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; -export function GenericSpotMngoDetailsCard(props: { +export function GenericSpotMangoDetailsCard(props: { ix: TransactionInstruction; index: number; result: SignatureResult; diff --git a/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx index aaa0a85b1..777eac21e 100644 --- a/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/MangoDetailsCard.tsx @@ -23,9 +23,9 @@ import { CancelPerpOrderDetailsCard } from './CancelPerpOrderDetailsCard'; import { CancelSpotOrderDetailsCard } from './CancelSpotOrderDetailsCard'; import { ChangePerpMarketParamsDetailsCard } from './ChangePerpMarketParamsDetailsCard'; import { ConsumeEventsDetailsCard } from './ConsumeEventsDetailsCard'; -import { GenericMngoAccountDetailsCard } from './GenericMngoAccountDetailsCard'; -import { GenericPerpMngoDetailsCard } from './GenericPerpMngoDetailsCard'; -import { GenericSpotMngoDetailsCard } from './GenericSpotMngoDetailsCard'; +import { GenericMangoAccountDetailsCard } from './GenericMangoAccountDetailsCard'; +import { GenericPerpMangoDetailsCard } from './GenericPerpMangoDetailsCard'; +import { GenericSpotMangoDetailsCard } from './GenericSpotMangoDetailsCard'; import { PlacePerpOrder2DetailsCard } from './PlacePerpOrder2DetailsCard'; import { PlacePerpOrderDetailsCard } from './PlacePerpOrderDetailsCard'; import { PlaceSpotOrderDetailsCard } from './PlaceSpotOrderDetailsCard'; @@ -48,13 +48,13 @@ export function MangoDetailsCard(props: { switch (title) { case 'InitMangoAccount': - return ; + return ; case 'Deposit': - return ; + return ; case 'Withdraw': - return ; + return ; case 'InitSpotOpenOrders': - return ; + return ; case 'PlaceSpotOrder': return ; case 'CancelSpotOrder': @@ -71,7 +71,7 @@ export function MangoDetailsCard(props: { return ; case 'SettleFunds': return ( - Date: Mon, 27 Apr 2026 15:44:34 +0100 Subject: [PATCH 06/17] fix: add prebuild into dev script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0036366b6..224af3f10 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build:info": "node scripts/update-build-info.js", "build-sb": "storybook build", "coverage": "vitest --coverage", - "dev": "next dev", + "dev": "pnpm run build:packages && next dev", "eslint:lint": "eslint", "format": "pnpm pretty:format '**/*.{ts,tsx,mts,mjs,cjs}' '*.json'", "format:ci": "pnpm format", From b6429ab38ce44a01dcbe17413c89e1634b6927eb Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:16:27 +0100 Subject: [PATCH 07/17] chore(pacakges): improve scripts to check pacakges with decode-mango as example --- eslint.config.mjs | 9 ++++---- package.json | 23 ++++++++++--------- packages/decoder-mango/package.json | 2 +- .../src/__tests__/config.spec.ts | 2 +- .../src/__tests__/market.spec.ts | 8 +++---- packages/decoder-mango/src/config.ts | 4 ++-- packages/decoder-mango/src/decoder.ts | 2 +- packages/decoder-mango/src/detection.ts | 2 +- packages/decoder-mango/src/market.ts | 4 ++-- tsconfig.json | 2 +- 10 files changed, 30 insertions(+), 28 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index f983763f0..ad6e1bea1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -19,10 +19,11 @@ const TEST_AND_STORY_FILES = [ ]; export default tseslint.config( - // Global ignores + // Global ignores. + // packages/* are intentionally not ignored: root `eslint .` (like prettier's `**/*.ts` glob) lints their source with this shared config — only built output is excluded. { ignores: [ - 'dist/**', + '**/dist/**', 'lib/**', '.next/**', '.next-dev/**', @@ -288,8 +289,8 @@ export default tseslint.config( '.storybook/**', '**/*.stories.[jt]s?(x)', - // Generic config files - '*.config.{ts,mts,js,mjs,cjs}', + // Generic config files (including nested ones, e.g. packages/*/vitest.config.ts) + '**/*.config.{ts,mts,js,mjs,cjs}', ], rules: { 'import/no-default-export': 'off', diff --git a/package.json b/package.json index 224af3f10..2baa5ed16 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "pnpm run build:packages && next build", - "build:packages": "pnpm -r --filter './packages/**' run build", + "build": "pnpm build:packages && next build", "build:info": "node scripts/update-build-info.js", + "build:packages": "pnpm -r --filter './packages/**' run build", "build-sb": "storybook build", "coverage": "vitest --coverage", - "dev": "pnpm run build:packages && next dev", + "dev": "pnpm build:packages && next dev", "eslint:lint": "eslint", "format": "pnpm pretty:format '**/*.{ts,tsx,mts,mjs,cjs}' '*.json'", "format:ci": "pnpm format", @@ -19,27 +19,28 @@ "sb": "storybook dev -p 6006", "scan": "next dev & react-scan localhost:3000", "start": "next start", - "test": "pnpm run test:packages && vitest --project specs run", + "test": "pnpm test:packages && vitest --project specs run", "test:changed": "pnpm vitest:test related --run", - "test:packages": "pnpm -r --filter './packages/**' run test", - "test:ci": "pnpm run test:packages && vitest --project specs run", + "test:ci": "pnpm test:packages && vitest --project specs run", "test:e2e": "playwright test", "test:e2e:ci": "playwright test", "test:e2e:ui": "playwright test --ui", + "test:packages": "pnpm -r --filter './packages/**' run test", "test:sb": "vitest --project storybook run", "test:sb:watch": "vitest --project storybook --watch", "test:watch": "vitest --project specs --watch", "pretypecheck": "next build", - "typecheck": "tsc --noEmit", + "typecheck": "pnpm typecheck:packages && tsc --noEmit", + "typecheck:packages": "pnpm -r --filter './packages/**' run typecheck", "vitest:test": "vitest --project specs" }, "dependencies": { - "@explorer/decoder-mango": "workspace:*", "@bonfida/spl-name-service": "0.1.30", "@codama/dynamic-client": "0.1.0", "@codama/dynamic-parsers": "1.1.30", "@codama/nodes-from-anchor": "1.3.8", "@coral-xyz/anchor": "0.30.1", + "@explorer/decoder-mango": "workspace:*", "@fast-csv/format": "5.0.5", "@mantine/hooks": "7.17.3", "@metaplex-foundation/mpl-token-metadata": "3.4.0", @@ -197,6 +198,9 @@ "vitest": "catalog:" }, "packageManager": "pnpm@10.17.1", + "engines": { + "node": "^22" + }, "devEngines": { "packageManager": { "name": "pnpm", @@ -204,9 +208,6 @@ "onFail": "error" } }, - "engines": { - "node": "^22" - }, "pnpm": { "onlyBuiltDependencies": [ "@sentry/cli", diff --git a/packages/decoder-mango/package.json b/packages/decoder-mango/package.json index ae134f947..0fbc569ad 100644 --- a/packages/decoder-mango/package.json +++ b/packages/decoder-mango/package.json @@ -16,7 +16,7 @@ "build": "tsc", "test": "vitest run", "test:watch": "vitest", - "types": "tsc --noEmit" + "typecheck": "tsc --noEmit" }, "dependencies": { "@blockworks-foundation/mango-client": "3.6.7", diff --git a/packages/decoder-mango/src/__tests__/config.spec.ts b/packages/decoder-mango/src/__tests__/config.spec.ts index 3f08834eb..43a250df0 100644 --- a/packages/decoder-mango/src/__tests__/config.spec.ts +++ b/packages/decoder-mango/src/__tests__/config.spec.ts @@ -23,7 +23,7 @@ describe('findGroupConfig', () => { it.each(['mainnet', 'devnet', 'testnet'] as const)('should return a group config for %s program ID', network => { const result = findGroupConfig(MANGO_PROGRAM_IDS[network]); expect(result).toBeDefined(); - expect(result!.mangoProgramId.equals(MANGO_PROGRAM_IDS[network])).toBe(true); + expect(result?.mangoProgramId.equals(MANGO_PROGRAM_IDS[network])).toBe(true); }); it('should return undefined for an unknown program ID', () => { diff --git a/packages/decoder-mango/src/__tests__/market.spec.ts b/packages/decoder-mango/src/__tests__/market.spec.ts index e7dc84ae3..377caac01 100644 --- a/packages/decoder-mango/src/__tests__/market.spec.ts +++ b/packages/decoder-mango/src/__tests__/market.spec.ts @@ -16,8 +16,8 @@ describe('getSpotMarketFromInstruction', () => { const result = getSpotMarketFromInstruction(ix, spotMarketMeta); expect(result).toBeDefined(); - expect(result!.name).toBe('MNGO/USDC'); - expect(result!.publicKey.equals(SPOT_MARKETS['MNGO/USDC'].publicKey)).toBe(true); + expect(result?.name).toBe('MNGO/USDC'); + expect(result?.publicKey.equals(SPOT_MARKETS['MNGO/USDC'].publicKey)).toBe(true); }); it('should return undefined for unknown spot market pubkey', () => { @@ -61,8 +61,8 @@ describe('getPerpMarketFromInstruction', () => { const result = getPerpMarketFromInstruction(ix, perpMarketMeta); expect(result).toBeDefined(); - expect(result!.name).toBe('BTC-PERP'); - expect(result!.publicKey.equals(PERP_MARKETS['BTC-PERP'].publicKey)).toBe(true); + expect(result?.name).toBe('BTC-PERP'); + expect(result?.publicKey.equals(PERP_MARKETS['BTC-PERP'].publicKey)).toBe(true); }); it('should return undefined for unknown perp market pubkey', () => { diff --git a/packages/decoder-mango/src/config.ts b/packages/decoder-mango/src/config.ts index 334a1458d..0495ea993 100644 --- a/packages/decoder-mango/src/config.ts +++ b/packages/decoder-mango/src/config.ts @@ -1,5 +1,5 @@ -import { Config, GroupConfig } from '@blockworks-foundation/mango-client'; -import { PublicKey } from '@solana/web3.js'; +import { Config, type GroupConfig } from '@blockworks-foundation/mango-client'; +import { type PublicKey } from '@solana/web3.js'; // note: mainnet.1 suffices since its a superset of mainnet.0 export const mangoGroups = Config.ids().groups.filter(group => group.name !== 'mainnet.0'); diff --git a/packages/decoder-mango/src/decoder.ts b/packages/decoder-mango/src/decoder.ts index acffd44a8..01ce94415 100644 --- a/packages/decoder-mango/src/decoder.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -1,5 +1,5 @@ import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; -import { AccountMeta, TransactionInstruction } from '@solana/web3.js'; +import { type AccountMeta, type TransactionInstruction } from '@solana/web3.js'; export type Deposit = { quantity: number; diff --git a/packages/decoder-mango/src/detection.ts b/packages/decoder-mango/src/detection.ts index ed05c8042..c0089725c 100644 --- a/packages/decoder-mango/src/detection.ts +++ b/packages/decoder-mango/src/detection.ts @@ -1,5 +1,5 @@ import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; -import { TransactionInstruction } from '@solana/web3.js'; +import { type TransactionInstruction } from '@solana/web3.js'; import { mangoGroups } from './config'; diff --git a/packages/decoder-mango/src/market.ts b/packages/decoder-mango/src/market.ts index f0ad679bb..7b34567d6 100644 --- a/packages/decoder-mango/src/market.ts +++ b/packages/decoder-mango/src/market.ts @@ -1,6 +1,6 @@ -import { PerpMarket, PerpMarketConfig, PerpMarketLayout, SpotMarketConfig } from '@blockworks-foundation/mango-client'; +import { PerpMarket, type PerpMarketConfig, PerpMarketLayout, type SpotMarketConfig } from '@blockworks-foundation/mango-client'; import { Market } from '@project-serum/serum'; -import { AccountInfo, AccountMeta, Connection, PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { type AccountInfo, type AccountMeta, Connection, type PublicKey, type TransactionInstruction } from '@solana/web3.js'; import { findGroupConfig } from './config'; diff --git a/tsconfig.json b/tsconfig.json index f68c1d8ac..00f93bb6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -43,5 +43,5 @@ ".next/dev/types/**/*.ts", ".next-dev/dev/types/**/*.ts" ], - "exclude": ["node_modules"] + "exclude": ["node_modules", "packages"] } From 1ac614e350aa22d45c4652b16738ea7e9b170c99 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:18:02 +0100 Subject: [PATCH 08/17] fix: codestyle --- packages/decoder-mango/src/market.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/decoder-mango/src/market.ts b/packages/decoder-mango/src/market.ts index 7b34567d6..fd2f099b9 100644 --- a/packages/decoder-mango/src/market.ts +++ b/packages/decoder-mango/src/market.ts @@ -1,6 +1,17 @@ -import { PerpMarket, type PerpMarketConfig, PerpMarketLayout, type SpotMarketConfig } from '@blockworks-foundation/mango-client'; +import { + PerpMarket, + type PerpMarketConfig, + PerpMarketLayout, + type SpotMarketConfig, +} from '@blockworks-foundation/mango-client'; import { Market } from '@project-serum/serum'; -import { type AccountInfo, type AccountMeta, Connection, type PublicKey, type TransactionInstruction } from '@solana/web3.js'; +import { + type AccountInfo, + type AccountMeta, + Connection, + type PublicKey, + type TransactionInstruction, +} from '@solana/web3.js'; import { findGroupConfig } from './config'; From eebacbd77c361db7a0f5e5c914a52cd8027591f8 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:36:33 +0100 Subject: [PATCH 09/17] fix: resolve codestyle issues --- .../model/__tests__/use-mango-market.spec.tsx | 28 +++++++++---------- .../model/use-mango-market.ts | 18 ++++++------ .../ui/AddSpotMarketDetailsCard.tsx | 4 +-- .../ui/CancelPerpOrderDetailsCard.tsx | 4 +-- .../ui/CancelSpotOrderDetailsCard.tsx | 4 +-- .../ui/ChangePerpMarketParamsDetailsCard.tsx | 8 +++--- .../ui/ConsumeEventsDetailsCard.tsx | 4 +-- .../ui/GenericPerpMangoDetailsCard.tsx | 4 +-- .../ui/GenericSpotMangoDetailsCard.tsx | 4 +-- .../ui/PlacePerpOrder2DetailsCard.tsx | 12 ++++---- .../ui/PlacePerpOrderDetailsCard.tsx | 12 ++++---- .../ui/PlaceSpotOrderDetailsCard.tsx | 12 ++++---- 12 files changed, 57 insertions(+), 57 deletions(-) diff --git a/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx b/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx index 5bb3de389..09a29e8bd 100644 --- a/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx +++ b/app/features/instruction-program-mango/model/__tests__/use-mango-market.spec.tsx @@ -33,14 +33,14 @@ describe('useMangoPerpMarket', () => { vi.clearAllMocks(); }); - it('returns null when config is undefined', () => { + it('should return undefined when config is undefined', () => { const { result } = renderHook(() => useMangoPerpMarket(undefined)); - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); expect(getPerpMarketFromPerpMarketConfig).not.toHaveBeenCalled(); }); - it('returns the resolved perp market when config is provided', async () => { + it('should return the resolved perp market when config is provided', async () => { const resolved = { name: 'BTC-PERP' } as unknown as PerpMarket; vi.mocked(getPerpMarketFromPerpMarketConfig).mockResolvedValueOnce(resolved); @@ -52,7 +52,7 @@ describe('useMangoPerpMarket', () => { expect(getPerpMarketFromPerpMarketConfig).toHaveBeenCalledWith('https://mainnet.rpc.address', perpMarketConfig); }); - it('returns null when resolution throws', async () => { + it('should return undefined when resolution throws', async () => { vi.mocked(getPerpMarketFromPerpMarketConfig).mockRejectedValueOnce(new Error('rpc failed')); const { result } = renderHook(() => useMangoPerpMarket(perpMarketConfig)); @@ -60,10 +60,10 @@ describe('useMangoPerpMarket', () => { await waitFor(() => { expect(getPerpMarketFromPerpMarketConfig).toHaveBeenCalled(); }); - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); }); - it('resets to null when config transitions from defined to undefined', async () => { + it('should reset to undefined when config transitions from defined to undefined', async () => { const resolved = { name: 'BTC-PERP' } as unknown as PerpMarket; vi.mocked(getPerpMarketFromPerpMarketConfig).mockResolvedValueOnce(resolved); @@ -78,7 +78,7 @@ describe('useMangoPerpMarket', () => { rerender({ config: undefined }); await waitFor(() => { - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); }); }); }); @@ -88,14 +88,14 @@ describe('useMangoSpotMarket', () => { vi.clearAllMocks(); }); - it('returns null when config is undefined', () => { + it('should return undefined when config is undefined', () => { const { result } = renderHook(() => useMangoSpotMarket(programId, undefined)); - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); expect(getSpotMarketFromSpotMarketConfig).not.toHaveBeenCalled(); }); - it('returns the resolved spot market when config is provided', async () => { + it('should return the resolved spot market when config is provided', async () => { const resolved = { address: 'spot' } as unknown as Market; vi.mocked(getSpotMarketFromSpotMarketConfig).mockResolvedValueOnce(resolved); @@ -111,7 +111,7 @@ describe('useMangoSpotMarket', () => { ); }); - it('returns null when resolver yields undefined', async () => { + it('should return undefined when resolver yields undefined', async () => { vi.mocked(getSpotMarketFromSpotMarketConfig).mockResolvedValueOnce(undefined); const { result } = renderHook(() => useMangoSpotMarket(programId, spotMarketConfig)); @@ -119,10 +119,10 @@ describe('useMangoSpotMarket', () => { await waitFor(() => { expect(getSpotMarketFromSpotMarketConfig).toHaveBeenCalled(); }); - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); }); - it('returns null when resolution throws', async () => { + it('should return undefined when resolution throws', async () => { vi.mocked(getSpotMarketFromSpotMarketConfig).mockRejectedValueOnce(new Error('rpc failed')); const { result } = renderHook(() => useMangoSpotMarket(programId, spotMarketConfig)); @@ -130,6 +130,6 @@ describe('useMangoSpotMarket', () => { await waitFor(() => { expect(getSpotMarketFromSpotMarketConfig).toHaveBeenCalled(); }); - expect(result.current).toBeNull(); + expect(result.current).toBeUndefined(); }); }); diff --git a/app/features/instruction-program-mango/model/use-mango-market.ts b/app/features/instruction-program-mango/model/use-mango-market.ts index e43158926..dd630ba90 100644 --- a/app/features/instruction-program-mango/model/use-mango-market.ts +++ b/app/features/instruction-program-mango/model/use-mango-market.ts @@ -11,21 +11,21 @@ import { PublicKey } from '@solana/web3.js'; import { useState } from 'react'; import useAsyncEffect from 'use-async-effect'; -export function useMangoPerpMarket(config: PerpMarketConfig | undefined): PerpMarket | null { +export function useMangoPerpMarket(config: PerpMarketConfig | undefined): PerpMarket | undefined { const { url } = useCluster(); - const [market, setMarket] = useState(null); + const [market, setMarket] = useState(undefined); useAsyncEffect( async isMounted => { if (config === undefined) { - if (isMounted()) setMarket(null); + if (isMounted()) setMarket(undefined); return; } try { const resolved = await getPerpMarketFromPerpMarketConfig(url, config); if (isMounted()) setMarket(resolved); } catch { - if (isMounted()) setMarket(null); + if (isMounted()) setMarket(undefined); } }, [url, config], @@ -34,21 +34,21 @@ export function useMangoPerpMarket(config: PerpMarketConfig | undefined): PerpMa return market; } -export function useMangoSpotMarket(programId: PublicKey, config: SpotMarketConfig | undefined): Market | null { +export function useMangoSpotMarket(programId: PublicKey, config: SpotMarketConfig | undefined): Market | undefined { const { url } = useCluster(); - const [market, setMarket] = useState(null); + const [market, setMarket] = useState(undefined); useAsyncEffect( async isMounted => { if (config === undefined) { - if (isMounted()) setMarket(null); + if (isMounted()) setMarket(undefined); return; } try { const resolved = await getSpotMarketFromSpotMarketConfig(programId, url, config); - if (isMounted()) setMarket(resolved ?? null); + if (isMounted()) setMarket(resolved ?? undefined); } catch { - if (isMounted()) setMarket(null); + if (isMounted()) setMarket(undefined); } }, [url, programId, config], diff --git a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx index 22d65898b..f05101f65 100644 --- a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx @@ -1,9 +1,9 @@ import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { AddSpotMarket, spotMarketFromIndex } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function AddSpotMarketDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx index 0c2c06eba..6359b4ebb 100644 --- a/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelPerpOrderDetailsCard.tsx @@ -1,10 +1,10 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { CancelPerpOrder, getPerpMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function CancelPerpOrderDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx index a8628a99e..4a1a7cf28 100644 --- a/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/CancelSpotOrderDetailsCard.tsx @@ -1,10 +1,10 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { CancelSpotOrder, getSpotMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function CancelSpotOrderDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx index e6c49c755..fe053eefb 100644 --- a/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/ChangePerpMarketParamsDetailsCard.tsx @@ -3,9 +3,9 @@ import { ChangePerpMarketParams, getPerpMarketFromInstruction } from '@explorer/ import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import { formatDuration } from '@utils/date'; -import { useMangoPerpMarket } from '../model/use-mango-market'; - import { BaseTable } from '@/app/shared/ui/Table'; + +import { useMangoPerpMarket } from '../model/use-mango-market'; export function ChangePerpMarketParamsDetailsCard(props: { ix: TransactionInstruction; index: number; @@ -18,7 +18,7 @@ export function ChangePerpMarketParamsDetailsCard(props: { const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); - const targetPeriodLength = perpMarket?.liquidityMiningInfo.targetPeriodLength.toNumber() ?? null; + const targetPeriodLength = perpMarket?.liquidityMiningInfo.targetPeriodLength.toNumber(); return ( - MNGO per {targetPeriodLength !== null && formatDuration(targetPeriodLength, 'seconds')} + MNGO per {targetPeriodLength !== undefined && formatDuration(targetPeriodLength, 'seconds')} {info.mngoPerPeriod} {} diff --git a/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx b/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx index 9571c2eb0..63f6fe791 100644 --- a/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/ConsumeEventsDetailsCard.tsx @@ -1,10 +1,10 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { ConsumeEvents, getPerpMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function ConsumeEventsDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx index d18b2dcbb..b5d8befd0 100644 --- a/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericPerpMangoDetailsCard.tsx @@ -1,10 +1,10 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { getPerpMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function GenericPerpMangoDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx b/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx index 8dbb3094a..a28b17176 100644 --- a/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/GenericSpotMangoDetailsCard.tsx @@ -1,10 +1,10 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { getSpotMarketFromInstruction } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; +import { BaseTable } from '@/app/shared/ui/Table'; + export function GenericSpotMangoDetailsCard(props: { ix: TransactionInstruction; index: number; diff --git a/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx index 85948db30..5b24f3dbf 100644 --- a/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrder2DetailsCard.tsx @@ -1,12 +1,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { getPerpMarketFromInstruction, OrderLotDetails, PlacePerpOrder2 } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; import { useMemo } from 'react'; +import { BaseTable } from '@/app/shared/ui/Table'; + import { useMangoPerpMarket } from '../model/use-mango-market'; export function PlacePerpOrder2DetailsCard(props: { @@ -21,8 +21,8 @@ export function PlacePerpOrder2DetailsCard(props: { const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); - const orderLotDetails = useMemo(() => { - if (!perpMarket) return null; + const orderLotDetails = useMemo(() => { + if (!perpMarket) return undefined; return { price: perpMarket.priceLotsToNumber(new BN(info.price.toString())), size: perpMarket.baseLotsToNumber(new BN(info.maxBaseQuantity.toString())), @@ -71,13 +71,13 @@ export function PlacePerpOrder2DetailsCard(props: { Side {info.side} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( price {orderLotDetails?.price} USDC )} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( quantity {orderLotDetails?.size} diff --git a/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx index 0ed4f235d..33eea51c7 100644 --- a/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlacePerpOrderDetailsCard.tsx @@ -1,12 +1,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { getPerpMarketFromInstruction, OrderLotDetails, PlacePerpOrder } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; import { useMemo } from 'react'; +import { BaseTable } from '@/app/shared/ui/Table'; + import { useMangoPerpMarket } from '../model/use-mango-market'; export function PlacePerpOrderDetailsCard(props: { @@ -21,8 +21,8 @@ export function PlacePerpOrderDetailsCard(props: { const mangoPerpMarketConfig = getPerpMarketFromInstruction(ix, info.perpMarket); const perpMarket = useMangoPerpMarket(mangoPerpMarketConfig); - const orderLotDetails = useMemo(() => { - if (!perpMarket) return null; + const orderLotDetails = useMemo(() => { + if (!perpMarket) return undefined; return { price: perpMarket.priceLotsToNumber(new BN(info.price.toString())), size: perpMarket.baseLotsToNumber(new BN(info.quantity.toString())), @@ -76,14 +76,14 @@ export function PlacePerpOrderDetailsCard(props: { {info.side} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( price {orderLotDetails?.price} USDC )} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( quantity {orderLotDetails?.size} diff --git a/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx index 63738ddd7..339840c9b 100644 --- a/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/PlaceSpotOrderDetailsCard.tsx @@ -1,12 +1,12 @@ import { Address } from '@components/common/Address'; import { InstructionCard } from '@components/instruction/InstructionCard'; - -import { BaseTable } from '@/app/shared/ui/Table'; import { getSpotMarketFromInstruction, OrderLotDetails, PlaceSpotOrder } from '@explorer/decoder-mango'; import { SignatureResult, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; import { useMemo } from 'react'; +import { BaseTable } from '@/app/shared/ui/Table'; + import { useMangoSpotMarket } from '../model/use-mango-market'; export function PlaceSpotOrderDetailsCard(props: { @@ -21,8 +21,8 @@ export function PlaceSpotOrderDetailsCard(props: { const mangoSpotMarketConfig = getSpotMarketFromInstruction(ix, info.spotMarket); const spotMarket = useMangoSpotMarket(ix.programId, mangoSpotMarketConfig); - const orderLotDetails = useMemo(() => { - if (!spotMarket) return null; + const orderLotDetails = useMemo(() => { + if (!spotMarket) return undefined; return { price: spotMarket.priceLotsToNumber(new BN(info.limitPrice.toString())), size: spotMarket.baseSizeLotsToNumber(new BN(info.maxBaseQuantity.toString())), @@ -77,7 +77,7 @@ export function PlaceSpotOrderDetailsCard(props: { {info.side} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( Limit price {/* todo fix price */} @@ -85,7 +85,7 @@ export function PlaceSpotOrderDetailsCard(props: { )} - {orderLotDetails !== null && ( + {orderLotDetails !== undefined && ( Size {orderLotDetails?.size} From d38dc98dc74af14bc4bbeb9922760ba820827378 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:42:50 +0100 Subject: [PATCH 10/17] feat(mango): rework internals to allow to lighten the endpoint --- app/components/account/TokenHistoryCard.tsx | 3 ++- .../transaction/ui/InstructionsSection.tsx | 2 +- packages/decoder-mango/package.json | 4 ++++ .../src/__tests__/decoder.spec.ts | 20 ++++++++++++++++++ .../src/__tests__/detection.spec.ts | 21 +------------------ .../decoder-mango/src/__tests__/fixtures.ts | 7 +------ packages/decoder-mango/src/decoder.ts | 5 +++++ packages/decoder-mango/src/detection.ts | 15 ++++++------- packages/decoder-mango/src/index.ts | 3 ++- packages/decoder-mango/src/program-ids.ts | 8 +++++++ 10 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 packages/decoder-mango/src/program-ids.ts diff --git a/app/components/account/TokenHistoryCard.tsx b/app/components/account/TokenHistoryCard.tsx index 0749f4f6b..0aa53fec0 100644 --- a/app/components/account/TokenHistoryCard.tsx +++ b/app/components/account/TokenHistoryCard.tsx @@ -11,7 +11,8 @@ import { parseTokenLendingInstructionTitle, } from '@components/instruction/token-lending/types'; import { isTokenSwapInstruction, parseTokenSwapInstructionTitle } from '@components/instruction/token-swap/types'; -import { isMangoInstruction, parseMangoInstructionTitle } from '@explorer/decoder-mango'; +import { parseMangoInstructionTitle } from '@explorer/decoder-mango'; +import { isMangoInstruction } from '@explorer/decoder-mango/detection'; import { isTokenProgramData } from '@providers/accounts'; import { useAccountHistories, useFetchAccountHistory } from '@providers/accounts/history'; import { isTokenProgramId, TokenInfoWithPubkey, useAccountOwnedTokens } from '@providers/accounts/tokens'; diff --git a/app/features/transaction/ui/InstructionsSection.tsx b/app/features/transaction/ui/InstructionsSection.tsx index b80122db6..59522e29e 100644 --- a/app/features/transaction/ui/InstructionsSection.tsx +++ b/app/features/transaction/ui/InstructionsSection.tsx @@ -36,7 +36,7 @@ import { } from '@components/instruction/ZkElGamalProofDetailsCard'; import { useAnchorProgram } from '@entities/idl'; import { useInstructionParser } from '@entities/instruction-parser'; -import { isMangoInstruction } from '@explorer/decoder-mango'; +import { isMangoInstruction } from '@explorer/decoder-mango/detection'; import { MetaplexTokenMetadataDetailsCard } from '@features/mpl-token-metadata'; import { isStakeInstruction, RawStakeDetailsCard, StakeDetailsCard } from '@features/stake'; import { isTokenBatchInstruction, TokenBatchCard } from '@features/token-batch'; diff --git a/packages/decoder-mango/package.json b/packages/decoder-mango/package.json index 0fbc569ad..fed88eb6e 100644 --- a/packages/decoder-mango/package.json +++ b/packages/decoder-mango/package.json @@ -7,6 +7,10 @@ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" + }, + "./detection": { + "types": "./dist/detection.d.ts", + "import": "./dist/detection.js" } }, "files": [ diff --git a/packages/decoder-mango/src/__tests__/decoder.spec.ts b/packages/decoder-mango/src/__tests__/decoder.spec.ts index bb865abf2..cfd6980da 100644 --- a/packages/decoder-mango/src/__tests__/decoder.spec.ts +++ b/packages/decoder-mango/src/__tests__/decoder.spec.ts @@ -13,6 +13,7 @@ import { decodePlacePerpOrder2, decodePlaceSpotOrder, decodeWithdraw, + parseMangoInstructionTitle, } from '../decoder'; import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS, TEST_KEYS } from './fixtures'; @@ -152,3 +153,22 @@ describe('decodeConsumeEvents', () => { expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[2]); }); }); + +describe('parseMangoInstructionTitle', () => { + it.each([ + 'Deposit', + 'Withdraw', + 'AddToBasket', + 'PlaceSpotOrder', + 'CancelSpotOrder', + 'PlacePerpOrder', + 'PlacePerpOrder2', + 'CancelPerpOrder', + 'AddSpotMarket', + 'AddPerpMarket', + 'ChangePerpMarketParams', + ] as const)('should parse %s instruction', title => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS[title], programId); + expect(parseMangoInstructionTitle(ix)).toBe(title); + }); +}); diff --git a/packages/decoder-mango/src/__tests__/detection.spec.ts b/packages/decoder-mango/src/__tests__/detection.spec.ts index 57c721692..3b538d105 100644 --- a/packages/decoder-mango/src/__tests__/detection.spec.ts +++ b/packages/decoder-mango/src/__tests__/detection.spec.ts @@ -1,7 +1,7 @@ import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; import { describe, expect, it } from 'vitest'; -import { isMangoInstruction, parseMangoInstructionTitle } from '../detection'; +import { isMangoInstruction } from '../detection'; import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS } from './fixtures'; describe('isMangoInstruction', () => { @@ -31,22 +31,3 @@ describe('isMangoInstruction', () => { expect(isMangoInstruction(ix)).toBe(false); }); }); - -describe('parseMangoInstructionTitle', () => { - it.each([ - 'Deposit', - 'Withdraw', - 'AddToBasket', - 'PlaceSpotOrder', - 'CancelSpotOrder', - 'PlacePerpOrder', - 'PlacePerpOrder2', - 'CancelPerpOrder', - 'AddSpotMarket', - 'AddPerpMarket', - 'ChangePerpMarketParams', - ] as const)('should parse %s instruction', title => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS[title], MANGO_PROGRAM_IDS.mainnet); - expect(parseMangoInstructionTitle(ix)).toBe(title); - }); -}); diff --git a/packages/decoder-mango/src/__tests__/fixtures.ts b/packages/decoder-mango/src/__tests__/fixtures.ts index 1bf0f1705..7fb94f3b0 100644 --- a/packages/decoder-mango/src/__tests__/fixtures.ts +++ b/packages/decoder-mango/src/__tests__/fixtures.ts @@ -2,12 +2,7 @@ import { encodeMangoInstruction } from '@blockworks-foundation/mango-client'; import { Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js'; import BN from 'bn.js'; -/** Known Mango v3 program IDs from Config.ids() */ -export const MANGO_PROGRAM_IDS = { - devnet: new PublicKey('4skJ85cdxQAFVKbcGgfun8iZPL7BadVYXG3kGEGkufqA'), - mainnet: new PublicKey('mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68'), - testnet: new PublicKey('BXhdkETgbHrr5QmVBT1xbz3JrMM28u5djbVtmTUfmFTH'), -}; +export { MANGO_PROGRAM_IDS } from '../program-ids'; /** Known spot market from mainnet.1 group */ export const SPOT_MARKETS = { diff --git a/packages/decoder-mango/src/decoder.ts b/packages/decoder-mango/src/decoder.ts index 01ce94415..b96cb1c32 100644 --- a/packages/decoder-mango/src/decoder.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -280,3 +280,8 @@ export type ConsumeEvents = { export const decodeConsumeEvents = (ix: TransactionInstruction): ConsumeEvents => ({ perpMarket: ix.keys[2], }); + +export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { + const decodedInstruction = MangoInstructionLayout.decode(instruction.data, 0); + return Object.keys(decodedInstruction)[0]; +}; diff --git a/packages/decoder-mango/src/detection.ts b/packages/decoder-mango/src/detection.ts index c0089725c..ea70e3821 100644 --- a/packages/decoder-mango/src/detection.ts +++ b/packages/decoder-mango/src/detection.ts @@ -1,13 +1,10 @@ -import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; import { type TransactionInstruction } from '@solana/web3.js'; -import { mangoGroups } from './config'; +import { MANGO_PROGRAM_IDS } from './program-ids'; -export const isMangoInstruction = (instruction: TransactionInstruction) => { - return mangoGroups.map(group => group.mangoProgramId.toBase58()).includes(instruction.programId.toBase58()); -}; +const programIds = new Set(Object.values(MANGO_PROGRAM_IDS).map(id => id.toBase58())); -export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { - const decodedInstruction = MangoInstructionLayout.decode(instruction.data, 0); - return Object.keys(decodedInstruction)[0]; -}; +export const isMangoInstruction = (instruction: TransactionInstruction): boolean => + programIds.has(instruction.programId.toBase58()); + +export { MANGO_PROGRAM_IDS }; diff --git a/packages/decoder-mango/src/index.ts b/packages/decoder-mango/src/index.ts index 8a2533210..98b99e3e5 100644 --- a/packages/decoder-mango/src/index.ts +++ b/packages/decoder-mango/src/index.ts @@ -1,4 +1,4 @@ -export { isMangoInstruction, parseMangoInstructionTitle } from './detection'; +export { isMangoInstruction, MANGO_PROGRAM_IDS } from './detection'; export { decodeAddPerpMarket, decodeAddSpotMarket, @@ -12,6 +12,7 @@ export { decodePlacePerpOrder2, decodePlaceSpotOrder, decodeWithdraw, + parseMangoInstructionTitle, } from './decoder'; export type { AddPerpMarket, diff --git a/packages/decoder-mango/src/program-ids.ts b/packages/decoder-mango/src/program-ids.ts new file mode 100644 index 000000000..cabc7d64c --- /dev/null +++ b/packages/decoder-mango/src/program-ids.ts @@ -0,0 +1,8 @@ +import { PublicKey } from '@solana/web3.js'; + +// Hardcoded Mango v3 program IDs (from Config.ids()) so instruction detection stays free of @blockworks-foundation/mango-client. +export const MANGO_PROGRAM_IDS = { + devnet: new PublicKey('4skJ85cdxQAFVKbcGgfun8iZPL7BadVYXG3kGEGkufqA'), + mainnet: new PublicKey('mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68'), + testnet: new PublicKey('BXhdkETgbHrr5QmVBT1xbz3JrMM28u5djbVtmTUfmFTH'), +}; From 572f22f8e0b08ad5ed5e7f97299797448ee39e22 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:44:48 +0100 Subject: [PATCH 11/17] chore: collect build info --- bench/BUILD.md | 56 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/bench/BUILD.md b/bench/BUILD.md index 6a6563ea6..16bf1dc6e 100644 --- a/bench/BUILD.md +++ b/bench/BUILD.md @@ -4,31 +4,31 @@ |------|-------|------|---------------| | Static | `/` | 130 kB | 1.15 MB | | Static | `/_not-found` | 0 B | 1.03 MB | -| Dynamic | `/address/[address]` | 430 kB | 1.45 MB | -| Dynamic | `/address/[address]/anchor-account` | 390 kB | 1.40 MB | -| Dynamic | `/address/[address]/anchor-program` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/attestation` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/attributes` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/blockhashes` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/compression` | 390 kB | 1.40 MB | -| Dynamic | `/address/[address]/concurrent-merkle-tree` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/domains` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/entries` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/feature-gate` | 380 kB | 1.40 MB | +| Dynamic | `/address/[address]` | 440 kB | 1.45 MB | +| Dynamic | `/address/[address]/anchor-account` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/anchor-program` | 390 kB | 1.40 MB | +| Dynamic | `/address/[address]/attestation` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/attributes` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/blockhashes` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/compression` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/concurrent-merkle-tree` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/domains` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/entries` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/feature-gate` | 390 kB | 1.40 MB | | Dynamic | `/address/[address]/idl` | 530 kB | 1.54 MB | -| Dynamic | `/address/[address]/instructions` | 430 kB | 1.45 MB | -| Dynamic | `/address/[address]/metadata` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/nftoken-collection-nfts` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/program-multisig` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/rewards` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/security` | 390 kB | 1.40 MB | -| Dynamic | `/address/[address]/slot-hashes` | 380 kB | 1.40 MB | -| Dynamic | `/address/[address]/stake-history` | 380 kB | 1.40 MB | +| Dynamic | `/address/[address]/instructions` | 440 kB | 1.45 MB | +| Dynamic | `/address/[address]/metadata` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/nftoken-collection-nfts` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/program-multisig` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/rewards` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/security` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/slot-hashes` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/stake-history` | 390 kB | 1.41 MB | | Dynamic | `/address/[address]/token-extensions` | 390 kB | 1.41 MB | -| Dynamic | `/address/[address]/tokens` | 520 kB | 1.53 MB | -| Dynamic | `/address/[address]/transfers` | 430 kB | 1.45 MB | -| Dynamic | `/address/[address]/verified-build` | 390 kB | 1.40 MB | -| Dynamic | `/address/[address]/vote-history` | 380 kB | 1.40 MB | +| Dynamic | `/address/[address]/tokens` | 520 kB | 1.54 MB | +| Dynamic | `/address/[address]/transfers` | 440 kB | 1.45 MB | +| Dynamic | `/address/[address]/verified-build` | 390 kB | 1.41 MB | +| Dynamic | `/address/[address]/vote-history` | 390 kB | 1.41 MB | | Dynamic | `/api/anchor` | — | — | | Dynamic | `/api/ans-domains/[address]` | — | — | | Dynamic | `/api/domain-info/[domain]` | — | — | @@ -44,7 +44,7 @@ | Dynamic | `/api/verification/coingecko/[address]` | — | — | | Dynamic | `/api/verification/jupiter/[mintAddress]` | — | — | | Dynamic | `/api/verification/rugcheck/[mintAddress]` | — | — | -| Dynamic | `/block/[slot]` | 220 kB | 1.24 MB | +| Dynamic | `/block/[slot]` | 230 kB | 1.25 MB | | Dynamic | `/block/[slot]/accounts` | 220 kB | 1.24 MB | | Dynamic | `/block/[slot]/programs` | 220 kB | 1.24 MB | | Dynamic | `/block/[slot]/rewards` | 220 kB | 1.24 MB | @@ -53,7 +53,7 @@ | Dynamic | `/og/feature-gate/[address]` | — | — | | Dynamic | `/og/receipt/[signature]` | — | — | | Static | `/opengraph-image.png` | — | — | -| Static | `/tos` | 890 B | 1.03 MB | -| Dynamic | `/tx/[signature]` | 600 kB | 1.61 MB | -| Dynamic | `/tx/[signature]/inspect` | 390 kB | 1.40 MB | -| Static | `/tx/inspector` | 390 kB | 1.40 MB | \ No newline at end of file +| Static | `/tos` | 880 B | 1.03 MB | +| Dynamic | `/tx/[signature]` | 520 kB | 1.53 MB | +| Dynamic | `/tx/[signature]/inspect` | 390 kB | 1.41 MB | +| Static | `/tx/inspector` | 390 kB | 1.41 MB | \ No newline at end of file From 8cd18b216f08397ff4f9e89e6b895c3162497d18 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 17:47:35 +0100 Subject: [PATCH 12/17] fix: review comments --- packages/decoder-mango/src/market.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/decoder-mango/src/market.ts b/packages/decoder-mango/src/market.ts index fd2f099b9..60c4fd3ba 100644 --- a/packages/decoder-mango/src/market.ts +++ b/packages/decoder-mango/src/market.ts @@ -15,15 +15,16 @@ import { import { findGroupConfig } from './config'; -// caching of account info's by public keys +// caching of account info's by cluster url + public key const accountInfoCache: Record | null>> = {}; function getAccountInfo(clusterUrl: string, publicKey: PublicKey): Promise | null> { - if (publicKey.toBase58() in accountInfoCache) { - return accountInfoCache[publicKey.toBase58()]; + const cacheKey = `${clusterUrl}:${publicKey.toBase58()}`; + if (cacheKey in accountInfoCache) { + return accountInfoCache[cacheKey]; } const connection = new Connection(clusterUrl); const accountInfoPromise = connection.getAccountInfo(publicKey); - accountInfoCache[publicKey.toBase58()] = accountInfoPromise; + accountInfoCache[cacheKey] = accountInfoPromise; return accountInfoPromise; } @@ -77,7 +78,10 @@ export async function getPerpMarketFromPerpMarketConfig( mangoPerpMarketConfig: PerpMarketConfig, ): Promise { const acc = await getAccountInfo(clusterUrl, mangoPerpMarketConfig.publicKey); - const decoded = PerpMarketLayout.decode(acc?.data); + if (acc === null) { + throw new Error(`PerpMarket account not found: ${mangoPerpMarketConfig.publicKey.toBase58()}`); + } + const decoded = PerpMarketLayout.decode(acc.data); return new PerpMarket( mangoPerpMarketConfig.publicKey, From 038726725386ca663e3cb378ba926abd6da96ca9 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 18:02:05 +0100 Subject: [PATCH 13/17] chore: prebuild packages for Storybook --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2baa5ed16..e284ec704 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "test:e2e:ci": "playwright test", "test:e2e:ui": "playwright test --ui", "test:packages": "pnpm -r --filter './packages/**' run test", + "pretest:sb": "pnpm build:packages", "test:sb": "vitest --project storybook run", "test:sb:watch": "vitest --project storybook --watch", "test:watch": "vitest --project specs --watch", From 8b34335c06a6b4da00c9d190e612d557e778f80e Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 18:52:53 +0100 Subject: [PATCH 14/17] fix: improve `spotMarketFromIndex` utility --- .../ui/AddSpotMarketDetailsCard.tsx | 8 ++++---- packages/decoder-mango/src/decoder.ts | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx index f05101f65..dc781a28e 100644 --- a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx @@ -14,6 +14,8 @@ export function AddSpotMarketDetailsCard(props: { }) { const { ix, index, result, info, innerCards, childIndex } = props; + const spotMarket = spotMarketFromIndex(ix, info.marketIndex); + return ( - {spotMarketFromIndex(ix, info.marketIndex) !== 'UNKNOWN' && ( + {spotMarket !== undefined && spotMarket !== 'UNKNOWN' && ( Market - - {spotMarketFromIndex(ix, info.marketIndex)} - + {spotMarket} )} diff --git a/packages/decoder-mango/src/decoder.ts b/packages/decoder-mango/src/decoder.ts index b96cb1c32..389f40924 100644 --- a/packages/decoder-mango/src/decoder.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -187,24 +187,24 @@ export type ChangePerpMarketParams = { export const decodeChangePerpMarketParams = (ix: TransactionInstruction): ChangePerpMarketParams => { const decoded = MangoInstructionLayout.decode(ix.data, 0); const changePerpMarketParams: ChangePerpMarketParams = { - initLeverage: decoded.ChangePerpMarketParams.initLeverage.toString(), + initLeverage: decoded.ChangePerpMarketParams.initLeverage.toNumber(), initLeverageOption: decoded.ChangePerpMarketParams.initLeverageOption, - liquidationFee: decoded.ChangePerpMarketParams.liquidationFee.toString(), + liquidationFee: decoded.ChangePerpMarketParams.liquidationFee.toNumber(), liquidationFeeOption: decoded.ChangePerpMarketParams.liquidationFeeOption, - maintLeverage: decoded.ChangePerpMarketParams.maintLeverage.toString(), + maintLeverage: decoded.ChangePerpMarketParams.maintLeverage.toNumber(), maintLeverageOption: decoded.ChangePerpMarketParams.maintLeverageOption, - makerFee: decoded.ChangePerpMarketParams.makerFee.toString(), + makerFee: decoded.ChangePerpMarketParams.makerFee.toNumber(), makerFeeOption: decoded.ChangePerpMarketParams.makerFeeOption, - maxDepthBps: decoded.ChangePerpMarketParams.maxDepthBps.toString(), + maxDepthBps: decoded.ChangePerpMarketParams.maxDepthBps.toNumber(), maxDepthBpsOption: decoded.ChangePerpMarketParams.maxDepthBpsOption, - mngoPerPeriod: decoded.ChangePerpMarketParams.mngoPerPeriod.toString(), + mngoPerPeriod: decoded.ChangePerpMarketParams.mngoPerPeriod.toNumber(), mngoPerPeriodOption: decoded.ChangePerpMarketParams.mngoPerPeriodOption, perpMarket: ix.keys[1], - rate: decoded.ChangePerpMarketParams.rate.toString(), + rate: decoded.ChangePerpMarketParams.rate.toNumber(), rateOption: decoded.ChangePerpMarketParams.rateOption, - takerFee: decoded.ChangePerpMarketParams.takerFee.toString(), + takerFee: decoded.ChangePerpMarketParams.takerFee.toNumber(), takerFeeOption: decoded.ChangePerpMarketParams.takerFeeOption, - targetPeriodLength: decoded.ChangePerpMarketParams.targetPeriodLength.toString(), + targetPeriodLength: decoded.ChangePerpMarketParams.targetPeriodLength.toNumber(), targetPeriodLengthOption: decoded.ChangePerpMarketParams.targetPeriodLengthOption, }; return changePerpMarketParams; From 5fd58ebb3470e8de3ae99a2ff3ff22d13e0472a6 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 23:15:48 +0100 Subject: [PATCH 15/17] feat(mango): impove marketIndex handling --- app/components/account/TokenHistoryCard.tsx | 3 +- .../ui/AddPerpMarketDetailsCard.tsx | 10 ++- .../ui/AddSpotMarketDetailsCard.tsx | 12 +-- .../src/__tests__/decoder.spec.ts | 29 ++----- .../src/__tests__/detection.spec.ts | 33 +++++++- packages/decoder-mango/src/decoder.ts | 13 +--- packages/decoder-mango/src/detection.ts | 12 +++ packages/decoder-mango/src/index.ts | 3 +- .../decoder-mango/src/instruction-names.ts | 76 +++++++++++++++++++ 9 files changed, 144 insertions(+), 47 deletions(-) create mode 100644 packages/decoder-mango/src/instruction-names.ts diff --git a/app/components/account/TokenHistoryCard.tsx b/app/components/account/TokenHistoryCard.tsx index 0aa53fec0..20e6f9208 100644 --- a/app/components/account/TokenHistoryCard.tsx +++ b/app/components/account/TokenHistoryCard.tsx @@ -11,8 +11,7 @@ import { parseTokenLendingInstructionTitle, } from '@components/instruction/token-lending/types'; import { isTokenSwapInstruction, parseTokenSwapInstructionTitle } from '@components/instruction/token-swap/types'; -import { parseMangoInstructionTitle } from '@explorer/decoder-mango'; -import { isMangoInstruction } from '@explorer/decoder-mango/detection'; +import { isMangoInstruction, parseMangoInstructionTitle } from '@explorer/decoder-mango/detection'; import { isTokenProgramData } from '@providers/accounts'; import { useAccountHistories, useFetchAccountHistory } from '@providers/accounts/history'; import { isTokenProgramId, TokenInfoWithPubkey, useAccountOwnedTokens } from '@providers/accounts/tokens'; diff --git a/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx index a16a9ffb5..01e059da3 100644 --- a/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddPerpMarketDetailsCard.tsx @@ -24,10 +24,12 @@ export function AddPerpMarketDetailsCard(props: { innerCards={innerCards} childIndex={childIndex} > - - Market index - {info.marketIndex} - + {info.marketIndex !== undefined && ( + + Market index + {info.marketIndex} + + )} Maintenance leverage {info.maintLeverage} diff --git a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx index dc781a28e..e9833ff99 100644 --- a/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx +++ b/app/features/instruction-program-mango/ui/AddSpotMarketDetailsCard.tsx @@ -14,7 +14,7 @@ export function AddSpotMarketDetailsCard(props: { }) { const { ix, index, result, info, innerCards, childIndex } = props; - const spotMarket = spotMarketFromIndex(ix, info.marketIndex); + const spotMarket = info.marketIndex !== undefined ? spotMarketFromIndex(ix, info.marketIndex) : undefined; return ( {spotMarket} )} - - Market index - {info.marketIndex} - + {info.marketIndex !== undefined && ( + + Market index + {info.marketIndex} + + )} Maint leverage {info.maintLeverage} diff --git a/packages/decoder-mango/src/__tests__/decoder.spec.ts b/packages/decoder-mango/src/__tests__/decoder.spec.ts index cfd6980da..84168126e 100644 --- a/packages/decoder-mango/src/__tests__/decoder.spec.ts +++ b/packages/decoder-mango/src/__tests__/decoder.spec.ts @@ -13,7 +13,6 @@ import { decodePlacePerpOrder2, decodePlaceSpotOrder, decodeWithdraw, - parseMangoInstructionTitle, } from '../decoder'; import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS, TEST_KEYS } from './fixtures'; @@ -117,15 +116,16 @@ describe('decodeCancelPerpOrder', () => { }); }); -// Both AddSpotMarket and AddPerpMarket layouts lack a marketIndex field (it comes -// from account keys), so the decoder throws when accessing .marketIndex.toNumber(). +// Neither AddSpotMarket nor AddPerpMarket layout carries a marketIndex field (it's +// assigned from mango group state), so it decodes as undefined rather than throwing. describe.each([ ['decodeAddSpotMarket', decodeAddSpotMarket, ENCODED_INSTRUCTIONS.AddSpotMarket], ['decodeAddPerpMarket', decodeAddPerpMarket, ENCODED_INSTRUCTIONS.AddPerpMarket], ] as const)('%s', (_name, decodeFn, data) => { - it('should throw because layout does not include marketIndex field', () => { + it('should decode without a marketIndex field', () => { const ix = makeInstruction(data, programId); - expect(() => decodeFn(ix)).toThrow(); + const result = decodeFn(ix); + expect(result.marketIndex).toBeUndefined(); }); }); @@ -153,22 +153,3 @@ describe('decodeConsumeEvents', () => { expect(result.perpMarket.pubkey).toEqual(TEST_KEYS[2]); }); }); - -describe('parseMangoInstructionTitle', () => { - it.each([ - 'Deposit', - 'Withdraw', - 'AddToBasket', - 'PlaceSpotOrder', - 'CancelSpotOrder', - 'PlacePerpOrder', - 'PlacePerpOrder2', - 'CancelPerpOrder', - 'AddSpotMarket', - 'AddPerpMarket', - 'ChangePerpMarketParams', - ] as const)('should parse %s instruction', title => { - const ix = makeInstruction(ENCODED_INSTRUCTIONS[title], programId); - expect(parseMangoInstructionTitle(ix)).toBe(title); - }); -}); diff --git a/packages/decoder-mango/src/__tests__/detection.spec.ts b/packages/decoder-mango/src/__tests__/detection.spec.ts index 3b538d105..dde2b650e 100644 --- a/packages/decoder-mango/src/__tests__/detection.spec.ts +++ b/packages/decoder-mango/src/__tests__/detection.spec.ts @@ -1,7 +1,9 @@ +import { MangoInstructionLayout } from '@blockworks-foundation/mango-client'; import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; import { describe, expect, it } from 'vitest'; -import { isMangoInstruction } from '../detection'; +import { isMangoInstruction, parseMangoInstructionTitle } from '../detection'; +import { MANGO_INSTRUCTION_NAMES } from '../instruction-names'; import { ENCODED_INSTRUCTIONS, makeInstruction, MANGO_PROGRAM_IDS } from './fixtures'; describe('isMangoInstruction', () => { @@ -31,3 +33,32 @@ describe('isMangoInstruction', () => { expect(isMangoInstruction(ix)).toBe(false); }); }); + +describe('parseMangoInstructionTitle', () => { + it.each([ + 'Deposit', + 'Withdraw', + 'AddToBasket', + 'PlaceSpotOrder', + 'CancelSpotOrder', + 'PlacePerpOrder', + 'PlacePerpOrder2', + 'CancelPerpOrder', + 'AddSpotMarket', + 'AddPerpMarket', + 'ChangePerpMarketParams', + ] as const)('should parse %s instruction', title => { + const ix = makeInstruction(ENCODED_INSTRUCTIONS[title], MANGO_PROGRAM_IDS.mainnet); + expect(parseMangoInstructionTitle(ix)).toBe(title); + }); + + it('should match the layout registry exactly', () => { + const { registry } = MangoInstructionLayout as unknown as { + registry: Record; + }; + const expected = new Map( + Object.entries(registry).map(([index, variant]) => [Number(index), variant.property]), + ); + expect(MANGO_INSTRUCTION_NAMES).toEqual(expected); + }); +}); diff --git a/packages/decoder-mango/src/decoder.ts b/packages/decoder-mango/src/decoder.ts index 389f40924..86acc21af 100644 --- a/packages/decoder-mango/src/decoder.ts +++ b/packages/decoder-mango/src/decoder.ts @@ -211,7 +211,8 @@ export const decodeChangePerpMarketParams = (ix: TransactionInstruction): Change }; export type AddSpotMarket = { - marketIndex: number; + // optional: not in the AddSpotMarket binary layout, it's assigned from mango group state + marketIndex?: number; maintLeverage: number; initLeverage: number; liquidationFee: number; @@ -226,7 +227,6 @@ export const decodeAddSpotMarket = (ix: TransactionInstruction): AddSpotMarket = initLeverage: decoded.AddSpotMarket.initLeverage.toNumber(), liquidationFee: decoded.AddSpotMarket.liquidationFee.toNumber(), maintLeverage: decoded.AddSpotMarket.maintLeverage.toNumber(), - marketIndex: decoded.AddSpotMarket.marketIndex.toNumber(), maxRate: decoded.AddSpotMarket.maxRate.toNumber(), optimalRate: decoded.AddSpotMarket.optimalRate.toNumber(), optimalUtil: decoded.AddSpotMarket.optimalUtil.toNumber(), @@ -235,7 +235,8 @@ export const decodeAddSpotMarket = (ix: TransactionInstruction): AddSpotMarket = }; export type AddPerpMarket = { - marketIndex: number; + // optional: not in the AddPerpMarket binary layout, it's assigned from mango group state + marketIndex?: number; maintLeverage: number; initLeverage: number; liquidationFee: number; @@ -257,7 +258,6 @@ export const decodeAddPerpMarket = (ix: TransactionInstruction): AddPerpMarket = liquidationFee: decoded.AddPerpMarket.liquidationFee.toNumber(), maintLeverage: decoded.AddPerpMarket.maintLeverage.toNumber(), makerFee: decoded.AddPerpMarket.makerFee.toNumber(), - marketIndex: decoded.AddPerpMarket.marketIndex.toNumber(), maxDepthBps: decoded.AddPerpMarket.maxDepthBps.toNumber(), mngoPerPeriod: decoded.AddPerpMarket.mngoPerPeriod.toNumber(), quoteLotSize: decoded.AddPerpMarket.quoteLotSize.toNumber(), @@ -280,8 +280,3 @@ export type ConsumeEvents = { export const decodeConsumeEvents = (ix: TransactionInstruction): ConsumeEvents => ({ perpMarket: ix.keys[2], }); - -export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { - const decodedInstruction = MangoInstructionLayout.decode(instruction.data, 0); - return Object.keys(decodedInstruction)[0]; -}; diff --git a/packages/decoder-mango/src/detection.ts b/packages/decoder-mango/src/detection.ts index ea70e3821..3294a25be 100644 --- a/packages/decoder-mango/src/detection.ts +++ b/packages/decoder-mango/src/detection.ts @@ -1,5 +1,6 @@ import { type TransactionInstruction } from '@solana/web3.js'; +import { MANGO_INSTRUCTION_NAMES } from './instruction-names'; import { MANGO_PROGRAM_IDS } from './program-ids'; const programIds = new Set(Object.values(MANGO_PROGRAM_IDS).map(id => id.toBase58())); @@ -7,4 +8,15 @@ const programIds = new Set(Object.values(MANGO_PROGRAM_IDS).map(id => id.toBase5 export const isMangoInstruction = (instruction: TransactionInstruction): boolean => programIds.has(instruction.programId.toBase58()); +export const parseMangoInstructionTitle = (instruction: TransactionInstruction): string => { + const { buffer, byteOffset, byteLength } = instruction.data; + // Mango instruction data is a buffer-layout union keyed by a leading u32 LE discriminator. + const variant = new DataView(buffer, byteOffset, byteLength).getUint32(0, true); + const title = MANGO_INSTRUCTION_NAMES.get(variant); + if (title === undefined) { + throw new Error(`Unknown Mango instruction variant: ${variant}`); + } + return title; +}; + export { MANGO_PROGRAM_IDS }; diff --git a/packages/decoder-mango/src/index.ts b/packages/decoder-mango/src/index.ts index 98b99e3e5..12dacf1d7 100644 --- a/packages/decoder-mango/src/index.ts +++ b/packages/decoder-mango/src/index.ts @@ -1,4 +1,4 @@ -export { isMangoInstruction, MANGO_PROGRAM_IDS } from './detection'; +export { isMangoInstruction, MANGO_PROGRAM_IDS, parseMangoInstructionTitle } from './detection'; export { decodeAddPerpMarket, decodeAddSpotMarket, @@ -12,7 +12,6 @@ export { decodePlacePerpOrder2, decodePlaceSpotOrder, decodeWithdraw, - parseMangoInstructionTitle, } from './decoder'; export type { AddPerpMarket, diff --git a/packages/decoder-mango/src/instruction-names.ts b/packages/decoder-mango/src/instruction-names.ts new file mode 100644 index 000000000..ea42bf56f --- /dev/null +++ b/packages/decoder-mango/src/instruction-names.ts @@ -0,0 +1,76 @@ +// Mango v3 instruction index → name (frozen protocol), hardcoded so parseMangoInstructionTitle stays free of @blockworks-foundation/mango-client; cross-checked against the real layout registry in detection.spec.ts. +export const MANGO_INSTRUCTION_NAMES = new Map([ + [0, 'InitMangoGroup'], + [1, 'InitMangoAccount'], + [2, 'Deposit'], + [3, 'Withdraw'], + [4, 'AddSpotMarket'], + [5, 'AddToBasket'], + [6, 'Borrow'], + [7, 'CachePrices'], + [8, 'CacheRootBanks'], + [9, 'PlaceSpotOrder'], + [10, 'AddOracle'], + [11, 'AddPerpMarket'], + [12, 'PlacePerpOrder'], + [13, 'CancelPerpOrderByClientId'], + [14, 'CancelPerpOrder'], + [15, 'ConsumeEvents'], + [16, 'CachePerpMarkets'], + [17, 'UpdateFunding'], + [18, 'SetOracle'], + [19, 'SettleFunds'], + [20, 'CancelSpotOrder'], + [21, 'UpdateRootBank'], + [22, 'SettlePnl'], + [23, 'SettleBorrow'], + [24, 'ForceCancelSpotOrders'], + [25, 'ForceCancelPerpOrders'], + [26, 'LiquidateTokenAndToken'], + [27, 'LiquidateTokenAndPerp'], + [28, 'LiquidatePerpMarket'], + [29, 'SettleFees'], + [30, 'ResolvePerpBankruptcy'], + [31, 'ResolveTokenBankruptcy'], + [32, 'InitSpotOpenOrders'], + [33, 'RedeemMngo'], + [34, 'AddMangoAccountInfo'], + [35, 'DepositMsrm'], + [36, 'WithdrawMsrm'], + [37, 'ChangePerpMarketParams'], + [38, 'SetGroupAdmin'], + [39, 'CancelAllPerpOrders'], + [41, 'PlaceSpotOrder2'], + [42, 'InitAdvancedOrders'], + [43, 'AddPerpTriggerOrder'], + [44, 'RemoveAdvancedOrder'], + [45, 'ExecutePerpTriggerOrder'], + [46, 'CreatePerpMarket'], + [47, 'ChangePerpMarketParams2'], + [48, 'UpdateMarginBasket'], + [49, 'ChangeMaxMangoAccounts'], + [50, 'CloseMangoAccount'], + [51, 'CloseSpotOpenOrders'], + [52, 'CloseAdvancedOrders'], + [53, 'CreateDustAccount'], + [54, 'ResolveDust'], + [55, 'CreateMangoAccount'], + [56, 'UpgradeMangoAccountV0V1'], + [57, 'CancelPerpOrdersSide'], + [58, 'SetDelegate'], + [59, 'ChangeSpotMarketParams'], + [60, 'CreateSpotOpenOrders'], + [61, 'ChangeReferralFeeParams'], + [62, 'SetReferrerMemory'], + [63, 'RegisterReferrerId'], + [64, 'PlacePerpOrder2'], + [65, 'CancelAllSpotOrders'], + [66, 'Withdraw2'], + [67, 'SetMarketMode'], + [68, 'RemovePerpMarket'], + [69, 'SwapSpotMarket'], + [70, 'RemoveSpotMarket'], + [71, 'RemoveOracle'], + [72, 'LiquidateDelistingToken'], + [73, 'ForceSettlePerpPosition'], +]); From 2a3b657cbd4a4ebe3898a2d99d1dbd644c5d5257 Mon Sep 17 00:00:00 2001 From: Sergo Date: Mon, 15 Jun 2026 23:22:57 +0100 Subject: [PATCH 16/17] fix: codestyle --- packages/decoder-mango/src/__tests__/detection.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/decoder-mango/src/__tests__/detection.spec.ts b/packages/decoder-mango/src/__tests__/detection.spec.ts index dde2b650e..1af130e41 100644 --- a/packages/decoder-mango/src/__tests__/detection.spec.ts +++ b/packages/decoder-mango/src/__tests__/detection.spec.ts @@ -56,9 +56,7 @@ describe('parseMangoInstructionTitle', () => { const { registry } = MangoInstructionLayout as unknown as { registry: Record; }; - const expected = new Map( - Object.entries(registry).map(([index, variant]) => [Number(index), variant.property]), - ); + const expected = new Map(Object.entries(registry).map(([index, variant]) => [Number(index), variant.property])); expect(MANGO_INSTRUCTION_NAMES).toEqual(expected); }); }); From 316cc76e912b0cb1ca40510ca7ee21c630d6a5a5 Mon Sep 17 00:00:00 2001 From: Sergo Date: Tue, 16 Jun 2026 14:25:29 +0100 Subject: [PATCH 17/17] feat: improve handling cached exceptions --- packages/decoder-mango/src/market.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/decoder-mango/src/market.ts b/packages/decoder-mango/src/market.ts index 60c4fd3ba..f72a619a0 100644 --- a/packages/decoder-mango/src/market.ts +++ b/packages/decoder-mango/src/market.ts @@ -23,7 +23,11 @@ function getAccountInfo(clusterUrl: string, publicKey: PublicKey): Promise { + delete accountInfoCache[cacheKey]; + throw error; + }); accountInfoCache[cacheKey] = accountInfoPromise; return accountInfoPromise; }