From 4c3e68bfaff41cf057b39cb26e92a5c3dfe0dc21 Mon Sep 17 00:00:00 2001 From: poppyseeddev Date: Fri, 24 Apr 2026 16:18:30 +0200 Subject: [PATCH 1/5] refactor: adopt zama-summer-games infra patterns Scope is infra-only. FHECounter contract and RainbowKit auth stay as-is; only scripts, dev tooling, ABI-file structure, and SDK version change. Scripts - chain.sh now deploys FHECounter on top of the FHEVM cleartext host in one command (single-terminal flow) - deploy-local.sh -> deploy-localhost.sh, with env-var stubs so forge 1.x does not trip on unset SEPOLIA_RPC_URL / ETHERSCAN_API_KEY - generateTsAbis.ts emits per-contract files with .local.ts sidecars (gitignored), walks every run-*.json so incremental deploys do not drop reused addresses, and cleans stale artifacts Dev tooling - CI workflow (forge test, frontend typecheck/lint/build, prettier check, PR-scoped gitleaks scan) - husky + lint-staged (prettier + eslint on staged TS/JS, forge fmt on .sol) - .gitleaks.toml / .gitleaksignore - Root .prettierrc.json + .prettierignore, .env.example - postinstall regenerates ABIs so fresh clones boot without a manual step SDK v3 bump (+ upstream react-sdk patterns rolled in) - @zama-fhe/sdk + @zama-fhe/react-sdk -> ^3.0.0, viem -> ^2.47.12 - DappWrapperWithProviders: module-scoped signer/storage; IndexedDBStorage (persistent across reload) replaces memoryStorage; relayer still swaps per chain at runtime to keep RainbowKit multi-chain UX intact - Adopt query-based useUserDecrypt + useAllow/useIsAllowed gate - Keep local WagmiSigner: @zama-fhe/react-sdk@3.0.0 ships one that imports watchConnection from wagmi/actions, which wagmi does not export ABI files - Drop monolithic deployedContracts.ts in favor of FHECounter.ts + FHECounter.local.ts - Drop scaffold-eth useDeployedContractInfo + utils/helper/contract.ts (unused after the switch) - New utils/contract.ts exposes ContractDeployment + deploymentFor() helper Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 5 + .github/workflows/ci.yml | 119 ++++++ .gitignore | 11 +- .gitleaks.toml | 26 ++ .gitleaksignore | 3 + .husky/pre-commit | 1 + .prettierignore | 24 ++ .prettierrc.json | 9 + README.md | 124 ++++-- package.json | 38 +- packages/foundry/README.md | 14 +- packages/foundry/foundry.toml | 6 + .../components/DappWrapperWithProviders.tsx | 20 +- .../RainbowKitCustomConnectButton/index.tsx | 6 +- packages/nextjs/contracts/FHECounter.ts | 106 +++++ .../nextjs/contracts/deployedContracts.ts | 194 --------- .../fhecounter-example/useFHECounterWagmi.tsx | 62 ++- packages/nextjs/hooks/helper/index.ts | 1 - .../hooks/helper/useDeployedContractInfo.ts | 86 ---- packages/nextjs/package.json | 6 +- packages/nextjs/services/web3/wagmiSigner.ts | 20 +- packages/nextjs/styles/globals.css | 18 +- packages/nextjs/utils/contract.ts | 32 ++ packages/nextjs/utils/helper/contract.ts | 47 --- pnpm-lock.yaml | 399 +++++++++++++----- scripts/chain.sh | 48 ++- scripts/deploy-local.sh | 24 -- scripts/deploy-localhost.sh | 53 +++ scripts/deploy-sepolia.sh | 6 + scripts/generateTsAbis.ts | 280 +++++++++--- tsconfig.json | 5 +- 31 files changed, 1152 insertions(+), 641 deletions(-) create mode 100644 .env.example create mode 100644 .github/workflows/ci.yml create mode 100644 .gitleaks.toml create mode 100644 .gitleaksignore create mode 100644 .husky/pre-commit create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 packages/nextjs/contracts/FHECounter.ts delete mode 100644 packages/nextjs/contracts/deployedContracts.ts delete mode 100644 packages/nextjs/hooks/helper/useDeployedContractInfo.ts create mode 100644 packages/nextjs/utils/contract.ts delete mode 100644 packages/nextjs/utils/helper/contract.ts delete mode 100755 scripts/deploy-local.sh create mode 100755 scripts/deploy-localhost.sh diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..a6085c932 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# Sepolia deploy (consumed by scripts/deploy-sepolia.sh). +# Copy this file to .env.local and fill in values. +SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com +DEPLOYER_PRIVATE_KEY= +ETHERSCAN_API_KEY= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..3922508c5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,119 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + name: build + test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: pnpm install + run: pnpm install --frozen-lockfile + + # dependencies/ is gitignored; soldeer resolves deps from foundry.toml. + - name: Install contract deps + working-directory: packages/foundry + run: forge soldeer install + + - name: forge build + working-directory: packages/foundry + run: forge build --sizes + + - name: forge test + working-directory: packages/foundry + run: forge test -vv + + - name: frontend typecheck + run: pnpm --filter ./packages/nextjs check-types + + - name: frontend lint + run: pnpm --filter ./packages/nextjs lint + + - name: frontend build + env: + # scaffold.config.ts throws in production if this is unset. CI only + # exercises the build pipeline — a stub is enough since no RPC call + # actually happens during `next build`. + NEXT_PUBLIC_ALCHEMY_API_KEY: ci-stub + run: pnpm --filter ./packages/nextjs build + + - name: prettier check + run: pnpm exec prettier --check . + + gitleaks: + name: gitleaks + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # Use the OSS gitleaks binary directly — gitleaks-action@v2 requires a + # paid license for GitHub org repos. + - name: Install gitleaks + run: | + VERSION=8.18.4 + curl -sSfL \ + "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \ + | sudo tar -xz -C /usr/local/bin gitleaks + + # Scan only the commits introduced by the PR (or the newly-pushed commits + # on main). Scanning full history re-reports pre-existing findings that + # aren't this change's problem. `fetch-depth: 0` above ensures both ends + # of the range are present locally. + - name: Determine scan range + id: range + env: + EVENT_NAME: ${{ github.event_name }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PUSH_BEFORE: ${{ github.event.before }} + PUSH_AFTER: ${{ github.event.after }} + run: | + if [ "$EVENT_NAME" = "pull_request" ]; then + range="${PR_BASE_SHA}..${PR_HEAD_SHA}" + elif [ "$PUSH_BEFORE" = "0000000000000000000000000000000000000000" ]; then + # First push to a branch — no prior ref, fall back to scanning the + # tip commit only. + range="${PUSH_AFTER}~1..${PUSH_AFTER}" + else + range="${PUSH_BEFORE}..${PUSH_AFTER}" + fi + echo "range=$range" >> "$GITHUB_OUTPUT" + echo "scanning $range" + + - name: Scan new commits + run: | + gitleaks detect \ + --config .gitleaks.toml \ + --no-banner \ + --redact \ + --verbose \ + --log-opts="${{ steps.range.outputs.range }}" diff --git a/.gitignore b/.gitignore index c6163ceb6..a53844db8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,13 @@ packages/*/.env packages/*/.turbo packages/*/coverage tmp* -.vscode \ No newline at end of file +.vscode + +# anvil dumped chain state (from scripts/chain.sh) +.anvil-state.json + +# local (chainId 31337) deployment overlays — generated by pnpm generate +# after `pnpm deploy:localhost`. Per-machine; not committed. +packages/nextjs/contracts/*.local.ts + +CLAUDE.md diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 000000000..1e741443c --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,26 @@ +# gitleaks config — runs in CI only (see .github/workflows/ci.yml). +[extend] +useDefault = true + +[allowlist] +description = "Paths and literals that never contain real secrets" +paths = [ + # Public contract addresses + ABIs, auto-generated from forge broadcasts. + '''packages/nextjs/contracts/''', + # Example env files contain placeholder values only. + '''\.env\.example''', + # Foundry-installed libraries; not our code. + '''packages/foundry/lib/''', + '''packages/foundry/out/''', + '''packages/foundry/cache/''', + '''packages/foundry/broadcast/''', + '''packages/foundry/dependencies/''', + # Lockfile — registry URLs, not secrets. + '''pnpm-lock\.yaml''', +] + +# Anvil default account #0 — publicly documented in foundry's docs and in +# every FHEVM template. Not a real secret. +stopwords = [ + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", +] diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 000000000..7baf0a13c --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,3 @@ +# One-line-per-finding ignores: ::: +# Populate as needed for historical commits that tripped a rule but are +# known-safe (e.g. anvil devkeys in historical deploy scripts). diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..5ee7abd87 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm exec lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..8f6958478 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,24 @@ +# build outputs + caches +**/.next/ +**/out/ +**/node_modules/ +**/dist/ +**/build/ +**/*.tsbuildinfo + +# lockfiles — let pnpm own the format +pnpm-lock.yaml + +# foundry +packages/foundry/lib/ +packages/foundry/out/ +packages/foundry/cache/ +packages/foundry/broadcast/ +packages/foundry/dependencies/ + +# auto-generated — regenerated by scripts/generateTsAbis.ts +packages/nextjs/contracts/ + +# editor/IDE +.vercel/ +.vscode/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..5debb8caa --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/README.md b/README.md index b8e042075..069502e6a 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ FHEVM (Fully Homomorphic Encryption Virtual Machine) lets smart contracts comput - **Contracts**: Foundry, Solidity 0.8.27, [forge-fhevm](https://github.com/zama-ai/forge-fhevm) for host contracts + testing helpers - **Frontend**: Next.js 15 (App Router), React 19, wagmi, viem, RainbowKit, Tailwind + daisyUI -- **FHE SDK**: `@zama-fhe/sdk` + `@zama-fhe/react-sdk` v2 +- **FHE SDK**: `@zama-fhe/sdk` + `@zama-fhe/react-sdk` v3 - `RelayerCleartext` for local anvil (plaintext mirror executor — no KMS/gateway) - `RelayerWeb` for Sepolia (real relayer, WASM worker) +- **Tooling**: husky + lint-staged pre-commit (prettier + eslint + `forge fmt`), gitleaks scan in CI, GitHub Actions for forge test + frontend typecheck/lint/build ## Prerequisites @@ -27,18 +28,17 @@ FHEVM (Fully Homomorphic Encryption Virtual Machine) lets smart contracts comput pnpm install ``` +The `postinstall` hook regenerates `packages/nextjs/contracts/*.ts` from any existing broadcasts, and `prepare` installs husky hooks. + ### Local (recommended for development) -Three terminals: +Two terminals: ```bash -# 1. Start anvil + deploy the FHEVM cleartext host stack +# 1. Start anvil + deploy the FHEVM cleartext host stack + FHECounter pnpm chain -# 2. Deploy FHECounter + regenerate frontend ABIs -pnpm deploy:localhost - -# 3. Start the frontend +# 2. Start the frontend pnpm start ``` @@ -50,14 +50,21 @@ Open http://localhost:3000 and add the local network to MetaMask: Import an anvil dev account (10,000 ETH each) — e.g. private key `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` (address `0xf39F…2266`). +To redeploy `FHECounter` without restarting anvil, run `pnpm deploy:localhost` in a third terminal. + ### Sepolia -Add a `.env.local` at the repo root: +Copy the example env file and fill it in: ```bash -DEPLOYER_PRIVATE_KEY=0x... # deployer funded with Sepolia ETH +cp .env.example .env.local +``` + +```bash +# .env.local +DEPLOYER_PRIVATE_KEY=0x... # deployer funded with Sepolia ETH SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY -ETHERSCAN_API_KEY=... # optional, enables --verify +ETHERSCAN_API_KEY=... # optional, enables --verify ``` Add to `packages/nextjs/.env.local`: @@ -75,46 +82,69 @@ pnpm start # same frontend picks up the 11155111 entry automatically ## Scripts -| Command | What it does | -| ------------------------ | ----------------------------------------------------------------------- | -| `pnpm chain` | Starts anvil on 8545 + deploys FHEVM cleartext host stack | -| `pnpm deploy:localhost` | Deploys `FHECounter` to local anvil + regenerates frontend ABIs | -| `pnpm deploy:sepolia` | Deploys to Sepolia (reads `.env.local`) + regenerates frontend ABIs | -| `pnpm compile` | `forge build` on `packages/foundry` | -| `pnpm test` | `forge test` on `packages/foundry` | -| `pnpm generate` | Rebuilds `packages/nextjs/contracts/deployedContracts.ts` from foundry | -| `pnpm start` | `next dev` (http://localhost:3000) | -| `pnpm next:build` | Production build of the frontend | -| `pnpm next:check-types` | TypeScript check on the frontend | -| `pnpm lint` | Lint the frontend | -| `pnpm format` | Prettier on the frontend | +| Command | What it does | +| ------------------------ | -------------------------------------------------------------------------------------------- | +| `pnpm chain` | Starts anvil on 8545 + deploys FHEVM cleartext host stack + `FHECounter` | +| `pnpm deploy:localhost` | Deploys `FHECounter` to local anvil + regenerates frontend ABIs | +| `pnpm deploy:sepolia` | Deploys to Sepolia (reads `.env.local`) + regenerates frontend ABIs | +| `pnpm contracts:install` | `forge soldeer install` in `packages/foundry` | +| `pnpm contracts:build` | `forge build` in `packages/foundry` | +| `pnpm contracts:test` | `forge test -vv` in `packages/foundry` | +| `pnpm compile` | Alias for `contracts:build` | +| `pnpm test` | Alias for `contracts:test` (forge only — no frontend tests) | +| `pnpm generate` | Emits `packages/nextjs/contracts/.ts` + `.local.ts` from forge broadcasts + out/ | +| `pnpm start` | `next dev` (http://localhost:3000) | +| `pnpm next:build` | Production build of the frontend | +| `pnpm next:check-types` | TypeScript check on the frontend | +| `pnpm lint` | Lint the frontend | +| `pnpm format` | Prettier write on the whole repo | +| `pnpm format:check` | Prettier check (no write) — used by CI | ## Project structure ``` fhevm-react-template/ -├── packages/ -│ ├── foundry/ # Solidity contracts + forge tests -│ │ ├── src/ -│ │ │ └── FHECounter.sol -│ │ ├── script/ -│ │ │ └── DeployFHECounter.s.sol -│ │ ├── test/ -│ │ │ └── FHECounter.t.sol # uses forge-fhevm's FhevmTest -│ │ ├── foundry.toml -│ │ └── remappings.txt -│ └── nextjs/ # React frontend -│ ├── app/ -│ ├── components/ -│ │ └── DappWrapperWithProviders.tsx # wires ZamaProvider + relayer -│ ├── hooks/ -│ │ └── fhecounter-example/useFHECounterWagmi.tsx -│ ├── contracts/ -│ │ └── deployedContracts.ts # autogenerated from forge broadcast -│ └── scaffold.config.ts -└── scripts/ # chain, deploy, ABI generator +├── .github/workflows/ci.yml # forge test + frontend typecheck/lint/build + gitleaks +├── .husky/pre-commit # runs lint-staged +├── .gitleaks.toml # gitleaks allowlist/stopwords +├── .prettierrc.json # root prettier config +├── .env.example # copy to .env.local for Sepolia deploys +├── scripts/ +│ ├── chain.sh # anvil + FHEVM host + FHECounter +│ ├── deploy-localhost.sh +│ ├── deploy-sepolia.sh +│ └── generateTsAbis.ts # emits per-contract .ts + .local.ts sidecars +└── packages/ + ├── foundry/ # Solidity contracts + forge tests + │ ├── src/FHECounter.sol + │ ├── script/DeployFHECounter.s.sol + │ ├── test/FHECounter.t.sol # inherits forge-fhevm's FhevmTest + │ ├── foundry.toml + │ └── remappings.txt + └── nextjs/ # React frontend + ├── app/ + ├── components/ + │ └── DappWrapperWithProviders.tsx # wires ZamaProvider + relayer + ├── hooks/ + │ └── fhecounter-example/useFHECounterWagmi.tsx + ├── services/web3/ + │ └── wagmiSigner.ts # local workaround for SDK 3.0.0's broken WagmiSigner + ├── contracts/ + │ ├── FHECounter.ts # autogenerated — non-local (Sepolia) deployments, tracked + │ └── FHECounter.local.ts # autogenerated — chainId 31337 overlay, gitignored + ├── utils/contract.ts # ContractDeployment type + deploymentFor() helper + └── scaffold.config.ts ``` +### ABI generation + +`scripts/generateTsAbis.ts` walks `packages/foundry/broadcast/*/*/run-*.json` and `packages/foundry/out/` to produce **one pair of files per contract**: + +- `packages/nextjs/contracts/.ts` — non-local chain entries (Sepolia, mainnet, etc.). Tracked in git. +- `packages/nextjs/contracts/.local.ts` — chainId 31337 overlay. Gitignored. + +The main file imports its sidecar and merges at module load, so consumers stay agnostic to where a deployment lives. A `postinstall` hook runs the generator on every `pnpm install`, and a fresh clone with no broadcasts gets empty stub sidecars automatically so imports resolve. + ## Troubleshooting ### MetaMask nonce mismatch after restarting anvil @@ -131,6 +161,14 @@ MetaMask also caches view-function results across reloads. After restarting anvi The Zama relayer SDK requires EIP-55 checksummed addresses. `scripts/generateTsAbis.ts` already checksums via viem's `getAddress()` — if you see this error, rerun `pnpm generate` after a deploy. +### Sepolia entry disappeared from `FHECounter.ts` + +Shouldn't happen on current `main` — the generator preserves the tracked REMOTE entries when a run only produces local broadcasts. If it does, rerun `pnpm deploy:sepolia` to repopulate. + +### `pnpm install` asks for a package manager version + +The root `package.json` pins `packageManager: "pnpm@10.18.3"`. Upgrade pnpm (`corepack prepare pnpm@10.18.3 --activate`) or match your local install. + ### Sepolia deploy fails with weird path errors Your `.env.local` likely has a typo (double `==`, spaces around `=`, quoted values with stray chars). Inspect and fix. diff --git a/package.json b/package.json index 137023064..705abb1a7 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.4.0", "private": true, "license": "BSD-3-Clause-Clear", + "packageManager": "pnpm@10.18.3", "engines": { "node": ">=20.0.0" }, @@ -12,30 +13,49 @@ ] }, "scripts": { - "chain": "sh scripts/chain.sh", + "chain": "./scripts/chain.sh", + "deploy:localhost": "./scripts/deploy-localhost.sh", + "deploy:sepolia": "./scripts/deploy-sepolia.sh", + "generate": "ts-node ./scripts/generateTsAbis.ts", + "postinstall": "ts-node ./scripts/generateTsAbis.ts || true", + "contracts:install": "cd packages/foundry && forge soldeer install", + "contracts:build": "cd packages/foundry && forge build", + "contracts:test": "cd packages/foundry && forge test -vv", "compile": "cd packages/foundry && forge build", "test": "cd packages/foundry && forge test", - "deploy:localhost": "sh scripts/deploy-local.sh", - "deploy:sepolia": "sh scripts/deploy-sepolia.sh", - "generate": "ts-node ./scripts/generateTsAbis.ts", - "format": "pnpm next:format", + "start": "pnpm --filter ./packages/nextjs dev", + "format": "prettier --write .", + "format:check": "prettier --check .", "lint": "pnpm next:lint", "next:build": "pnpm --filter ./packages/nextjs build", "next:check-types": "pnpm --filter ./packages/nextjs check-types", - "next:format": "pnpm --filter ./packages/nextjs format", "next:lint": "pnpm --filter ./packages/nextjs lint", "next:serve": "pnpm --filter ./packages/nextjs serve", - "start": "pnpm --filter ./packages/nextjs dev", "vercel": "pnpm --filter ./packages/nextjs vercel", "vercel:login": "pnpm --filter ./packages/nextjs vercel:login", - "vercel:yolo": "pnpm --filter ./packages/nextjs vercel:yolo" + "vercel:yolo": "pnpm --filter ./packages/nextjs vercel:yolo", + "prepare": "husky" + }, + "lint-staged": { + "packages/nextjs/**/*.{ts,tsx,js,jsx}": [ + "pnpm exec prettier --write", + "pnpm --filter ./packages/nextjs exec eslint --fix" + ], + "*.{json,md,yml,yaml,css}": [ + "pnpm exec prettier --write" + ], + "packages/foundry/**/*.sol": [ + "forge fmt" + ] }, "devDependencies": { "@types/node": "^22.7.5", + "husky": "^9.1.7", + "lint-staged": "^16.4.0", "prettier": "^3.6.2", "ts-node": "^10.9.2", "typescript": "^5.9.2", - "viem": "^2.47.10" + "viem": "^2.47.12" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/packages/foundry/README.md b/packages/foundry/README.md index ade1f0032..431a66210 100644 --- a/packages/foundry/README.md +++ b/packages/foundry/README.md @@ -71,13 +71,13 @@ fhevm-foundry-template/ ## Available Scripts -| Script | Description | -| ------------------------------------------ | ------------------------ | -| `forge build` | Compile all contracts | -| `forge test -vvv` | Run all tests | -| `forge test --match-test test_name -vvv` | Run a single test | -| `forge fmt` | Format code | -| `forge fmt --check` | Check formatting | +| Script | Description | +| ---------------------------------------- | --------------------- | +| `forge build` | Compile all contracts | +| `forge test -vvv` | Run all tests | +| `forge test --match-test test_name -vvv` | Run a single test | +| `forge fmt` | Format code | +| `forge fmt --check` | Check formatting | ## Documentation diff --git a/packages/foundry/foundry.toml b/packages/foundry/foundry.toml index 22233ef1f..c0624afb9 100644 --- a/packages/foundry/foundry.toml +++ b/packages/foundry/foundry.toml @@ -17,6 +17,12 @@ runs = 256 [fmt] line_length = 120 +[rpc_endpoints] +sepolia = "${SEPOLIA_RPC_URL}" + +[etherscan] +sepolia = { key = "${ETHERSCAN_API_KEY}" } + [dependencies] "@openzeppelin-contracts" = "5.1.0" "@openzeppelin-contracts-upgradeable" = "5.1.0" diff --git a/packages/nextjs/components/DappWrapperWithProviders.tsx b/packages/nextjs/components/DappWrapperWithProviders.tsx index b15acf4b7..c40ff58ea 100644 --- a/packages/nextjs/components/DappWrapperWithProviders.tsx +++ b/packages/nextjs/components/DappWrapperWithProviders.tsx @@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from "react"; import { RainbowKitProvider, darkTheme, lightTheme } from "@rainbow-me/rainbowkit"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ZamaProvider } from "@zama-fhe/react-sdk"; -import { RelayerWeb, SepoliaConfig, type ZamaSDKEvent, memoryStorage } from "@zama-fhe/sdk"; +import { IndexedDBStorage, RelayerWeb, SepoliaConfig, type ZamaSDKEvent } from "@zama-fhe/sdk"; import { RelayerCleartext, hardhatCleartextConfig } from "@zama-fhe/sdk/cleartext"; import { AppProgressBar as ProgressBar } from "next-nprogress-bar"; import { useTheme } from "next-themes"; @@ -13,9 +13,17 @@ import { WagmiProvider, useChainId } from "wagmi"; import { Header } from "~~/components/Header"; import { BlockieAvatar } from "~~/components/helper"; import { wagmiConfig } from "~~/services/web3/wagmiConfig"; +// Local re-implementation — see services/web3/wagmiSigner.ts for why we can't +// use @zama-fhe/react-sdk/wagmi directly in SDK 3.0.0. import { WagmiSigner } from "~~/services/web3/wagmiSigner"; +// Module-scoped — the signer, keypair store and session store are chain-agnostic +// and there's no reason to rebuild them on chain change. IndexedDBStorage lets +// the keypair + EIP-712 session survive page reloads, matching Zama's hosted +// app patterns. const signer = new WagmiSigner({ config: wagmiConfig }); +const storage = new IndexedDBStorage("KeypairStore", 1); +const sessionStorage = new IndexedDBStorage("SignatureStore", 1); export const queryClient = new QueryClient({ defaultOptions: { @@ -37,7 +45,7 @@ const ZamaRuntimeProvider = ({ children }: { children: React.ReactNode }) => { return new RelayerWeb({ getChainId: () => signer.getChainId(), transports: { - 11155111: SepoliaConfig, + [SepoliaConfig.chainId]: SepoliaConfig, }, }); }, [chainId]); @@ -53,7 +61,13 @@ const ZamaRuntimeProvider = ({ children }: { children: React.ReactNode }) => { } return ( - + {children} ); diff --git a/packages/nextjs/components/helper/RainbowKitCustomConnectButton/index.tsx b/packages/nextjs/components/helper/RainbowKitCustomConnectButton/index.tsx index ec427c852..cf0b7279f 100644 --- a/packages/nextjs/components/helper/RainbowKitCustomConnectButton/index.tsx +++ b/packages/nextjs/components/helper/RainbowKitCustomConnectButton/index.tsx @@ -28,7 +28,11 @@ export const RainbowKitCustomConnectButton = () => { {(() => { if (!connected) { return ( - ); diff --git a/packages/nextjs/contracts/FHECounter.ts b/packages/nextjs/contracts/FHECounter.ts new file mode 100644 index 000000000..50ad20a60 --- /dev/null +++ b/packages/nextjs/contracts/FHECounter.ts @@ -0,0 +1,106 @@ +/** + * This file is autogenerated. Do not edit by hand — run `pnpm generate`. + * + * Non-local chain deployments live here; local (chainId 31337) deployments + * live in `./FHECounter.local.ts` (gitignored) and are merged in at module + * load. Import by name: `import { FHECounter } from "~~/contracts/FHECounter";` + */ +import { FHECounter as FHECounter_LOCAL } from "./FHECounter.local"; +import type { ContractDeployment } from "~~/utils/contract"; + +const REMOTE = { + 11155111: { + address: "0x3CC73d13B88cbE6d31Ff2Fa8c5A7b12ef68f96c9", + abi: [ + { + type: "function", + name: "confidentialProtocolId", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "decrement", + inputs: [ + { + name: "inputEuint32", + type: "bytes32", + internalType: "externalEuint32", + }, + { + name: "inputProof", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getCount", + inputs: [], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "euint32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [ + { + name: "inputEuint32", + type: "bytes32", + internalType: "externalEuint32", + }, + { + name: "inputProof", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "error", + name: "SenderNotAllowedToUseHandle", + inputs: [ + { + name: "handle", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ZamaProtocolUnsupported", + inputs: [], + }, + ], + deployedOnBlock: 10678430, + }, +} as const; + +export const FHECounter = { + ...REMOTE, + ...FHECounter_LOCAL, +} as const satisfies Partial>; diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts deleted file mode 100644 index a01bce6fb..000000000 --- a/packages/nextjs/contracts/deployedContracts.ts +++ /dev/null @@ -1,194 +0,0 @@ -/** - * This file is autogenerated. - * You should not edit it manually or your changes might be overwritten. - */ -import { GenericContractsDeclaration } from "~~/utils/helper/contract"; - -const deployedContracts = { - 31337: { - FHECounter: { - address: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82", - abi: [ - { - type: "function", - name: "confidentialProtocolId", - inputs: [], - outputs: [ - { - name: "", - type: "uint256", - internalType: "uint256", - }, - ], - stateMutability: "view", - }, - { - type: "function", - name: "decrement", - inputs: [ - { - name: "inputEuint32", - type: "bytes32", - internalType: "externalEuint32", - }, - { - name: "inputProof", - type: "bytes", - internalType: "bytes", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - name: "getCount", - inputs: [], - outputs: [ - { - name: "", - type: "bytes32", - internalType: "euint32", - }, - ], - stateMutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [ - { - name: "inputEuint32", - type: "bytes32", - internalType: "externalEuint32", - }, - { - name: "inputProof", - type: "bytes", - internalType: "bytes", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "error", - name: "SenderNotAllowedToUseHandle", - inputs: [ - { - name: "handle", - type: "bytes32", - internalType: "bytes32", - }, - { - name: "sender", - type: "address", - internalType: "address", - }, - ], - }, - { - type: "error", - name: "ZamaProtocolUnsupported", - inputs: [], - }, - ], - inheritedFunctions: {}, - deployedOnBlock: 14, - }, - }, - 11155111: { - FHECounter: { - address: "0x3CC73d13B88cbE6d31Ff2Fa8c5A7b12ef68f96c9", - abi: [ - { - type: "function", - name: "confidentialProtocolId", - inputs: [], - outputs: [ - { - name: "", - type: "uint256", - internalType: "uint256", - }, - ], - stateMutability: "view", - }, - { - type: "function", - name: "decrement", - inputs: [ - { - name: "inputEuint32", - type: "bytes32", - internalType: "externalEuint32", - }, - { - name: "inputProof", - type: "bytes", - internalType: "bytes", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - name: "getCount", - inputs: [], - outputs: [ - { - name: "", - type: "bytes32", - internalType: "euint32", - }, - ], - stateMutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [ - { - name: "inputEuint32", - type: "bytes32", - internalType: "externalEuint32", - }, - { - name: "inputProof", - type: "bytes", - internalType: "bytes", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "error", - name: "SenderNotAllowedToUseHandle", - inputs: [ - { - name: "handle", - type: "bytes32", - internalType: "bytes32", - }, - { - name: "sender", - type: "address", - internalType: "address", - }, - ], - }, - { - type: "error", - name: "ZamaProtocolUnsupported", - inputs: [], - }, - ], - inheritedFunctions: {}, - deployedOnBlock: 10678430, - }, - }, -} as const; - -export default deployedContracts satisfies GenericContractsDeclaration; diff --git a/packages/nextjs/hooks/fhecounter-example/useFHECounterWagmi.tsx b/packages/nextjs/hooks/fhecounter-example/useFHECounterWagmi.tsx index e3dc65499..0ba2c3c0a 100644 --- a/packages/nextjs/hooks/fhecounter-example/useFHECounterWagmi.tsx +++ b/packages/nextjs/hooks/fhecounter-example/useFHECounterWagmi.tsx @@ -1,43 +1,35 @@ "use client"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { useDeployedContractInfo } from "../helper"; import { useAllow, useEncrypt, useIsAllowed, useUserDecrypt } from "@zama-fhe/react-sdk"; import { ZERO_HANDLE, ZamaSDKEvents } from "@zama-fhe/sdk"; import { bytesToHex } from "viem"; import { useAccount, useChainId, useReadContract, useWriteContract } from "wagmi"; -import type { Contract } from "~~/utils/helper/contract"; -import type { AllowedChainIds } from "~~/utils/helper/networks"; +import { FHECounter } from "~~/contracts/FHECounter"; +import { deploymentFor } from "~~/utils/contract"; /** - * useFHECounterWagmi - FHE Counter hook using @zama-fhe/react-sdk v2 + wagmi + * FHE Counter hook using @zama-fhe/react-sdk v3 + wagmi. * - * What it does: - * - Reads the current encrypted counter via wagmi's useReadContract - * - Decrypts the handle on-demand using useUserDecrypt (query-based: handles keypair + EIP-712 + signing internally) - * - Encrypts inputs with useEncrypt and writes increment/decrement via useWriteContract + * - Reads the encrypted counter via wagmi's useReadContract. + * - Decrypts the handle on-demand via useUserDecrypt (query-based — handles + * keypair generation, EIP-712 signing, and caching internally). + * - Encrypts inputs via useEncrypt and writes increment/decrement via + * useWriteContract. */ export const useFHECounterWagmi = () => { const { address, isConnected } = useAccount(); const chainId = useChainId(); + const fheCounter = useMemo(() => deploymentFor(FHECounter, chainId), [chainId]); - // Resolve deployed contract info once we know the chain - const allowedChainId = typeof chainId === "number" ? (chainId as AllowedChainIds) : undefined; - const { data: fheCounter } = useDeployedContractInfo({ contractName: "FHECounter", chainId: allowedChainId }); - - type FHECounterInfo = Contract<"FHECounter"> & { chainId?: number }; - - // Simple status string for UX messages const [message, setMessage] = useState(""); const [isProcessing, setIsProcessing] = useState(false); - // Helpers const hasContract = Boolean(fheCounter?.address && fheCounter?.abi); - // Read count handle via wagmi const readResult = useReadContract({ - address: hasContract ? (fheCounter!.address as `0x${string}`) : undefined, - abi: hasContract ? ((fheCounter as FHECounterInfo).abi as any) : undefined, + address: hasContract ? fheCounter!.address : undefined, + abi: hasContract ? fheCounter!.abi : undefined, functionName: "getCount" as const, query: { enabled: Boolean(hasContract && isConnected), @@ -52,10 +44,7 @@ export const useFHECounterWagmi = () => { if (res.error) setMessage("FHECounter.getCount() failed: " + (res.error as Error).message); }, [readResult]); - // Encryption hook const encrypt = useEncrypt(); - - // Contract write hook const { writeContractAsync } = useWriteContract(); useEffect(() => { @@ -70,30 +59,32 @@ export const useFHECounterWagmi = () => { return () => ctrl.abort(); }, []); - // Build handles array for decryption query (query-based, fires automatically when enabled) + // Handles array for the user-decrypt query (fires automatically once enabled). const decryptHandles = useMemo(() => { if (!countHandle || countHandle === ZERO_HANDLE || !fheCounter?.address) return []; - return [{ handle: countHandle as `0x${string}`, contractAddress: fheCounter.address as `0x${string}` }]; + return [ + { + handle: countHandle as `0x${string}`, + contractAddress: fheCounter.address, + }, + ]; }, [countHandle, fheCounter?.address]); - // Authorization: useAllow acquires FHE keypair + EIP-712 signature, useIsAllowed gates decryption + // Authorization: useAllow acquires FHE keypair + EIP-712 signature; useIsAllowed + // gates whether a user-decrypt call would succeed. const { mutate: allow, isPending: isAllowing } = useAllow(); const contractAddr = (fheCounter?.address ?? "0x0") as `0x${string}`; const { data: isAllowed } = useIsAllowed({ contractAddresses: [contractAddr] }); - // Whether the user has requested decryption const [decryptEnabled, setDecryptEnabled] = useState(false); - // Decryption hook - query-based: fires when authorized and handles are provided const decrypt = useUserDecrypt({ handles: decryptHandles }, { enabled: decryptEnabled && !!isAllowed }); - // Extract decrypted value from query result const cachedDecryptedValue = useMemo(() => { if (!countHandle || !decrypt.data) return undefined; return decrypt.data[countHandle as `0x${string}`]; }, [countHandle, decrypt.data]); - // Derived state const isDecrypted = cachedDecryptedValue !== undefined; const isDecrypting = decrypt.isFetching; const clearCount = useMemo(() => { @@ -115,26 +106,23 @@ export const useFHECounterWagmi = () => { const canUpdateCounter = Boolean(hasContract && isConnected && address && !isProcessing); - // Decrypt the current count handle: authorize if needed, then enable the query const decryptCountHandle = useCallback(async () => { if (!canDecrypt || !countHandle || !fheCounter?.address) return; setDecryptEnabled(true); if (!isAllowed) { setMessage("Authorizing decryption..."); - allow([fheCounter.address as `0x${string}`]); + allow([fheCounter.address]); return; } setMessage("Starting decryption..."); }, [canDecrypt, countHandle, fheCounter?.address, isAllowed, allow]); - // Report decryption errors useEffect(() => { if (decrypt.error) { setMessage(`Decryption failed: ${decrypt.error.message}`); } }, [decrypt.error]); - // Mutations (increment/decrement) const updateCounter = useCallback( async (value: number) => { if (isProcessing || !canUpdateCounter || value === 0 || !fheCounter?.address || !address) return; @@ -143,7 +131,6 @@ export const useFHECounterWagmi = () => { setIsProcessing(true); setMessage(`Starting ${op}(${valueAbs})...`); try { - // Encrypt the value with FHE type annotation setMessage("Encrypting value..."); const enc = await encrypt.mutateAsync({ values: [{ value: BigInt(valueAbs), type: "euint32" }], @@ -151,12 +138,11 @@ export const useFHECounterWagmi = () => { userAddress: address, }); - // Write to contract using wagmi - // FHE operations are gas-intensive; cap below Sepolia's block gas limit (16,777,216) + // FHE operations are gas-intensive; cap below Sepolia's block gas limit (16,777,216). setMessage("Sending transaction..."); await writeContractAsync({ - address: fheCounter.address as `0x${string}`, - abi: (fheCounter as FHECounterInfo).abi as any, + address: fheCounter.address, + abi: fheCounter.abi, functionName: op, args: [bytesToHex(enc.handles[0]!), bytesToHex(enc.inputProof)], gas: 15_000_000n, diff --git a/packages/nextjs/hooks/helper/index.ts b/packages/nextjs/hooks/helper/index.ts index 3e77825f7..97c19b26b 100644 --- a/packages/nextjs/hooks/helper/index.ts +++ b/packages/nextjs/hooks/helper/index.ts @@ -1,4 +1,3 @@ -export * from "./useDeployedContractInfo"; export * from "./useOutsideClick"; export * from "./useTargetNetwork"; export * from "./useSelectedNetwork"; diff --git a/packages/nextjs/hooks/helper/useDeployedContractInfo.ts b/packages/nextjs/hooks/helper/useDeployedContractInfo.ts deleted file mode 100644 index 9562b7e9c..000000000 --- a/packages/nextjs/hooks/helper/useDeployedContractInfo.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { useEffect, useState } from "react"; -import { useIsMounted } from "usehooks-ts"; -import { usePublicClient } from "wagmi"; -import { useSelectedNetwork } from "~~/hooks/helper"; -import { - Contract, - ContractCodeStatus, - ContractName, - UseDeployedContractConfig, - contracts, -} from "~~/utils/helper/contract"; - -type DeployedContractData = { - data: Contract | undefined; - isLoading: boolean; -}; - -/** - * Gets the matching contract info for the provided contract name from the contracts present in deployedContracts.ts - * and externalContracts.ts corresponding to targetNetworks configured in scaffold.config.ts - */ -export function useDeployedContractInfo( - config: UseDeployedContractConfig, -): DeployedContractData; -/** - * @deprecated Use object parameter version instead: useDeployedContractInfo({ contractName: "YourContract" }) - */ -export function useDeployedContractInfo( - contractName: TContractName, -): DeployedContractData; - -export function useDeployedContractInfo( - configOrName: UseDeployedContractConfig | TContractName, -): DeployedContractData { - const isMounted = useIsMounted(); - - const finalConfig: UseDeployedContractConfig = - typeof configOrName === "string" ? { contractName: configOrName } : (configOrName as any); - - useEffect(() => { - if (typeof configOrName === "string") { - console.warn( - "Using `useDeployedContractInfo` with a string parameter is deprecated. Please use the object parameter version instead.", - ); - } - }, [configOrName]); - const { contractName, chainId } = finalConfig; - const selectedNetwork = useSelectedNetwork(chainId); - const deployedContract = contracts?.[selectedNetwork.id]?.[contractName as ContractName] as Contract; - const [status, setStatus] = useState(ContractCodeStatus.LOADING); - const publicClient = usePublicClient({ chainId: selectedNetwork.id }); - - useEffect(() => { - const checkContractDeployment = async () => { - try { - if (!isMounted() || !publicClient) return; - - if (!deployedContract) { - setStatus(ContractCodeStatus.NOT_FOUND); - return; - } - - const code = await publicClient.getBytecode({ - address: deployedContract.address, - }); - - // If contract code is `0x` => no contract deployed on that address - if (code === "0x") { - setStatus(ContractCodeStatus.NOT_FOUND); - return; - } - setStatus(ContractCodeStatus.DEPLOYED); - } catch (e) { - console.error(e); - setStatus(ContractCodeStatus.NOT_FOUND); - } - }; - - checkContractDeployment(); - }, [isMounted, contractName, deployedContract, publicClient]); - - return { - data: status === ContractCodeStatus.DEPLOYED ? deployedContract : undefined, - isLoading: status === ContractCodeStatus.LOADING, - }; -} diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 9493b78c6..2b75df9c8 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -18,8 +18,8 @@ "@heroicons/react": "~2.1.5", "@rainbow-me/rainbowkit": "^2.2.10", "@tanstack/react-query": "^5.96.2", - "@zama-fhe/react-sdk": "^2.5.0", - "@zama-fhe/sdk": "^2.5.0", + "@zama-fhe/react-sdk": "^3.0.0", + "@zama-fhe/sdk": "^3.0.0", "blo": "~1.2.0", "burner-connector": "0.0.18", "daisyui": "5.0.9", @@ -30,7 +30,7 @@ "react-dom": "~19.0.0", "react-hot-toast": "~2.4.0", "usehooks-ts": "~3.1.0", - "viem": "^2.47.0", + "viem": "^2.47.12", "wagmi": "^2.19.5", "zustand": "~5.0.0" }, diff --git a/packages/nextjs/services/web3/wagmiSigner.ts b/packages/nextjs/services/web3/wagmiSigner.ts index 2fd3a144c..b8ac237a7 100644 --- a/packages/nextjs/services/web3/wagmiSigner.ts +++ b/packages/nextjs/services/web3/wagmiSigner.ts @@ -12,10 +12,17 @@ import { } from "wagmi/actions"; /** - * Custom WagmiSigner that implements GenericSigner using wagmi/actions. - * Includes subscribe() for session lifecycle (disconnect/account/chain change). - * This replaces `@zama-fhe/react-sdk/wagmi`'s WagmiSigner which uses - * `watchConnection` (not available in all wagmi 2.x versions). + * Wagmi-backed GenericSigner. + * + * Reimplements `@zama-fhe/react-sdk/wagmi`'s WagmiSigner locally because the + * bundled version imports `watchConnection` from `wagmi/actions`, which wagmi + * (through ≥2.22.x) does not export. wagmi exposes `watchAccount` instead, + * which delivers the same disconnect / account-change / chain-change events + * we need for the SDK's session lifecycle. + * + * Remove this file and switch to `import { WagmiSigner } from + * "@zama-fhe/react-sdk/wagmi"` once the upstream fix reaches a stable + * @zama-fhe/react-sdk release. */ export class WagmiSigner implements GenericSigner { private config: Config; @@ -37,7 +44,10 @@ export class WagmiSigner implements GenericSigner { } async signTypedData(typedData: EIP712TypedData): Promise { - const { EIP712Domain: _, ...sigTypes } = typedData.types; + // wagmi's signTypedData derives EIP712Domain from `domain`; passing it via + // `types` triggers "Ambiguous primary type" — strip it here. + const sigTypes = { ...typedData.types }; + delete (sigTypes as Record).EIP712Domain; return signTypedData(this.config, { primaryType: Object.keys(sigTypes)[0]!, types: sigTypes, diff --git a/packages/nextjs/styles/globals.css b/packages/nextjs/styles/globals.css index 89518049a..f6d4958e9 100644 --- a/packages/nextjs/styles/globals.css +++ b/packages/nextjs/styles/globals.css @@ -90,9 +90,21 @@ body { min-height: 100vh; - font-family: "Telegraf", ui-sans-serif, system-ui, -apple-system, Segoe UI, - Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol"; + font-family: + "Telegraf", + ui-sans-serif, + system-ui, + -apple-system, + Segoe UI, + Roboto, + Ubuntu, + Cantarell, + Noto Sans, + Helvetica Neue, + Arial, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol"; } h1, diff --git a/packages/nextjs/utils/contract.ts b/packages/nextjs/utils/contract.ts new file mode 100644 index 000000000..20d3c28ea --- /dev/null +++ b/packages/nextjs/utils/contract.ts @@ -0,0 +1,32 @@ +import type { Abi, Address } from "viem"; + +export type ContractDeployment = { + address: Address; + abi: Abi; + inheritedFunctions?: Record; + deployedOnBlock: number; +}; + +export type GenericContractsDeclaration = { + [chainId: number]: { + [contractName: string]: ContractDeployment; + }; +}; + +/** Pick a deployment for a chain, preserving the narrow (as-const) shape + * so viem/wagmi can still infer function names and return types from the ABI. + * + * contracts/.ts exports `{...REMOTE, ...LOCAL} as const satisfies + * Partial>`; when both sides are empty on + * a fresh clone, `keyof typeof X` collapses to `never`. The bracketed + * conditional avoids distribution and falls back to the wide ContractDeployment + * type so the helper stays usable (returning `ContractDeployment | undefined`) + * until the sidecar or remote entries are populated. */ +type DeploymentOf = [keyof T] extends [never] ? ContractDeployment : NonNullable; + +export function deploymentFor>>( + byChain: T, + chainId: number, +): DeploymentOf | undefined { + return (byChain as Partial>>)[chainId]; +} diff --git a/packages/nextjs/utils/helper/contract.ts b/packages/nextjs/utils/helper/contract.ts deleted file mode 100644 index 87420cacc..000000000 --- a/packages/nextjs/utils/helper/contract.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AllowedChainIds } from "./networks"; -import { Abi, Address } from "viem"; -import deployedContractsData from "~~/contracts/deployedContracts"; -import scaffoldConfig from "~~/scaffold.config"; - -export type InheritedFunctions = { readonly [key: string]: string }; - -export type GenericContract = { - address: Address; - abi: Abi; - inheritedFunctions?: InheritedFunctions; - external?: true; - deployedOnBlock?: number; -}; - -export type GenericContractsDeclaration = { - [chainId: number]: { - [contractName: string]: GenericContract; - }; -}; - -export const contracts = deployedContractsData as GenericContractsDeclaration | null; - -type ConfiguredChainId = (typeof scaffoldConfig)["targetNetworks"][0]["id"]; - -type IsContractDeclarationMissing = typeof deployedContractsData extends { [key in ConfiguredChainId]: any } - ? TNo - : TYes; - -type ContractsDeclaration = IsContractDeclarationMissing; - -type Contracts = ContractsDeclaration[ConfiguredChainId]; - -export type ContractName = keyof Contracts; - -export type Contract = Contracts[TContractName]; - -export enum ContractCodeStatus { - "LOADING", - "DEPLOYED", - "NOT_FOUND", -} - -export type UseDeployedContractConfig = { - contractName: TContractName; - chainId?: AllowedChainIds; -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4afc4f390..8cb0829d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@types/node': specifier: ^22.7.5 version: 22.7.5 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.4.0 + version: 16.4.0 prettier: specifier: ^3.6.2 version: 3.6.2 @@ -21,8 +27,8 @@ importers: specifier: ^5.9.2 version: 5.9.2 viem: - specifier: ^2.47.10 - version: 2.47.10(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.3.6) + specifier: ^2.47.12 + version: 2.48.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.3.6) packages/nextjs: dependencies: @@ -31,16 +37,16 @@ importers: version: 2.1.5(react@19.0.0) '@rainbow-me/rainbowkit': specifier: ^2.2.10 - version: 2.2.10(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + version: 2.2.10(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) '@tanstack/react-query': specifier: ^5.96.2 version: 5.96.2(react@19.0.0) '@zama-fhe/react-sdk': - specifier: ^2.5.0 - version: 2.5.0(@tanstack/react-query@5.96.2(react@19.0.0))(@zama-fhe/sdk@2.5.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + specifier: ^3.0.0 + version: 3.0.0(@tanstack/react-query@5.96.2(react@19.0.0))(@zama-fhe/sdk@3.0.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) '@zama-fhe/sdk': - specifier: ^2.5.0 - version: 2.5.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + specifier: ^3.0.0 + version: 3.0.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) blo: specifier: ~1.2.0 version: 1.2.0 @@ -72,11 +78,11 @@ importers: specifier: ~3.1.0 version: 3.1.1(react@19.0.0) viem: - specifier: ^2.47.0 - version: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + specifier: ^2.47.12 + version: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) wagmi: specifier: ^2.19.5 - version: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + version: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) zustand: specifier: ~5.0.0 version: 5.0.8(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) @@ -383,79 +389,67 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -625,28 +619,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-musl@15.2.5': resolution: {integrity: sha512-4ZNKmuEiW5hRKkGp2HWwZ+JrvK4DQLgf8YDaqtZyn7NYdl0cHfatvlnLFSWUayx9yFAUagIgRGRk8pFxS8Qniw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-x64-gnu@15.2.5': resolution: {integrity: sha512-bE6lHQ9GXIf3gCDE53u2pTl99RPZW5V1GLHSRMJ5l/oB/MT+cohu9uwnCK7QUph2xIOu2a6+27kL0REa/kqwZw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-musl@15.2.5': resolution: {integrity: sha512-y7EeQuSkQbTAkCEQnJXm1asRUuGSWAchGJ3c+Qtxh8LVjXleZast8Mn/rL7tZOm7o35QeIpIcid6ufG7EVTTcA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-win32-arm64-msvc@15.2.5': resolution: {integrity: sha512-gQMz0yA8/dskZM2Xyiq2FRShxSrsJNha40Ob/M2n2+JGRrZ0JwTVjLdvtN6vCxuq4ByhOd4a9qEf60hApNR2gQ==} @@ -1246,28 +1236,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.0.15': resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.0.15': resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.0.15': resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@tailwindcss/oxide-win32-arm64-msvc@4.0.15': resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==} @@ -1468,49 +1454,41 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] - libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -1732,12 +1710,12 @@ packages: '@walletconnect/window-metadata@1.0.1': resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} - '@zama-fhe/react-sdk@2.5.0': - resolution: {integrity: sha512-AGEbpzvUWBL9D2IPXnOefrK73EGbOZHBXLge3UYvpxTD6IpxsveIA+jRbnqih5NOH+BMj3lbiY4IoRqcH+efHA==} + '@zama-fhe/react-sdk@3.0.0': + resolution: {integrity: sha512-pObZM9v5pDELZWUv9Oyt9iHzYLzRfmK5F/embwMnnp1+F9tnUlocBs0DOZmQcxalNBrxUwCqmuCiwIUiQlQpXw==} engines: {node: '>=22'} peerDependencies: '@tanstack/react-query': '>=5' - '@zama-fhe/sdk': ^2.5.0 + '@zama-fhe/sdk': ^3.0.0 react: '>=18' viem: ^2.47.0 wagmi: '>=2' @@ -1750,8 +1728,8 @@ packages: engines: {node: '>=22'} hasBin: true - '@zama-fhe/sdk@2.5.0': - resolution: {integrity: sha512-wHqrw/KX3H0h/JWXkcwZyI2h+iTWwyv5oiKbhDMCTJkXnP/+KjF/HwwPDLXXQ6TyWQOqc4K1/p8t8g3MBD9SOg==} + '@zama-fhe/sdk@3.0.0': + resolution: {integrity: sha512-43G5css2FWe6+Q4hRlU8DFQvIeC7HybjGY6dYVF3CEb7iv0KAruYGaf0Pf9rMTvY5i7IMGMVhgm1vAl9q1BsZQ==} engines: {node: '>=22'} peerDependencies: '@tanstack/query-core': '>=5' @@ -1833,14 +1811,26 @@ packages: ajv@8.6.3: resolution: {integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2079,6 +2069,14 @@ packages: cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -2114,6 +2112,9 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2122,6 +2123,10 @@ packages: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2363,6 +2368,9 @@ packages: electron-to-chromium@1.5.222: resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2392,6 +2400,10 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -2884,6 +2896,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2999,6 +3015,11 @@ packages: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3106,6 +3127,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.0: resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} @@ -3313,28 +3338,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.29.2: resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.29.2: resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.29.2: resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.29.2: resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} @@ -3352,6 +3373,15 @@ packages: resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} engines: {node: '>= 12.0.0'} + lint-staged@16.4.0: + resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + lit-element@4.2.1: resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} @@ -3378,6 +3408,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -3437,6 +3471,10 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3669,6 +3707,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + openapi-fetch@0.13.8: resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} @@ -3687,8 +3729,8 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - ox@0.14.7: - resolution: {integrity: sha512-zSQ/cfBdolj7U4++NAvH7sI+VG0T3pEohITCgcQj8KlawvTDY4vGVhDT64Atsm0d6adWfIYHDpu88iUBMMp+AQ==} + ox@0.14.20: + resolution: {integrity: sha512-rby38C3nDn8eQkf29Zgw4hkCZJ64Qqi0zRPWL8ENUQ7JVuoITqrVtwWQgM/He19SCMUEc7hS/Sjw0jIOSLJhOw==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -4062,10 +4104,17 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -4179,9 +4228,21 @@ packages: resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==} engines: {node: '>=14'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-swizzle@0.2.4: resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + socket.io-client@4.8.1: resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} engines: {node: '>=10.0.0'} @@ -4240,10 +4301,22 @@ packages: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -4277,6 +4350,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -4346,6 +4423,10 @@ packages: resolution: {integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==} engines: {node: '>=10'} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -4677,8 +4758,8 @@ packages: typescript: optional: true - viem@2.47.10: - resolution: {integrity: sha512-D+l6SDDZWB5bh8u9hgICzMX2/egMrgEQ+Pef/QkZgmOl6bOTyCQMSgWAH8jZTWJ/218J9QNv7s/9BH6Wu5oPDg==} + viem@2.48.4: + resolution: {integrity: sha512-mReP/rgY2P+WeeRSG4sUvccCLKfyAW1C73Y3KkobAqgzYmVna9qyUMNE44xIUkDtfvRuC33r24UhF4baBYovsg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -4757,6 +4838,10 @@ packages: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -4845,6 +4930,11 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + 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'} @@ -5029,7 +5119,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.8.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) transitivePeerDependencies: - '@types/react' @@ -5050,7 +5140,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.8.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) transitivePeerDependencies: - '@types/react' @@ -5075,7 +5165,7 @@ snapshots: jose: 6.2.2 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -5106,7 +5196,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.8.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) transitivePeerDependencies: - '@types/react' @@ -5248,11 +5338,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@gemini-wallet/core@0.3.2(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': + '@gemini-wallet/core@0.3.2(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - supports-color @@ -5714,7 +5804,7 @@ snapshots: '@pkgr/core@0.2.9': {} - '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': + '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': dependencies: '@tanstack/react-query': 5.96.2(react@19.0.0) '@vanilla-extract/css': 1.17.3 @@ -5726,8 +5816,8 @@ snapshots: react-dom: 19.0.0(react@19.0.0) react-remove-scroll: 2.6.2(@types/react@19.0.14)(react@19.0.0) ua-parser-js: 1.0.41 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) - wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - babel-plugin-macros @@ -5756,7 +5846,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -5767,7 +5857,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript @@ -5780,7 +5870,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.0.14)(react@19.0.0) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5930,7 +6020,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.0.14)(react@19.0.0) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5984,7 +6074,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.0.14)(react@19.0.0) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -6035,7 +6125,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript @@ -7032,19 +7122,19 @@ snapshots: - utf-8-validate - zod - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6)': dependencies: '@base-org/account': 2.4.0(@types/react@19.0.14)(bufferutil@4.0.9)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(zod@4.3.6) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.0.14)(bufferutil@4.0.9)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(zod@4.3.6) - '@gemini-wallet/core': 0.3.2(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@gemini-wallet/core': 0.3.2(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + porto: 0.2.35(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -7100,11 +7190,11 @@ snapshots: - react - use-sync-external-store - '@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.8.3) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.0(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) optionalDependencies: '@tanstack/query-core': 5.96.2 @@ -7652,14 +7742,14 @@ snapshots: '@walletconnect/window-getters': 1.0.1 tslib: 1.14.1 - '@zama-fhe/react-sdk@2.5.0(@tanstack/react-query@5.96.2(react@19.0.0))(@zama-fhe/sdk@2.5.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': + '@zama-fhe/react-sdk@3.0.0(@tanstack/react-query@5.96.2(react@19.0.0))(@zama-fhe/sdk@3.0.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': dependencies: '@tanstack/react-query': 5.96.2(react@19.0.0) - '@zama-fhe/sdk': 2.5.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@zama-fhe/sdk': 3.0.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) react: 19.0.0 - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: - wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) '@zama-fhe/relayer-sdk@0.4.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: @@ -7676,13 +7766,13 @@ snapshots: - bufferutil - utf-8-validate - '@zama-fhe/sdk@2.5.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': + '@zama-fhe/sdk@3.0.0(@tanstack/query-core@5.96.2)(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: '@zama-fhe/relayer-sdk': 0.4.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: '@tanstack/query-core': 5.96.2 ethers: 6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -7760,12 +7850,20 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + any-promise@1.3.0: {} anymatch@3.1.3: @@ -8053,6 +8151,15 @@ snapshots: cjs-module-lexer@1.2.3: {} + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.0 + client-only@0.0.1: {} cliui@6.0.0: @@ -8087,12 +8194,16 @@ snapshots: color-string: 1.9.1 optional: true + colorette@2.0.20: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 commander@14.0.2: {} + commander@14.0.3: {} + concat-map@0.0.1: {} console-control-strings@1.1.0: {} @@ -8289,6 +8400,8 @@ snapshots: electron-to-chromium@1.5.222: {} + emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -8327,6 +8440,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.3 + environment@1.1.0: {} + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -8944,6 +9059,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.5.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9074,6 +9191,8 @@ snapshots: human-signals@1.1.1: {} + husky@9.1.7: {} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -9179,6 +9298,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + is-generator-function@1.1.0: dependencies: call-bound: 1.0.4 @@ -9394,6 +9517,24 @@ snapshots: lightningcss-win32-arm64-msvc: 1.29.2 lightningcss-win32-x64-msvc: 1.29.2 + lint-staged@16.4.0: + dependencies: + commander: 14.0.3 + listr2: 9.0.5 + picomatch: 4.0.3 + string-argv: 0.3.2 + tinyexec: 1.1.1 + yaml: 2.8.3 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + lit-element@4.2.1: dependencies: '@lit-labs/ssr-dom-shim': 1.4.0 @@ -9424,6 +9565,14 @@ snapshots: lodash@4.17.21: {} + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -9477,6 +9626,8 @@ snapshots: mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -9690,6 +9841,10 @@ snapshots: dependencies: mimic-fn: 2.1.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + openapi-fetch@0.13.8: dependencies: openapi-typescript-helpers: 0.0.15 @@ -9713,7 +9868,7 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.14.7(typescript@5.8.3)(zod@3.22.4): + ox@0.14.20(typescript@5.8.3)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -9728,7 +9883,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.14.7(typescript@5.8.3)(zod@3.25.76): + ox@0.14.20(typescript@5.8.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -9743,7 +9898,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.14.7(typescript@5.8.3)(zod@4.3.6): + ox@0.14.20(typescript@5.8.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -9758,7 +9913,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.14.7(typescript@5.9.2)(zod@4.3.6): + ox@0.14.20(typescript@5.9.2)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -9919,21 +10074,21 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)): + porto@0.2.35(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) hono: 4.12.12 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.8.3) ox: 0.9.17(typescript@5.8.3)(zod@4.3.6) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) zod: 4.3.6 zustand: 5.0.8(@types/react@19.0.14)(react@19.0.0)(use-sync-external-store@1.4.0(react@19.0.0)) optionalDependencies: '@tanstack/react-query': 5.96.2(react@19.0.0) react: 19.0.0 typescript: 5.8.3 - wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - immer @@ -10133,8 +10288,15 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} + rfdc@1.4.1: {} + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -10279,11 +10441,23 @@ snapshots: signal-exit@4.0.2: {} + signal-exit@4.1.0: {} + simple-swizzle@0.2.4: dependencies: is-arrayish: 0.3.4 optional: true + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 @@ -10341,12 +10515,25 @@ snapshots: strict-uri-encode@2.0.0: {} + string-argv@0.3.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -10409,6 +10596,10 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-bom@3.0.0: {} strip-final-newline@2.0.0: {} @@ -10467,6 +10658,8 @@ snapshots: dependencies: convert-hrtime: 3.0.0 + tinyexec@1.1.1: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -10790,7 +10983,7 @@ snapshots: - utf-8-validate - zod - viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -10798,7 +10991,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.8.3)(zod@3.22.4) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.8.3)(zod@3.22.4) + ox: 0.14.20(typescript@5.8.3)(zod@3.22.4) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.8.3 @@ -10807,7 +11000,7 @@ snapshots: - utf-8-validate - zod - viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -10815,7 +11008,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.8.3)(zod@3.25.76) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.8.3)(zod@3.25.76) + ox: 0.14.20(typescript@5.8.3)(zod@3.25.76) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.8.3 @@ -10824,7 +11017,7 @@ snapshots: - utf-8-validate - zod - viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6): + viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -10832,7 +11025,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.8.3)(zod@4.3.6) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.8.3)(zod@4.3.6) + ox: 0.14.20(typescript@5.8.3)(zod@4.3.6) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.8.3 @@ -10841,7 +11034,7 @@ snapshots: - utf-8-validate - zod - viem@2.47.10(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.3.6): + viem@2.48.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.3.6): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -10849,7 +11042,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.9.2)(zod@4.3.6) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.14.7(typescript@5.9.2)(zod@4.3.6) + ox: 0.14.20(typescript@5.9.2)(zod@4.3.6) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.2 @@ -10897,14 +11090,14 @@ snapshots: - utf-8-validate - zod - wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6): + wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6): dependencies: '@tanstack/react-query': 5.96.2(react@19.0.0) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.96.2)(@tanstack/react-query@5.96.2(react@19.0.0))(@types/react@19.0.14)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.0.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.96.2)(@types/react@19.0.14)(react@19.0.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.0.0))(viem@2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6)) react: 19.0.0 use-sync-external-store: 1.4.0(react@19.0.0) - viem: 2.47.10(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.48.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -11014,6 +11207,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrappy@1.0.2: {} ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): @@ -11059,6 +11258,8 @@ snapshots: yallist@4.0.0: {} + yaml@2.8.3: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 diff --git a/scripts/chain.sh b/scripts/chain.sh index 30b4851f6..688a708da 100755 --- a/scripts/chain.sh +++ b/scripts/chain.sh @@ -1,13 +1,18 @@ #!/usr/bin/env bash -# Start a local anvil node with the FHEVM cleartext host stack deployed. +# Start anvil + FHEVM cleartext host stack + FHECounter in one command. # -# This deploys CleartextFHEVMExecutor (and the full host stack) at the same -# fixed addresses that @zama-fhe/sdk/cleartext's hardhatCleartextConfig -# expects, so RelayerCleartext in the frontend works out of the box. +# Flow (2 terminals): +# pnpm chain # this script — anvil + FHEVM host + FHECounter +# pnpm start # frontend +# +# To redeploy FHECounter without restarting anvil, run +# `pnpm deploy:localhost` in another terminal. set -euo pipefail PORT="${ANVIL_PORT:-8545}" -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +RPC_URL="http://127.0.0.1:$PORT" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # forge-fhevm is installed as a soldeer dependency of packages/foundry. The # installed source tree includes deploy-local.sh (the canonical FHEVM host @@ -20,7 +25,7 @@ if [[ -z "$FORGE_FHEVM_DIR" || ! -d "$FORGE_FHEVM_DIR" ]]; then exit 1 fi -for bin in anvil forge cast jq; do +for bin in anvil forge cast jq pnpm; do command -v "$bin" >/dev/null || { echo "error: missing '$bin' on PATH" >&2; exit 1; } done @@ -41,23 +46,36 @@ cleanup() { [[ -n "$ANVIL_PID" ]] && kill "$ANVIL_PID" 2>/dev/null || true; } trap cleanup EXIT INT TERM echo "starting anvil on port $PORT..." -anvil --port "$PORT" --chain-id 31337 --silent & +ANVIL_STATE="${ANVIL_STATE:-$REPO_ROOT/.anvil-state.json}" +ANVIL_ARGS="--host 127.0.0.1 --port $PORT --chain-id 31337 --auto-impersonate --silent" +if [[ -f "$ANVIL_STATE" ]]; then + echo " restoring anvil state from $ANVIL_STATE" + anvil $ANVIL_ARGS --load-state "$ANVIL_STATE" --dump-state "$ANVIL_STATE" & +else + anvil $ANVIL_ARGS --dump-state "$ANVIL_STATE" & +fi ANVIL_PID=$! # Wait for RPC for _ in $(seq 1 150); do - nc -z 127.0.0.1 "$PORT" 2>/dev/null && break + cast chain-id --rpc-url "$RPC_URL" >/dev/null 2>&1 && break sleep 0.2 done -nc -z 127.0.0.1 "$PORT" 2>/dev/null || { echo "anvil failed to start on port $PORT" >&2; exit 1; } +kill -0 "$ANVIL_PID" 2>/dev/null \ + || { echo "anvil failed to start on port $PORT (already in use?)" >&2; exit 1; } echo "deploying FHEVM cleartext host stack..." -(cd "$FORGE_FHEVM_DIR" && ./deploy-local.sh --anvil-port "$PORT") +# Unset any chain override inherited from the calling shell — cast reads +# CHAIN (and legacy FOUNDRY_CHAIN / DAPP_CHAIN) and would fail if set to an +# invalid value such as "testnet". +(unset CHAIN FOUNDRY_CHAIN DAPP_CHAIN; cd "$FORGE_FHEVM_DIR" && ./deploy-local.sh --rpc-url "$RPC_URL") + +echo "deploying FHECounter..." +RPC_URL="$RPC_URL" "$SCRIPT_DIR/deploy-localhost.sh" -echo "" -echo "✓ anvil + FHEVM cleartext host ready on http://127.0.0.1:$PORT" -echo " chain id: 31337" -echo " press Ctrl+C to stop" -echo "" +echo +echo "✓ anvil + FHEVM host + FHECounter ready on $RPC_URL (chain id 31337)" +echo " next: pnpm start (in another terminal)" +echo wait "$ANVIL_PID" diff --git a/scripts/deploy-local.sh b/scripts/deploy-local.sh deleted file mode 100755 index 936e0e954..000000000 --- a/scripts/deploy-local.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# Deploy FHECounter to local anvil and regenerate the frontend's TS ABIs. -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -FOUNDRY_DIR="$REPO_ROOT/packages/foundry" -RPC_URL="${RPC_URL:-http://127.0.0.1:8545}" -# Anvil default account #0 -PRIVATE_KEY="${DEPLOYER_PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}" - -if ! nc -z 127.0.0.1 "${RPC_URL##*:}" 2>/dev/null; then - echo "error: no RPC at $RPC_URL — run 'pnpm chain' first" >&2 - exit 1 -fi - -cd "$FOUNDRY_DIR" -forge script script/DeployFHECounter.s.sol:DeployFHECounter \ - --rpc-url "$RPC_URL" \ - --private-key "$PRIVATE_KEY" \ - --broadcast \ - --silent - -cd "$REPO_ROOT" -pnpm generate diff --git a/scripts/deploy-localhost.sh b/scripts/deploy-localhost.sh new file mode 100755 index 000000000..824d07630 --- /dev/null +++ b/scripts/deploy-localhost.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Deploy FHECounter to a running anvil node at 127.0.0.1:8545 and regenerate +# the frontend's per-contract ABI/address files. +# +# Prereq: `pnpm chain` is running in another terminal. That script starts +# anvil AND materializes the FHEVM cleartext host stack at the canonical +# addresses RelayerCleartext expects. +set -euo pipefail + +RPC_URL="${RPC_URL:-http://127.0.0.1:8545}" +# Anvil's first default account — deterministic, same on every run. +ANVIL_PK="${PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +FOUNDRY_DIR="$REPO_ROOT/packages/foundry" + +if ! cast chain-id --rpc-url "$RPC_URL" >/dev/null 2>&1; then + echo "❌ No RPC at $RPC_URL. Run 'pnpm chain' in another terminal first." >&2 + exit 1 +fi + +echo "▸ Deploying FHECounter" +cd "$FOUNDRY_DIR" +# foundry.toml references SEPOLIA_RPC_URL / ETHERSCAN_API_KEY under +# [rpc_endpoints] / [etherscan]. forge 1.x refuses to load the config if +# those vars are unset, even on a localhost deploy that never touches them — +# so stub them here so a fresh checkout doesn't silently fail. +: "${SEPOLIA_RPC_URL:=unset}" +: "${ETHERSCAN_API_KEY:=unset}" +export SEPOLIA_RPC_URL ETHERSCAN_API_KEY + +deploy_log="$(mktemp)" +trap 'rm -f "$deploy_log"' EXIT +if ! PRIVATE_KEY="$ANVIL_PK" forge script script/DeployFHECounter.s.sol:DeployFHECounter \ + --rpc-url "$RPC_URL" \ + --private-key "$ANVIL_PK" \ + --broadcast \ + >"$deploy_log" 2>&1; then + echo "❌ forge script failed:" >&2 + cat "$deploy_log" >&2 + exit 1 +fi +grep -E "FHECounter|Owner|===" "$deploy_log" || true + +echo +echo "▸ Regenerating frontend ABIs + addresses" +cd "$REPO_ROOT" +pnpm generate + +echo +echo "✅ Local dev stack ready. Frontend reads addresses from" +echo " packages/nextjs/contracts/FHECounter.ts (+ FHECounter.local.ts)." diff --git a/scripts/deploy-sepolia.sh b/scripts/deploy-sepolia.sh index 1d9b1ee83..e5ba7457f 100755 --- a/scripts/deploy-sepolia.sh +++ b/scripts/deploy-sepolia.sh @@ -39,5 +39,11 @@ fi cd "$FOUNDRY_DIR" forge script "${FORGE_ARGS[@]}" +echo +echo "▸ Regenerating frontend ABIs + addresses" cd "$REPO_ROOT" pnpm generate + +echo +echo "✅ Sepolia deploy complete. Frontend reads addresses from" +echo " packages/nextjs/contracts/FHECounter.ts." diff --git a/scripts/generateTsAbis.ts b/scripts/generateTsAbis.ts index b1bad53de..32cd9bf82 100644 --- a/scripts/generateTsAbis.ts +++ b/scripts/generateTsAbis.ts @@ -1,13 +1,21 @@ /** - * Generates packages/nextjs/contracts/deployedContracts.ts from foundry + * Generates per-contract files in packages/nextjs/contracts/ from foundry * build output + broadcast receipts. * * Inputs: * packages/foundry/out/.sol/.json — compiled ABI * packages/foundry/broadcast/...//run-latest.json — deployed addr+block * - * Output: - * packages/nextjs/contracts/deployedContracts.ts + * Outputs (one pair per contract; bundlers tree-shake unused ones): + * packages/nextjs/contracts/.ts — non-local chains (tracked) + * packages/nextjs/contracts/.local.ts — local 31337 overlay (gitignored) + * + * The main file imports the sidecar and merges at module load, so consumers + * stay agnostic to which chain a deployment lives on. + * + * Invoked by: + * pnpm generate + * (also appended to deploy:localhost and deploy:sepolia) */ import * as fs from "fs"; import * as path from "path"; @@ -18,16 +26,27 @@ const REPO_ROOT = path.resolve(__dirname, ".."); const FOUNDRY_DIR = path.join(REPO_ROOT, "packages/foundry"); const OUT_DIR = path.join(FOUNDRY_DIR, "out"); const BROADCAST_DIR = path.join(FOUNDRY_DIR, "broadcast"); -const TARGET_FILE = path.join(REPO_ROOT, "packages/nextjs/contracts/deployedContracts.ts"); +const TARGET_DIR = path.join(REPO_ROOT, "packages/nextjs/contracts"); + +const LOCAL_CHAIN_ID = 31337; type Deployment = { address: `0x${string}`; deployedOnBlock: number; }; -/** Walk broadcast/ for `CREATE` txs, grouped by chainId → contract name. */ -function collectDeployments(): Record> { - const out: Record> = {}; +type ContractEntry = Deployment & { abi: unknown[] }; + +/** Walk broadcast/ for `CREATE` txs, grouped by contract name → chainId. + * When multiple scripts deploy the same contract to the same chain, the + * most-recently-modified broadcast wins. + * + * Reads every historical `run-NNN.json` (not just `run-latest.json`) so that + * incremental deploy scripts — where a partial run replaces run-latest with + * only the freshly-deployed CREATEs — don't drop the addresses of reused + * contracts that were CREATE'd in earlier runs of the same script. */ +function collectDeployments(): Record> { + const out: Record> = {}; if (!fs.existsSync(BROADCAST_DIR)) return out; for (const scriptDir of fs.readdirSync(BROADCAST_DIR)) { @@ -36,85 +55,230 @@ function collectDeployments(): Record> { for (const chainIdStr of fs.readdirSync(scriptPath)) { const chainPath = path.join(scriptPath, chainIdStr); - const runLatest = path.join(chainPath, "run-latest.json"); - if (!fs.existsSync(runLatest)) continue; + if (!fs.statSync(chainPath).isDirectory()) continue; const chainId = Number(chainIdStr); - const run = JSON.parse(fs.readFileSync(runLatest, "utf8")); - const receipts: Array<{ blockNumber: string; transactionHash: string }> = run.receipts ?? []; - const receiptByHash = new Map(receipts.map(r => [r.transactionHash, r])); - - for (const tx of run.transactions ?? []) { - if (tx.transactionType !== "CREATE" || !tx.contractName || !tx.contractAddress) continue; - const receipt = receiptByHash.get(tx.hash); - const deployedOnBlock = receipt ? parseInt(receipt.blockNumber, 16) : 0; - out[chainId] ??= {}; - // Foundry writes lowercase addresses; Zama's relayer SDK requires EIP-55 - // checksummed addresses (isChecksummedAddress() check in createRelayerEncryptedInput). - out[chainId][tx.contractName] = { - address: getAddress(tx.contractAddress), - deployedOnBlock, - }; + + // run-latest.json is a copy of the most recent run-.json, + // so iterating just the timestamped files covers it without double work. + const runFiles = fs + .readdirSync(chainPath) + .filter((f) => /^run-\d+\.json$/.test(f)) + .map((f) => path.join(chainPath, f)); + + // Fresh-clone fallback: if the only file present is run-latest.json + // (no timestamped runs yet), still process it so the first deploy works. + if (runFiles.length === 0) { + const runLatest = path.join(chainPath, "run-latest.json"); + if (fs.existsSync(runLatest)) runFiles.push(runLatest); + } + + for (const runPath of runFiles) { + const mtime = fs.statSync(runPath).mtimeMs; + const run = JSON.parse(fs.readFileSync(runPath, "utf8")); + const receipts: Array<{ blockNumber: string; transactionHash: string }> = + run.receipts ?? []; + const receiptByHash = new Map(receipts.map((r) => [r.transactionHash, r])); + + for (const tx of run.transactions ?? []) { + if (tx.transactionType !== "CREATE" || !tx.contractName || !tx.contractAddress) continue; + const receipt = receiptByHash.get(tx.hash); + const deployedOnBlock = receipt ? parseInt(receipt.blockNumber, 16) : 0; + out[tx.contractName] ??= {}; + const existing = out[tx.contractName][chainId]; + // Newest CREATE per (contract, chain) wins — reflects current on-chain state. + if (existing && existing.mtime >= mtime) continue; + // Foundry writes lowercase addresses; Zama's relayer SDK requires EIP-55 + // checksummed addresses (isChecksummedAddress() check in createRelayerEncryptedInput). + out[tx.contractName][chainId] = { + address: getAddress(tx.contractAddress), + deployedOnBlock, + mtime, + }; + } } } } return out; } -/** Read the compiled ABI for a contract from out/. */ -function readAbi(contractName: string): unknown[] { +/** Read the compiled ABI for a contract from out/. Returns null if missing + * (e.g. a contract deployed in an older broadcast but since removed). */ +function readAbi(contractName: string): unknown[] | null { const artifactPath = path.join(OUT_DIR, `${contractName}.sol`, `${contractName}.json`); - if (!fs.existsSync(artifactPath)) { - throw new Error(`Missing foundry artifact: ${artifactPath} — run 'forge build' in packages/foundry`); - } + if (!fs.existsSync(artifactPath)) return null; const { abi } = JSON.parse(fs.readFileSync(artifactPath, "utf8")); return abi; } +// Chain IDs must be numeric keys in the generated TS (consumer types narrow on `number`). +const serializeChains = (obj: Record) => + JSON.stringify(obj, null, 2).replace(/^(\s*)"(\d+)":/gm, "$1$2:"); + +async function renderMainFile(name: string, remote: Record) { + // `as const` preserves the literal ABI tuple type so viem/wagmi can narrow + // function names, args, and return types at consumer call sites. + // `Partial>` lets the merge typecheck even when one side is empty. + const body = `/** + * This file is autogenerated. Do not edit by hand — run \`pnpm generate\`. + * + * Non-local chain deployments live here; local (chainId ${LOCAL_CHAIN_ID}) + * deployments live in \`./${name}.local.ts\` (gitignored) and are merged in + * at module load. Import by name: \`import { ${name} } from "~~/contracts/${name}";\` + */ +import type { ContractDeployment } from "~~/utils/contract"; +import { ${name} as ${name}_LOCAL } from "./${name}.local"; + +const REMOTE = ${serializeChains(remote)} as const; + +export const ${name} = { + ...REMOTE, + ...${name}_LOCAL, +} as const satisfies Partial>; +`; + + const formatted = await prettier.format(body, { parser: "typescript" }); + const target = path.join(TARGET_DIR, `${name}.ts`); + fs.writeFileSync(target, formatted); + return target; +} + +async function renderLocalFile(name: string, local: Record) { + const body = `/** + * Autogenerated local (chainId ${LOCAL_CHAIN_ID}) overlay — do not edit by hand. + * + * This file is gitignored: your local deployment addresses live here and + * should not be committed. Run \`pnpm generate\` after \`pnpm deploy:localhost\` + * to refresh, or after a fresh clone to materialize a stub. + */ +import type { ContractDeployment } from "~~/utils/contract"; + +export const ${name} = ${serializeChains(local)} as const satisfies Partial>; +`; + + const formatted = await prettier.format(body, { parser: "typescript" }); + const target = path.join(TARGET_DIR, `${name}.local.ts`); + fs.writeFileSync(target, formatted); + return target; +} + +async function renderContractFile(name: string, perChain: Record) { + const remote: Record = {}; + const local: Record = {}; + for (const [chainIdStr, entry] of Object.entries(perChain)) { + const chainId = Number(chainIdStr); + if (chainId === LOCAL_CHAIN_ID) local[chainId] = entry; + else remote[chainId] = entry; + } + + await renderLocalFile(name, local); + + // Preserve tracked remote (Sepolia, etc.) entries when this run only has + // local broadcasts. Without this, `pnpm deploy:localhost` (or postinstall + // after a localhost deploy) would clobber the committed Sepolia addresses. + // A fresh Sepolia broadcast always overwrites the corresponding entry. + if (Object.keys(remote).length === 0) { + const mainPath = path.join(TARGET_DIR, `${name}.ts`); + if (fs.existsSync(mainPath)) { + const head = fs.readFileSync(mainPath, "utf8").slice(0, 200); + if (head.includes("This file is autogenerated")) return mainPath; + } + } + return renderMainFile(name, remote); +} + +/** Walk TARGET_DIR and ensure every autogenerated `.ts` has a matching + * `.local.ts` sidecar. On a fresh clone with no broadcasts, the tracked + * main files exist but the gitignored sidecars don't — without these stubs, + * the static `import { X } from "./X.local"` in the main file would fail. */ +async function ensureLocalStubs() { + if (!fs.existsSync(TARGET_DIR)) return; + for (const file of fs.readdirSync(TARGET_DIR)) { + if (!file.endsWith(".ts") || file.endsWith(".local.ts")) continue; + const name = file.slice(0, -3); + const mainPath = path.join(TARGET_DIR, file); + const head = fs.readFileSync(mainPath, "utf8").slice(0, 200); + if (!head.includes("This file is autogenerated")) continue; + const sidecar = path.join(TARGET_DIR, `${name}.local.ts`); + if (fs.existsSync(sidecar)) continue; + await renderLocalFile(name, {}); + console.log(` + wrote stub sidecar ${path.relative(REPO_ROOT, sidecar)}`); + } +} + +/** Wipe any previously-generated per-contract files that no longer have a + * broadcast entry, so renamed/removed contracts don't leave stale bundles. + * + * Skipped when no broadcasts exist at all (fresh clone / CI install) — in that + * case the tracked main files came from git and shouldn't be deleted. */ +function cleanStaleContractFiles(currentNames: Set) { + if (!fs.existsSync(TARGET_DIR)) return; + if (currentNames.size === 0) return; + for (const file of fs.readdirSync(TARGET_DIR)) { + if (!file.endsWith(".ts")) continue; + const baseName = file.endsWith(".local.ts") + ? file.slice(0, -".local.ts".length) + : file.slice(0, -3); + if (currentNames.has(baseName)) continue; + // Generated files start with a JSDoc marker we can detect — skip any + // hand-written TS that happens to share the directory. + const full = path.join(TARGET_DIR, file); + const head = fs.readFileSync(full, "utf8").slice(0, 200); + if (!head.includes("autogenerated")) continue; + fs.unlinkSync(full); + console.log(` ✗ removed stale ${path.relative(REPO_ROOT, full)}`); + } +} + async function main() { const deployments = collectDeployments(); - if (Object.keys(deployments).length === 0) { - console.warn("No deployments found in packages/foundry/broadcast/"); + const contractNames = Object.keys(deployments); + + if (contractNames.length === 0) { + console.warn("⚠ No deployments found in packages/foundry/broadcast/"); } - const contractsByChain: Record> = {}; - for (const [chainIdStr, contracts] of Object.entries(deployments)) { - contractsByChain[chainIdStr] = {}; - for (const [name, dep] of Object.entries(contracts)) { - contractsByChain[chainIdStr][name] = { + fs.mkdirSync(TARGET_DIR, { recursive: true }); + + const written: Array<{ name: string; chains: number[] }> = []; + const skipped: Array<{ name: string; reason: string }> = []; + + for (const [name, perChainRaw] of Object.entries(deployments)) { + const abi = readAbi(name); + if (abi === null) { + // Stale broadcast — contract source has been removed. Skip it so + // the generated files always match live source. + skipped.push({ name, reason: "no out/ artifact" }); + continue; + } + const perChain: Record = {}; + for (const [chainIdStr, dep] of Object.entries(perChainRaw)) { + perChain[Number(chainIdStr)] = { address: dep.address, - abi: readAbi(name), - inheritedFunctions: {}, + abi, deployedOnBlock: dep.deployedOnBlock, }; } + await renderContractFile(name, perChain); + written.push({ name, chains: Object.keys(perChain).map(Number) }); } - // Chain IDs must be numeric keys in the generated TS (the consumer type narrows on `number`). - const serialized = JSON.stringify(contractsByChain, null, 2).replace(/^(\s*)"(\d+)":/gm, "$1$2:"); - - const body = `/** - * This file is autogenerated. - * You should not edit it manually or your changes might be overwritten. - */ -import { GenericContractsDeclaration } from "~~/utils/helper/contract"; + const writtenNames = new Set(written.map((w) => w.name)); + cleanStaleContractFiles(writtenNames); + await ensureLocalStubs(); -const deployedContracts = ${serialized} as const; - -export default deployedContracts satisfies GenericContractsDeclaration; -`; - - const formatted = await prettier.format(body, { parser: "typescript" }); - fs.writeFileSync(TARGET_FILE, formatted); - console.log(`✓ wrote ${path.relative(REPO_ROOT, TARGET_FILE)}`); - for (const [chainId, contracts] of Object.entries(deployments)) { - for (const [name, dep] of Object.entries(contracts)) { - console.log(` ${chainId} ${name} @ ${dep.address} (block ${dep.deployedOnBlock})`); + for (const { name, chains } of written) { + for (const chainId of chains) { + const dep = deployments[name][chainId]; + console.log(`✓ ${name} [chainId ${chainId}] @ ${dep.address} (block ${dep.deployedOnBlock})`); } } + for (const s of skipped) { + console.warn(` ⚠ skipped ${s.name} — ${s.reason}`); + } } -main().catch(err => { +main().catch((err) => { console.error(err); process.exit(1); }); diff --git a/tsconfig.json b/tsconfig.json index e3f126a18..8d8ce66f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,8 +10,5 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, - "include": [ - "packages/**/*", - "scripts/**/*" - ] + "include": ["packages/**/*", "scripts/**/*"] } From ce3ff4f23d85aff95a461466acb86c87f9f2e996 Mon Sep 17 00:00:00 2001 From: poppyseeddev Date: Fri, 24 Apr 2026 16:38:17 +0200 Subject: [PATCH 2/5] feat: deploy sepolia --- .claude/scheduled_tasks.lock | 1 + packages/nextjs/contracts/FHECounter.ts | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 .claude/scheduled_tasks.lock diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 000000000..7161f6578 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"03f27c2d-4aa2-4b4d-a093-629670d57d95","pid":29712,"procStart":"Fri Apr 24 13:05:08 2026","acquiredAt":1777040627149} \ No newline at end of file diff --git a/packages/nextjs/contracts/FHECounter.ts b/packages/nextjs/contracts/FHECounter.ts index 50ad20a60..ad61746cd 100644 --- a/packages/nextjs/contracts/FHECounter.ts +++ b/packages/nextjs/contracts/FHECounter.ts @@ -1,16 +1,16 @@ /** * This file is autogenerated. Do not edit by hand — run `pnpm generate`. * - * Non-local chain deployments live here; local (chainId 31337) deployments - * live in `./FHECounter.local.ts` (gitignored) and are merged in at module - * load. Import by name: `import { FHECounter } from "~~/contracts/FHECounter";` + * Non-local chain deployments live here; local (chainId 31337) + * deployments live in `./FHECounter.local.ts` (gitignored) and are merged in + * at module load. Import by name: `import { FHECounter } from "~~/contracts/FHECounter";` */ import { FHECounter as FHECounter_LOCAL } from "./FHECounter.local"; import type { ContractDeployment } from "~~/utils/contract"; const REMOTE = { 11155111: { - address: "0x3CC73d13B88cbE6d31Ff2Fa8c5A7b12ef68f96c9", + address: "0xB9AA7F7E25c91D7004CcEb71c712Cd58a5fDFb03", abi: [ { type: "function", @@ -96,7 +96,7 @@ const REMOTE = { inputs: [], }, ], - deployedOnBlock: 10678430, + deployedOnBlock: 10723515, }, } as const; From 25e9dbe6b97fb95730699f02a80d1d5859c0fd2a Mon Sep 17 00:00:00 2001 From: poppyseeddev Date: Fri, 24 Apr 2026 16:54:29 +0200 Subject: [PATCH 3/5] docs: fix README --- README.md | 165 +++++------------- .../components/DappWrapperWithProviders.tsx | 4 +- packages/nextjs/services/web3/wagmiSigner.ts | 16 +- 3 files changed, 55 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 069502e6a..e0007ce44 100644 --- a/README.md +++ b/README.md @@ -2,191 +2,118 @@ A minimal React + Foundry template for building FHEVM-enabled dApps. Ships with `FHECounter.sol` (a trivial encrypted counter) and a Next.js frontend that reads, writes, and decrypts its value. -## What is FHEVM? - FHEVM (Fully Homomorphic Encryption Virtual Machine) lets smart contracts compute on encrypted data. Inputs, storage, and ciphertext handles stay private; only authorized callers can decrypt. ## Stack -- **Contracts**: Foundry, Solidity 0.8.27, [forge-fhevm](https://github.com/zama-ai/forge-fhevm) for host contracts + testing helpers -- **Frontend**: Next.js 15 (App Router), React 19, wagmi, viem, RainbowKit, Tailwind + daisyUI -- **FHE SDK**: `@zama-fhe/sdk` + `@zama-fhe/react-sdk` v3 - - `RelayerCleartext` for local anvil (plaintext mirror executor — no KMS/gateway) - - `RelayerWeb` for Sepolia (real relayer, WASM worker) -- **Tooling**: husky + lint-staged pre-commit (prettier + eslint + `forge fmt`), gitleaks scan in CI, GitHub Actions for forge test + frontend typecheck/lint/build +- **Contracts** — Foundry, Solidity 0.8.27, [forge-fhevm](https://github.com/zama-ai/forge-fhevm) for host contracts + testing helpers +- **Frontend** — Next.js 15 (App Router), React 19, wagmi, viem, RainbowKit, Tailwind + daisyUI +- **FHE SDK** — `@zama-fhe/sdk` + `@zama-fhe/react-sdk` v3; `RelayerCleartext` on localhost, `RelayerWeb` on Sepolia ## Prerequisites -- Node.js ≥ 20, pnpm -- [Foundry](https://book.getfoundry.sh/getting-started/installation) (`forge`, `anvil`, `cast`) -- `jq` (for the chain startup script) -- MetaMask +Node.js ≥ 20, pnpm, [Foundry](https://book.getfoundry.sh/getting-started/installation) (`forge` / `anvil` / `cast`), `jq`, MetaMask. ## Quick start ```bash -pnpm install +pnpm install # node deps + husky + regenerate ABIs +pnpm contracts:install # forge soldeer install — required before `pnpm chain` ``` -The `postinstall` hook regenerates `packages/nextjs/contracts/*.ts` from any existing broadcasts, and `prepare` installs husky hooks. - -### Local (recommended for development) - -Two terminals: +### Local ```bash -# 1. Start anvil + deploy the FHEVM cleartext host stack + FHECounter +# Terminal 1 — anvil + FHEVM cleartext host + FHECounter pnpm chain -# 2. Start the frontend +# Terminal 2 — frontend (http://localhost:3000) pnpm start ``` -Open http://localhost:3000 and add the local network to MetaMask: +Add the local network to MetaMask: RPC `http://127.0.0.1:8545`, chain id `31337`. Import any anvil dev account (e.g. private key `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`, address `0xf39F…2266`, 10 000 ETH). -- **RPC URL**: `http://127.0.0.1:8545` -- **Chain ID**: `31337` -- **Currency**: `ETH` - -Import an anvil dev account (10,000 ETH each) — e.g. private key `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` (address `0xf39F…2266`). - -To redeploy `FHECounter` without restarting anvil, run `pnpm deploy:localhost` in a third terminal. +To redeploy `FHECounter` without restarting anvil: `pnpm deploy:localhost`. ### Sepolia -Copy the example env file and fill it in: - ```bash -cp .env.example .env.local +cp .env.example .env.local # then fill in the three values below ``` ```bash -# .env.local -DEPLOYER_PRIVATE_KEY=0x... # deployer funded with Sepolia ETH +DEPLOYER_PRIVATE_KEY=0x... # deployer funded with Sepolia ETH SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY -ETHERSCAN_API_KEY=... # optional, enables --verify +ETHERSCAN_API_KEY=... # optional, enables --verify ``` -Add to `packages/nextjs/.env.local`: +Add an Alchemy key to `packages/nextjs/.env.local`: ```bash NEXT_PUBLIC_ALCHEMY_API_KEY=YOUR_KEY ``` -Then: +Deploy + run: ```bash -pnpm deploy:sepolia # forge script → writes deployment → regenerates ABIs -pnpm start # same frontend picks up the 11155111 entry automatically +pnpm deploy:sepolia +pnpm start ``` ## Scripts | Command | What it does | | ------------------------ | -------------------------------------------------------------------------------------------- | -| `pnpm chain` | Starts anvil on 8545 + deploys FHEVM cleartext host stack + `FHECounter` | -| `pnpm deploy:localhost` | Deploys `FHECounter` to local anvil + regenerates frontend ABIs | -| `pnpm deploy:sepolia` | Deploys to Sepolia (reads `.env.local`) + regenerates frontend ABIs | -| `pnpm contracts:install` | `forge soldeer install` in `packages/foundry` | +| `pnpm chain` | Anvil + FHEVM cleartext host + `FHECounter` on port 8545 | +| `pnpm deploy:localhost` | Deploys `FHECounter` to local anvil, then regenerates frontend ABIs | +| `pnpm deploy:sepolia` | Deploys to Sepolia (reads `.env.local`), then regenerates frontend ABIs | +| `pnpm contracts:install` | `forge soldeer install` — fetches forge-fhevm and other contract deps | | `pnpm contracts:build` | `forge build` in `packages/foundry` | | `pnpm contracts:test` | `forge test -vv` in `packages/foundry` | -| `pnpm compile` | Alias for `contracts:build` | -| `pnpm test` | Alias for `contracts:test` (forge only — no frontend tests) | | `pnpm generate` | Emits `packages/nextjs/contracts/.ts` + `.local.ts` from forge broadcasts + out/ | -| `pnpm start` | `next dev` (http://localhost:3000) | +| `pnpm start` | `next dev` | | `pnpm next:build` | Production build of the frontend | | `pnpm next:check-types` | TypeScript check on the frontend | | `pnpm lint` | Lint the frontend | -| `pnpm format` | Prettier write on the whole repo | -| `pnpm format:check` | Prettier check (no write) — used by CI | +| `pnpm format` | Prettier over the whole repo (`format:check` for no-write) | ## Project structure ``` fhevm-react-template/ -├── .github/workflows/ci.yml # forge test + frontend typecheck/lint/build + gitleaks -├── .husky/pre-commit # runs lint-staged -├── .gitleaks.toml # gitleaks allowlist/stopwords -├── .prettierrc.json # root prettier config -├── .env.example # copy to .env.local for Sepolia deploys -├── scripts/ -│ ├── chain.sh # anvil + FHEVM host + FHECounter -│ ├── deploy-localhost.sh -│ ├── deploy-sepolia.sh -│ └── generateTsAbis.ts # emits per-contract .ts + .local.ts sidecars -└── packages/ - ├── foundry/ # Solidity contracts + forge tests - │ ├── src/FHECounter.sol - │ ├── script/DeployFHECounter.s.sol - │ ├── test/FHECounter.t.sol # inherits forge-fhevm's FhevmTest - │ ├── foundry.toml - │ └── remappings.txt - └── nextjs/ # React frontend - ├── app/ - ├── components/ - │ └── DappWrapperWithProviders.tsx # wires ZamaProvider + relayer - ├── hooks/ - │ └── fhecounter-example/useFHECounterWagmi.tsx - ├── services/web3/ - │ └── wagmiSigner.ts # local workaround for SDK 3.0.0's broken WagmiSigner - ├── contracts/ - │ ├── FHECounter.ts # autogenerated — non-local (Sepolia) deployments, tracked - │ └── FHECounter.local.ts # autogenerated — chainId 31337 overlay, gitignored - ├── utils/contract.ts # ContractDeployment type + deploymentFor() helper - └── scaffold.config.ts +├── scripts/ # chain.sh, deploy-*.sh, generateTsAbis.ts +├── packages/foundry/ # Solidity contracts +│ ├── src/FHECounter.sol +│ ├── script/DeployFHECounter.s.sol +│ └── test/FHECounter.t.sol # inherits forge-fhevm's FhevmTest +└── packages/nextjs/ # Frontend + ├── components/DappWrapperWithProviders.tsx # wires ZamaProvider + relayer + ├── hooks/fhecounter-example/useFHECounterWagmi.tsx + ├── contracts/ + │ ├── FHECounter.ts # non-local (Sepolia, …) — tracked + │ └── FHECounter.local.ts # chainId 31337 overlay — gitignored + └── utils/contract.ts # ContractDeployment + deploymentFor() ``` -### ABI generation - -`scripts/generateTsAbis.ts` walks `packages/foundry/broadcast/*/*/run-*.json` and `packages/foundry/out/` to produce **one pair of files per contract**: - -- `packages/nextjs/contracts/.ts` — non-local chain entries (Sepolia, mainnet, etc.). Tracked in git. -- `packages/nextjs/contracts/.local.ts` — chainId 31337 overlay. Gitignored. - -The main file imports its sidecar and merges at module load, so consumers stay agnostic to where a deployment lives. A `postinstall` hook runs the generator on every `pnpm install`, and a fresh clone with no broadcasts gets empty stub sidecars automatically so imports resolve. +The per-contract `Name.ts` imports `Name.local.ts` and merges at module load, so consumer code is agnostic to which chain a deployment lives on. `postinstall` regenerates both on every `pnpm install`, including an empty stub sidecar on a fresh clone. ## Troubleshooting -### MetaMask nonce mismatch after restarting anvil - -MetaMask caches nonces; anvil resets them on restart. Fix: - -1. MetaMask → Settings → Advanced → **Clear activity tab data** - -### Stale view-function results - -MetaMask also caches view-function results across reloads. After restarting anvil, **restart your browser** (not just the tab) to clear the cache. - -### `Contract address is not a valid address` - -The Zama relayer SDK requires EIP-55 checksummed addresses. `scripts/generateTsAbis.ts` already checksums via viem's `getAddress()` — if you see this error, rerun `pnpm generate` after a deploy. - -### Sepolia entry disappeared from `FHECounter.ts` - -Shouldn't happen on current `main` — the generator preserves the tracked REMOTE entries when a run only produces local broadcasts. If it does, rerun `pnpm deploy:sepolia` to repopulate. - -### `pnpm install` asks for a package manager version - -The root `package.json` pins `packageManager: "pnpm@10.18.3"`. Upgrade pnpm (`corepack prepare pnpm@10.18.3 --activate`) or match your local install. - -### Sepolia deploy fails with weird path errors - -Your `.env.local` likely has a typo (double `==`, spaces around `=`, quoted values with stray chars). Inspect and fix. +- **MetaMask nonce mismatch after restarting anvil** — MetaMask → Settings → Advanced → _Clear activity tab data_. +- **Stale view-function results** — MetaMask caches across reloads; restart the browser (not the tab). +- **`Contract address is not a valid address`** — the relayer SDK requires EIP-55 checksummed addresses. Rerun `pnpm generate`. +- **`pnpm install` asks for a package manager version** — the root pins `packageManager: "pnpm@10.18.3"`. `corepack prepare pnpm@10.18.3 --activate` or match locally. ## FHEVM notes -- **ACL is mandatory.** Every encrypted value needs `FHE.allowThis(handle)` + `FHE.allow(handle, user)` — without it, reads silently fail. `FHECounter.sol` does this explicitly. -- **`euint32` vs `euint64`.** Types are baked into ciphertext handles. The frontend's `type: "euint32"` must match the contract's `externalEuint32` parameter — a mismatch reverts with `InvalidType()`. -- **Local uses cleartext mode.** Anvil runs a `CleartextFHEVMExecutor` from forge-fhevm that mirrors every FHE op into a `plaintexts(bytes32)` mapping. No KMS, no gateway, no WASM in the browser — `RelayerCleartext` reads plaintext directly. Good for dev, not for production. -- **Sepolia uses real relayer.** `RelayerWeb` spins up a Web Worker and fetches the FHE crypto from Zama's CDN. Requires `NEXT_PUBLIC_ALCHEMY_API_KEY` for the RPC transport. +- **ACL is mandatory.** Every encrypted value needs `FHE.allowThis(handle)` + `FHE.allow(handle, user)` — reads silently fail without it. `FHECounter.sol` does this explicitly. +- **Types are baked into ciphertext handles.** The frontend's `type: "euint32"` must match the contract's `externalEuint32` parameter — mismatch reverts with `InvalidType()`. +- **Local runs cleartext mode.** Anvil hosts a `CleartextFHEVMExecutor` that mirrors every FHE op into a `plaintexts(bytes32)` mapping. No KMS, no gateway, no WASM — `RelayerCleartext` reads plaintext directly. Dev-only. +- **Sepolia uses the real relayer.** `RelayerWeb` spins up a Web Worker and pulls FHE crypto from Zama's CDN. Needs `NEXT_PUBLIC_ALCHEMY_API_KEY`. ## References -- [Zama Protocol docs](https://docs.zama.ai/protocol/) -- [`@zama-fhe/sdk`](https://github.com/zama-ai/sdk) -- [forge-fhevm](https://github.com/zama-ai/forge-fhevm) -- [OpenZeppelin Confidential Contracts](https://github.com/OpenZeppelin/openzeppelin-confidential-contracts) -- [FHEVM Discord](https://discord.com/invite/zama) +[Zama Protocol docs](https://docs.zama.org/) · [`@zama-fhe/sdk`](https://github.com/zama-ai/sdk) · [forge-fhevm](https://github.com/zama-ai/forge-fhevm) · [OpenZeppelin Confidential Contracts](https://github.com/OpenZeppelin/openzeppelin-confidential-contracts) · [Discord](https://discord.com/invite/zama) ## License diff --git a/packages/nextjs/components/DappWrapperWithProviders.tsx b/packages/nextjs/components/DappWrapperWithProviders.tsx index c40ff58ea..06452f6d4 100644 --- a/packages/nextjs/components/DappWrapperWithProviders.tsx +++ b/packages/nextjs/components/DappWrapperWithProviders.tsx @@ -13,8 +13,8 @@ import { WagmiProvider, useChainId } from "wagmi"; import { Header } from "~~/components/Header"; import { BlockieAvatar } from "~~/components/helper"; import { wagmiConfig } from "~~/services/web3/wagmiConfig"; -// Local re-implementation — see services/web3/wagmiSigner.ts for why we can't -// use @zama-fhe/react-sdk/wagmi directly in SDK 3.0.0. +// Swap to `@zama-fhe/react-sdk/wagmi` once a patched stable ships — the fix +// is already in the alpha track (≥ 3.0.0-alpha.16). See wagmiSigner.ts. import { WagmiSigner } from "~~/services/web3/wagmiSigner"; // Module-scoped — the signer, keypair store and session store are chain-agnostic diff --git a/packages/nextjs/services/web3/wagmiSigner.ts b/packages/nextjs/services/web3/wagmiSigner.ts index b8ac237a7..dcab145be 100644 --- a/packages/nextjs/services/web3/wagmiSigner.ts +++ b/packages/nextjs/services/web3/wagmiSigner.ts @@ -14,15 +14,13 @@ import { /** * Wagmi-backed GenericSigner. * - * Reimplements `@zama-fhe/react-sdk/wagmi`'s WagmiSigner locally because the - * bundled version imports `watchConnection` from `wagmi/actions`, which wagmi - * (through ≥2.22.x) does not export. wagmi exposes `watchAccount` instead, - * which delivers the same disconnect / account-change / chain-change events - * we need for the SDK's session lifecycle. - * - * Remove this file and switch to `import { WagmiSigner } from - * "@zama-fhe/react-sdk/wagmi"` once the upstream fix reaches a stable - * @zama-fhe/react-sdk release. + * Reimplements `@zama-fhe/react-sdk/wagmi`'s WagmiSigner locally because + * @zama-fhe/react-sdk@3.0.0 (stable) imports `watchConnection` from + * `wagmi/actions`, and wagmi only exports `watchAccount`. The upstream fix + * is already in the alpha track (≥ 3.0.0-alpha.16 uses `watchAccount`); + * delete this file and switch `DappWrapperWithProviders` back to + * `import { WagmiSigner } from "@zama-fhe/react-sdk/wagmi"` once the fix + * reaches a stable release. */ export class WagmiSigner implements GenericSigner { private config: Config; From 3b4f5323ceaa533780be866b120b133ac72ca179 Mon Sep 17 00:00:00 2001 From: poppyseeddev Date: Fri, 24 Apr 2026 17:16:17 +0200 Subject: [PATCH 4/5] fix: remove outdated CLAUDE.md --- CLAUDE.md | 124 ------------------------------------------------------ 1 file changed, 124 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 981c80fcb..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,124 +0,0 @@ -# CLAUDE.md - -Guidance for Claude Code when working in this repository. - -## Project overview - -Monorepo template for building FHEVM dApps — confidential smart contracts with Fully Homomorphic Encryption via Zama's stack. Contracts in Foundry, frontend in Next.js. - -## Initial setup - -Requires Node.js ≥ 20, pnpm, Foundry (`forge`/`anvil`/`cast`), and `jq`. - -```bash -pnpm install -``` - -## Local development (3 terminals) - -```bash -# Terminal 1: anvil + FHEVM cleartext host stack -pnpm chain # http://127.0.0.1:8545, chainId 31337 - -# Terminal 2: deploy FHECounter + regenerate frontend ABIs -pnpm deploy:localhost - -# Terminal 3: frontend -pnpm start # http://localhost:3000 -``` - -`pnpm chain` runs `scripts/chain.sh` which starts anvil and then invokes `forge-fhevm/deploy-local.sh` (from the soldeer-installed copy under `packages/foundry/dependencies/`) to deploy the **cleartext** FHEVM host contracts (`CleartextFHEVMExecutor` and friends) at the addresses that `@zama-fhe/sdk/cleartext`'s `hardhatCleartextConfig` expects. This is what makes `RelayerCleartext` work locally — without it the frontend can't decrypt on 31337. - -## Common commands - -### Contracts (`packages/foundry`) - -```bash -pnpm compile # forge build -pnpm test # forge test (uses forge-fhevm's FhevmTest) -cd packages/foundry && forge test -vv # verbose -``` - -### Frontend (`packages/nextjs`) - -```bash -pnpm next:build # production build -pnpm next:check-types # tsc -pnpm next:lint # eslint -``` - -### Formatting / linting - -```bash -pnpm format # prettier on frontend -pnpm lint # eslint on frontend -``` - -## Architecture - -### Monorepo layout (pnpm workspaces) - -**`packages/foundry`** — Solidity contracts + forge tests. - -- `src/FHECounter.sol` — tiny encrypted-counter example inheriting `ZamaEthereumConfig`. -- `script/DeployFHECounter.s.sol` — single-contract deploy script (`vm.broadcast`). -- `test/FHECounter.t.sol` — tests inherit `forge-fhevm/FhevmTest.sol` and use `encryptUint32` / `signUserDecrypt` / `userDecrypt` helpers. -- `foundry.toml` uses `evm_version = "cancun"`, `solc 0.8.27`, soldeer for deps. -- `remappings.txt` wires soldeer-installed paths to familiar names (`@fhevm/solidity/`, `forge-fhevm/`, etc.). - -**`packages/nextjs`** — React app. - -- App Router (`app/`), RainbowKit + wagmi for wallet UI. -- `components/DappWrapperWithProviders.tsx` wires `ZamaProvider`. **Key pattern**: relayer is swapped per chain — `RelayerCleartext(hardhatCleartextConfig)` for 31337, `RelayerWeb` for Sepolia/mainnet. Provider re-mounts on chain change; the old relayer is `.terminate()`d in cleanup. -- `services/web3/wagmiSigner.ts` — local `GenericSigner` implementation built on `wagmi/actions`. Replaces `@zama-fhe/react-sdk/wagmi`, which has a broken `watchConnection` import in 2.2.0. -- `hooks/fhecounter-example/useFHECounterWagmi.tsx` — the example hook: `useEncrypt`, `useWriteContract`, `useUserDecrypt`. Important: the encryption `type:` literal must match the contract's `externalEuintN` parameter, or `FHE.fromExternal` reverts with `InvalidType()`. -- `contracts/deployedContracts.ts` — autogenerated (see below). -- `scaffold.config.ts` defines `targetNetworks = [hardhat, sepolia]` and throws in production if `NEXT_PUBLIC_ALCHEMY_API_KEY` is missing. - -### ABI + address generation - -Deploy → regenerate is one script. `scripts/generateTsAbis.ts` walks `packages/foundry/broadcast/*/*/run-latest.json` for every chain a deploy has run on, reads the ABI from `packages/foundry/out/.sol/.json`, checksums the address with viem's `getAddress()` (the Zama relayer SDK enforces EIP-55 via `isChecksummedAddress`), and writes `packages/nextjs/contracts/deployedContracts.ts`. - -`broadcast/` is git-ignored per `packages/foundry/.gitignore` — the single source of truth for the frontend is the generated TS file, which is tracked. - -### Network configuration - -Foundry (`foundry.toml`): solc 0.8.27, optimizer 800 runs. Soldeer deps: `forge-fhevm`, `@fhevm-solidity`, `@openzeppelin-confidential-contracts`, `@openzeppelin-contracts`, `@encrypted-types`. - -Frontend (`scaffold.config.ts`): `targetNetworks = [hardhat, sepolia]`. `NEXT_PUBLIC_ALCHEMY_API_KEY` required in production. Mainnet is implicitly added for ENS lookup; override its RPC via `rpcOverrides` if you see CORS noise from public fallbacks. - -### Env vars - -Repo root `.env.local` (sourced automatically by `deploy-sepolia.sh`): - -- `DEPLOYER_PRIVATE_KEY` — 0x-prefixed deployer key for Sepolia -- `SEPOLIA_RPC_URL` — JSON-RPC endpoint -- `ETHERSCAN_API_KEY` — optional, enables `--verify` - -Frontend `packages/nextjs/.env.local`: - -- `NEXT_PUBLIC_ALCHEMY_API_KEY` — Alchemy key for the Sepolia transport -- `NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID` — optional - -## Key dependencies - -- `@fhevm/solidity` — FHE Solidity library (`FHE.sol`, encrypted types) -- `forge-fhevm` — Foundry host contracts + test helpers (cleartext executor lives here) -- `@zama-fhe/sdk` / `@zama-fhe/react-sdk` v2 — browser FHE SDK (RelayerWeb + RelayerCleartext + ZamaProvider hooks) -- `@openzeppelin/confidential-contracts` — ERC-7984 (confidential tokens), not used here but available for extending -- `viem` + `wagmi` — Ethereum client -- `@rainbow-me/rainbowkit` — wallet connection UI - -## Troubleshooting - -**MetaMask nonce mismatch after anvil restart.** MetaMask caches nonces, anvil resets them. Fix: MetaMask → Settings → Advanced → "Clear activity tab data". - -**Stale view results.** MetaMask caches view-function returns. Restart the browser, not just the tab. - -**"Contract address is not a valid address".** Relayer SDK enforces EIP-55 checksum. Rerun `pnpm generate` — `generateTsAbis.ts` checksums addresses via viem `getAddress()`. If still broken, the deployed address in the generated file is off. - -**`InvalidType()` on contract write.** The frontend encrypted with the wrong FHE type (e.g. `euint64` when the contract takes `externalEuint32`). Type is baked into the first byte of the ciphertext handle. - -**`Invalid relayerUrl: ` on 31337.** Means you're pointing `RelayerWeb` at chain 31337 instead of `RelayerCleartext`. Check `DappWrapperWithProviders.tsx`'s chain switch. - -**`plaintexts(bytes32)` revert on 31337.** `pnpm chain` didn't run the forge-fhevm deploy step — the deployed executor isn't the cleartext variant. Stop anvil, rerun `pnpm chain` from scratch. From 323eafdb92fa70d7406265fa3616aed9bed44a14 Mon Sep 17 00:00:00 2001 From: poppyseeddev Date: Fri, 24 Apr 2026 17:16:52 +0200 Subject: [PATCH 5/5] fix: remove outdated CLAUDE.md --- .claude/scheduled_tasks.lock | 1 - .gitignore | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 .claude/scheduled_tasks.lock diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock deleted file mode 100644 index 7161f6578..000000000 --- a/.claude/scheduled_tasks.lock +++ /dev/null @@ -1 +0,0 @@ -{"sessionId":"03f27c2d-4aa2-4b4d-a093-629670d57d95","pid":29712,"procStart":"Fri Apr 24 13:05:08 2026","acquiredAt":1777040627149} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a53844db8..81044382a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ tmp* packages/nextjs/contracts/*.local.ts CLAUDE.md +.claude \ No newline at end of file