From dd79b594f1013085dc26c4e8a457789d37124b4e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 03:38:10 +0000 Subject: [PATCH] feat(frontend): add Enoki zkLogin (Sign in with Google) Register Enoki's zkLogin wallets into dapp-kit so social sign-in appears in the existing ConnectModal and works with the app's current useSignAndExecuteTransaction flow unchanged. - Add @mysten/enoki dependency - RegisterEnokiWallets component in main.tsx (inside SuiClientProvider, above WalletProvider); no-ops without keys or on unsupported networks - VITE_ENOKI_API_KEY / VITE_GOOGLE_CLIENT_ID config (both public, optional) - Document the new env vars in the frontend README Gas sponsorship (covering both zkLogin and external-wallet users via a backend + Enoki private key + portal allowlist) is a planned follow-up. --- frontend/README.md | 22 +++-- frontend/package-lock.json | 180 ++++++++++++++++++++++++++++++---- frontend/package.json | 1 + frontend/src/config.ts | 11 +++ frontend/src/main.tsx | 45 ++++++++- frontend/tsconfig.tsbuildinfo | 2 +- 6 files changed, 228 insertions(+), 33 deletions(-) diff --git a/frontend/README.md b/frontend/README.md index aedf0471..314b264f 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -33,16 +33,18 @@ implementation to real Sui SDK / indexer / WSS calls is a contained change. ## Environment variables -The frontend reads exactly three `VITE_`-prefixed variables. **All three have -defaults wired for local dev, so none are strictly required** — set them to -point at a non-local environment. Define them in `frontend/.env.local` (Vite -loads it automatically; not committed). - -| Variable | Required | Default | Purpose | -| -------------------- | -------- | ------------------------ | ------- | -| `VITE_ENVIRONMENT` | No | `testnet` | Selects which deployment block to read from `rust-backend/deployments.json`. One of `mainnet` \| `testnet` \| `devnet`. Drives the package / protocol-config / treasury ids (`src/config.ts`) **and** the wallet's default Sui network (`src/main.tsx`). An environment with no published deployment leaves those ids unset and the app falls back to its empty / "no deployment configured" states. | -| `VITE_API_BASE_URL` | No | `http://127.0.0.1:9003` | Base URL of the Rust `api-service` (REST). | -| `VITE_QUOTING_WS_URL`| No | `ws://127.0.0.1:9002/` | WebSocket URL of the quoting service. | +The frontend reads a handful of `VITE_`-prefixed variables. **All have defaults +wired for local dev (or are optional), so none are strictly required** — set +them to point at a non-local environment or to enable social sign-in. Define +them in `frontend/.env.local` (Vite loads it automatically; not committed). + +| Variable | Required | Default | Purpose | +| ---------------------- | -------- | ------------------------ | ------- | +| `VITE_ENVIRONMENT` | No | `testnet` | Selects which deployment block to read from `rust-backend/deployments.json`. One of `mainnet` \| `testnet` \| `devnet`. Drives the package / protocol-config / treasury ids (`src/config.ts`) **and** the wallet's default Sui network (`src/main.tsx`). An environment with no published deployment leaves those ids unset and the app falls back to its empty / "no deployment configured" states. | +| `VITE_API_BASE_URL` | No | `http://127.0.0.1:9003` | Base URL of the Rust `api-service` (REST). | +| `VITE_QUOTING_WS_URL` | No | `ws://127.0.0.1:9002/` | WebSocket URL of the quoting service. | +| `VITE_ENOKI_API_KEY` | No | _(unset)_ | Enoki **public** frontend API key (Enoki Developer Portal). Enables zkLogin "Sign in with Google" in the connect modal. When unset, social login is hidden and only browser-extension wallets are offered. | +| `VITE_GOOGLE_CLIENT_ID`| No | _(unset)_ | Google OAuth 2.0 **web** client id. Required alongside `VITE_ENOKI_API_KEY` to register the Enoki Google wallet. Both must be set for social login to appear. Enoki only supports `mainnet`/`testnet` (not `devnet`). | Contract ids (package, `ProtocolConfig`, `Treasury`) are **not** env vars — they come from `deployments.json` keyed by `VITE_ENVIRONMENT`. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7c9c8759..8daac722 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@mysten/dapp-kit": "^1.0.6", + "@mysten/enoki": "^1.0.8", "@mysten/sui": "^2.17.0", "@tanstack/react-query": "^5.100.11", "react": "^18.3.1", @@ -82,7 +83,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1409,6 +1409,34 @@ } } }, + "node_modules/@mysten/enoki": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mysten/enoki/-/enoki-1.0.8.tgz", + "integrity": "sha512-ZFWAnLavMEroBzZyiBNawAttkSLlpKgTTK3/irQW5jSNEP/G9U8+ZGeLpTnt78761b+9r13IEWmWbVlHx3hCuA==", + "license": "Apache-2.0", + "dependencies": { + "@mysten/utils": "^0.3.3", + "@mysten/wallet-standard": "^0.20.3", + "@mysten/webcrypto-signer": "^0.1.2", + "@nanostores/react": "^1.0.0", + "@wallet-standard/ui": "^1.0.1", + "idb-keyval": "^6.2.2", + "nanostores": "^1.1.0" + }, + "peerDependencies": { + "@mysten/sui": "^2.16.3", + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/@mysten/slush-wallet": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@mysten/slush-wallet/-/slush-wallet-1.0.5.tgz", @@ -1429,7 +1457,6 @@ "resolved": "https://registry.npmjs.org/@mysten/sui/-/sui-2.17.0.tgz", "integrity": "sha512-TrS1BCPm4V6rC69MBejmcqnKIyJ5t1iksax4XA9jWarZxAAp9LMmdxEBebejyDT/yAJxYIf3fNm5WBkHE2qnIw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "@mysten/bcs": "^2.0.5", @@ -1472,6 +1499,21 @@ "@mysten/sui": "*" } }, + "node_modules/@mysten/webcrypto-signer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@mysten/webcrypto-signer/-/webcrypto-signer-0.1.2.tgz", + "integrity": "sha512-MFI64BrUoyI05zZ1GXskZc8ohqCS9fM6StS/RO71YtX9pi7ZLTLdhUM7FEfgCYoSNsJHahG23JxvG0cfgiX/DQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^2.0.1" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@mysten/sui": "^2.16.2" + } + }, "node_modules/@mysten/window-wallet-core": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@mysten/window-wallet-core/-/window-wallet-core-0.1.6.tgz", @@ -1483,6 +1525,25 @@ "valibot": "^1.2.0" } }, + "node_modules/@nanostores/react": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nanostores/react/-/react-1.1.0.tgz", + "integrity": "sha512-MbH35fjhcf7LAubYX5vhOChYUfTLzNLqH/mBGLVsHkcvjy0F8crO1WQwdmQ2xKbAmtpalDa2zBt3Hlg5kqr8iw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "nanostores": "^1.2.0", + "react": ">=18.0.0" + } + }, "node_modules/@noble/curves": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz", @@ -2172,7 +2233,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.11.tgz", "integrity": "sha512-J0f9s5x3LE1450nNNfYx+e/n0DMa0uOBdFJUy5r0RvmsXd4nB/n0rbHtHI1vYXhikNFan+wf51p6Tmp4c8ucrg==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.100.11" }, @@ -2249,7 +2309,6 @@ "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2261,7 +2320,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -2349,12 +2407,12 @@ } }, "node_modules/@wallet-standard/base": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@wallet-standard/base/-/base-1.1.0.tgz", - "integrity": "sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/base/-/base-1.1.1.tgz", + "integrity": "sha512-gggIHTtxicF9XFMQ12DkfS6NAG92Ak795JeSA7f2whAQ6Y3AkMWWuCMxSZXG2NIPN42kEaZSNVjqMsJRaJRxMQ==", "license": "Apache-2.0", "engines": { - "node": ">=16" + "node": ">=22" } }, "node_modules/@wallet-standard/core": { @@ -2374,9 +2432,9 @@ } }, "node_modules/@wallet-standard/errors": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@wallet-standard/errors/-/errors-0.1.1.tgz", - "integrity": "sha512-V8Ju1Wvol8i/VDyQOHhjhxmMVwmKiwyxUZBnHhtiPZJTWY0U/Shb2iEWyGngYEbAkp2sGTmEeNX1tVyGR7PqNw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@wallet-standard/errors/-/errors-0.1.2.tgz", + "integrity": "sha512-oEzKUqJefKby6wcIvaJgrSEe/uNn/rnqkJ0P/85K+h0i5Tdo9E3L22VWq/j5K1e8hHMnZd6LgaIr8m/Wn7X/Ng==", "license": "Apache-2.0", "dependencies": { "chalk": "^5.4.1", @@ -2386,7 +2444,7 @@ "errors": "bin/cli.mjs" }, "engines": { - "node": ">=16" + "node": ">=22" } }, "node_modules/@wallet-standard/features": { @@ -2401,6 +2459,75 @@ "node": ">=16" } }, + "node_modules/@wallet-standard/ui": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wallet-standard/ui/-/ui-1.0.3.tgz", + "integrity": "sha512-H2QJTR5m3PeHi06q8kj4TiRf3U+fEe6W+jw+hztPkjTRBVDeCIrwRhbdgAyI8yaGc7dBhzGYqhUQ9ecYj7x0rQ==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/ui-compare": "^1.0.3", + "@wallet-standard/ui-core": "^1.0.1", + "@wallet-standard/ui-features": "^1.0.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@wallet-standard/ui-compare": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wallet-standard/ui-compare/-/ui-compare-1.0.3.tgz", + "integrity": "sha512-AXst7auT0+5eIaj8MyUXZLopKWN2HwqiQNTVgcF80u9X9a/fsiiHi8tlY0dc/y/AIOM654cMpvpJqsJEZNV9pg==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "1.1.1", + "@wallet-standard/ui-core": "1.0.1", + "@wallet-standard/ui-registry": "1.1.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@wallet-standard/ui-core": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/ui-core/-/ui-core-1.0.1.tgz", + "integrity": "sha512-LFmsSjMw3wXfZgZ5eAFGdIJ1512dGTWIEeoPDKsc6mHf7cMU18tvtpe4bOL76lT9Qh1Gc+4+kdtwSkTuJ9L+jw==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "1.1.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@wallet-standard/ui-features": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wallet-standard/ui-features/-/ui-features-1.0.3.tgz", + "integrity": "sha512-3YgxBoDXZCsaSyCNMph7i7G57ap+C6Y+l3HmTEQKigzTGxPUJ2mvqpTPwdj8D2cVkTKBzu8/78wweV7I9eG1rA==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "1.1.1", + "@wallet-standard/errors": "0.1.2", + "@wallet-standard/ui-core": "1.0.1", + "@wallet-standard/ui-registry": "1.1.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@wallet-standard/ui-registry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/ui-registry/-/ui-registry-1.1.1.tgz", + "integrity": "sha512-vJDZ/K2xwIqkxiyWUhPb6s62bTMRbYPj0MCJa9lYsqHGZgMbKBI3ZjLJK5MzWow2VxoOfuR77HuJng+HDWt1hA==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "1.1.1", + "@wallet-standard/errors": "0.1.2", + "@wallet-standard/ui-core": "1.0.1" + }, + "engines": { + "node": ">=22" + } + }, "node_modules/@wallet-standard/wallet": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@wallet-standard/wallet/-/wallet-1.1.0.tgz", @@ -2458,7 +2585,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2729,11 +2855,16 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.0.tgz", "integrity": "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==", "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.5.tgz", + "integrity": "sha512-eKQkTnS0relYsSOYomx8ozIbmdsQCKUdhyuIaQ2DZgKuaxtyQQMkyD/wlnQN32pO3yutN1b1L8uqwcDKaJd7/Q==", + "license": "Apache-2.0" + }, "node_modules/jose": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", @@ -2838,6 +2969,21 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanostores": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.3.0.tgz", + "integrity": "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, "node_modules/node-releases": { "version": "2.0.44", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", @@ -2891,7 +3037,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -2904,7 +3049,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -3121,7 +3265,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3224,7 +3367,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", diff --git a/frontend/package.json b/frontend/package.json index cbf8a9ad..27635153 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@mysten/dapp-kit": "^1.0.6", + "@mysten/enoki": "^1.0.8", "@mysten/sui": "^2.17.0", "@tanstack/react-query": "^5.100.11", "react": "^18.3.1", diff --git a/frontend/src/config.ts b/frontend/src/config.ts index b6237fe5..f5958cb0 100644 --- a/frontend/src/config.ts +++ b/frontend/src/config.ts @@ -62,6 +62,17 @@ export type SupportedToken = { export let SUPPORTED_TOKENS: SupportedToken[] = []; +// Enoki zkLogin (social sign-in). Both values are public: `VITE_ENOKI_API_KEY` +// is Enoki's *public* frontend key and the Google client id is a public OAuth +// id — neither is a secret, so they are safe in the bundle. When either is +// unset the Enoki "Sign in with Google" wallet is simply not registered and the +// app falls back to browser-extension wallets only (so local dev needs no +// setup). See `RegisterEnokiWallets` in `main.tsx`. +export const ENOKI_API_KEY: string | undefined = import.meta.env + .VITE_ENOKI_API_KEY as string | undefined; +export const GOOGLE_CLIENT_ID: string | undefined = import.meta.env + .VITE_GOOGLE_CLIENT_ID as string | undefined; + // --- wire shapes returned by token-info ------------------------------------- type PackageInfoDto = { diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 6862f0c2..282c27d5 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,12 +1,24 @@ -import React from "react"; +import React, { useEffect } from "react"; import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { SuiClientProvider, WalletProvider, createNetworkConfig } from "@mysten/dapp-kit"; +import { + SuiClientProvider, + WalletProvider, + createNetworkConfig, + useSuiClientContext, +} from "@mysten/dapp-kit"; +import { isEnokiNetwork, registerEnokiWallets } from "@mysten/enoki"; import { getJsonRpcFullnodeUrl } from "@mysten/sui/jsonRpc"; import { BrowserRouter } from "react-router-dom"; import "@mysten/dapp-kit/dist/index.css"; import { App } from "./App"; -import { ENV, TOKEN_INFO_URL, initConfig } from "./config"; +import { + ENOKI_API_KEY, + ENV, + GOOGLE_CLIENT_ID, + TOKEN_INFO_URL, + initConfig, +} from "./config"; import "./theme"; import "./styles/aqua.css"; import "./styles/global.css"; @@ -19,11 +31,38 @@ const { networkConfig } = createNetworkConfig({ const queryClient = new QueryClient(); +// Registers Enoki's zkLogin wallets (e.g. "Sign in with Google") into dapp-kit's +// wallet registry so they appear in the existing ConnectModal and work with the +// app's `useSignAndExecuteTransaction` calls unchanged. Must render inside +// SuiClientProvider (for the client/network context) and above WalletProvider. +// No-ops when the Enoki key / Google client id aren't configured, or on a +// network Enoki doesn't support (e.g. devnet). +function RegisterEnokiWallets() { + const { client, network } = useSuiClientContext(); + + useEffect(() => { + if (!ENOKI_API_KEY || !GOOGLE_CLIENT_ID || !isEnokiNetwork(network)) return; + + const { unregister } = registerEnokiWallets({ + apiKey: ENOKI_API_KEY, + providers: { + google: { clientId: GOOGLE_CLIENT_ID }, + }, + client, + network, + }); + return unregister; + }, [client, network]); + + return null; +} + function renderApp() { ReactDOM.createRoot(document.getElementById("root")!).render( + diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo index fef75ed0..fcfb1890 100644 --- a/frontend/tsconfig.tsbuildinfo +++ b/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/theme.ts","./src/types.ts","./src/api/client.ts","./src/api/pyth.ts","./src/api/quoting.ts","./src/api/quotingclient.ts","./src/api/usebuckets.ts","./src/api/usecalltokenlots.ts","./src/api/useownedcalloptions.ts","./src/api/usepositions.ts","./src/api/usepythprice.ts","./src/api/userfq.ts","./src/components/actionmodal.tsx","./src/components/amountinput.tsx","./src/components/bucketbar.tsx","./src/components/confirmmodal.tsx","./src/components/header.tsx","./src/components/livebuckets.tsx","./src/components/panels.tsx","./src/components/positioncards.tsx","./src/components/quotefeed.tsx","./src/components/striketiles.tsx","./src/components/tideline.tsx","./src/components/toast.tsx","./src/components/wavehero.tsx","./src/mocks/activity.ts","./src/mocks/composer.ts","./src/screens/activity.tsx","./src/screens/composer.tsx","./src/screens/dashboard.tsx","./src/state/dashboard.ts","./src/tx/dashboard.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/config.ts","./src/main.tsx","./src/theme.ts","./src/types.ts","./src/api/client.ts","./src/api/pyth.ts","./src/api/quoting.ts","./src/api/quotingClient.ts","./src/api/useAdminCap.ts","./src/api/useBuckets.ts","./src/api/useCallTokenLots.ts","./src/api/useCoinBalance.ts","./src/api/useOwnedCallOptions.ts","./src/api/useOwnedPositions.ts","./src/api/usePositions.ts","./src/api/usePythPrice.ts","./src/api/useRfq.ts","./src/components/ActionModal.tsx","./src/components/AmountInput.tsx","./src/components/BucketBar.tsx","./src/components/ConfirmModal.tsx","./src/components/Header.tsx","./src/components/LiveBuckets.tsx","./src/components/Panels.tsx","./src/components/PositionCards.tsx","./src/components/QuoteFeed.tsx","./src/components/StrikeTiles.tsx","./src/components/Tideline.tsx","./src/components/Toast.tsx","./src/components/WaveHero.tsx","./src/mocks/activity.ts","./src/screens/Activity.tsx","./src/screens/Admin.tsx","./src/screens/Composer.tsx","./src/screens/Dashboard.tsx","./src/screens/Faucet.tsx","./src/state/composer.ts","./src/state/dashboard.ts","./src/tx/admin.ts","./src/tx/composer.ts","./src/tx/dashboard.ts","./src/tx/faucet.ts"],"version":"5.9.3"} \ No newline at end of file