diff --git a/README.md b/README.md index 3cfb5e29..2986f14d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ FHEVM (Fully Homomorphic Encryption Virtual Machine) enables computation on encr - **🔗 RainbowKit**: Seamless wallet connection and management - **🌐 Multi-Network Support**: Works on both Sepolia testnet and local Hardhat node - **📦 Monorepo Structure**: Organized packages for SDK, contracts, and frontend +- **🧩 Vue Composables**: Composition API utilities available via `@fhevm-sdk/vue` ## 📋 Prerequinextjss diff --git a/packages/fhevm-sdk/package.json b/packages/fhevm-sdk/package.json index 47445861..d0efd4e3 100644 --- a/packages/fhevm-sdk/package.json +++ b/packages/fhevm-sdk/package.json @@ -26,6 +26,10 @@ "./react": { "types": "./src/react/index.ts", "default": "./dist/react/index.js" + }, + "./vue": { + "types": "./src/vue/index.ts", + "default": "./dist/vue/index.js" } }, "scripts": { @@ -42,7 +46,16 @@ "@fhevm/mock-utils": "^0.1.0", "@zama-fhe/relayer-sdk": "^0.2.0", "ethers": "^6.13.4", - "react": "^18.0.0 || ^19.0.0" + "react": "^18.0.0 || ^19.0.0", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "vue": { + "optional": true + } }, "devDependencies": { "@types/node": "~18.19.50", @@ -53,6 +66,7 @@ "fake-indexeddb": "~6.0.0", "jsdom": "^27.0.0", "react": "~19.0.0", + "vue": "^3.4.0", "typescript": "~5.8.2", "vitest": "~2.1.8" } diff --git a/packages/fhevm-sdk/src/internal/encryptionUtils.ts b/packages/fhevm-sdk/src/internal/encryptionUtils.ts new file mode 100644 index 00000000..1f6f2f45 --- /dev/null +++ b/packages/fhevm-sdk/src/internal/encryptionUtils.ts @@ -0,0 +1,64 @@ +export type EncryptResult = { + handles: Uint8Array[]; + inputProof: Uint8Array; +}; + +// Map external encrypted integer type to RelayerEncryptedInput builder method +export const getEncryptionMethod = (internalType: string) => { + switch (internalType) { + case "externalEbool": + return "addBool" as const; + case "externalEuint8": + return "add8" as const; + case "externalEuint16": + return "add16" as const; + case "externalEuint32": + return "add32" as const; + case "externalEuint64": + return "add64" as const; + case "externalEuint128": + return "add128" as const; + case "externalEuint256": + return "add256" as const; + case "externalEaddress": + return "addAddress" as const; + default: + console.warn(`Unknown internalType: ${internalType}, defaulting to add64`); + return "add64" as const; + } +}; + +// Convert Uint8Array or hex-like string to 0x-prefixed hex string +export const toHex = (value: Uint8Array | string): `0x${string}` => { + if (typeof value === "string") { + return (value.startsWith("0x") ? value : `0x${value}`) as `0x${string}`; + } + // value is Uint8Array + return ("0x" + Buffer.from(value).toString("hex")) as `0x${string}`; +}; + +// Build contract params from EncryptResult and ABI for a given function +export const buildParamsFromAbi = (enc: EncryptResult, abi: any[], functionName: string): any[] => { + const fn = abi.find((item: any) => item.type === "function" && item.name === functionName); + if (!fn) throw new Error(`Function ABI not found for ${functionName}`); + + return fn.inputs.map((input: any, index: number) => { + const raw = index === 0 ? enc.handles[0] : enc.inputProof; + switch (input.type) { + case "bytes32": + case "bytes": + return toHex(raw); + case "uint256": + return BigInt(raw as unknown as string); + case "address": + case "string": + return raw as unknown as string; + case "bool": + return Boolean(raw); + default: + console.warn(`Unknown ABI param type ${input.type}; passing as hex`); + return toHex(raw); + } + }); +}; + diff --git a/packages/fhevm-sdk/src/react/useFHEEncryption.ts b/packages/fhevm-sdk/src/react/useFHEEncryption.ts index 01fbfc03..a7a5e158 100644 --- a/packages/fhevm-sdk/src/react/useFHEEncryption.ts +++ b/packages/fhevm-sdk/src/react/useFHEEncryption.ts @@ -4,74 +4,14 @@ import { useCallback, useMemo } from "react"; import { FhevmInstance } from "../fhevmTypes.js"; import { RelayerEncryptedInput } from "@zama-fhe/relayer-sdk/web"; import { ethers } from "ethers"; +import type { EncryptResult } from "../internal/encryptionUtils.js"; -export type EncryptResult = { - handles: Uint8Array[]; - inputProof: Uint8Array; -}; - -// Map external encrypted integer type to RelayerEncryptedInput builder method -export const getEncryptionMethod = (internalType: string) => { - switch (internalType) { - case "externalEbool": - return "addBool" as const; - case "externalEuint8": - return "add8" as const; - case "externalEuint16": - return "add16" as const; - case "externalEuint32": - return "add32" as const; - case "externalEuint64": - return "add64" as const; - case "externalEuint128": - return "add128" as const; - case "externalEuint256": - return "add256" as const; - case "externalEaddress": - return "addAddress" as const; - default: - console.warn(`Unknown internalType: ${internalType}, defaulting to add64`); - return "add64" as const; - } -}; - -// Convert Uint8Array or hex-like string to 0x-prefixed hex string -export const toHex = (value: Uint8Array | string): `0x${string}` => { - if (typeof value === "string") { - return (value.startsWith("0x") ? value : `0x${value}`) as `0x${string}`; - } - // value is Uint8Array - return ("0x" + Buffer.from(value).toString("hex")) as `0x${string}`; -}; - -// Build contract params from EncryptResult and ABI for a given function -export const buildParamsFromAbi = (enc: EncryptResult, abi: any[], functionName: string): any[] => { - const fn = abi.find((item: any) => item.type === "function" && item.name === functionName); - if (!fn) throw new Error(`Function ABI not found for ${functionName}`); - - return fn.inputs.map((input: any, index: number) => { - const raw = index === 0 ? enc.handles[0] : enc.inputProof; - switch (input.type) { - case "bytes32": - case "bytes": - return toHex(raw); - case "uint256": - return BigInt(raw as unknown as string); - case "address": - case "string": - return raw as unknown as string; - case "bool": - return Boolean(raw); - default: - console.warn(`Unknown ABI param type ${input.type}; passing as hex`); - return toHex(raw); - } - }); -}; +export { buildParamsFromAbi, getEncryptionMethod, toHex } from "../internal/encryptionUtils.js"; +export type { EncryptResult } from "../internal/encryptionUtils.js"; export const useFHEEncryption = (params: { instance: FhevmInstance | undefined; - ethersSigner: ethers.JsonRpcSigner | undefined; + ethersSigner: ethers.Signer | undefined; contractAddress: `0x${string}` | undefined; }) => { const { instance, ethersSigner, contractAddress } = params; diff --git a/packages/fhevm-sdk/src/vue/index.ts b/packages/fhevm-sdk/src/vue/index.ts new file mode 100644 index 00000000..fc2d4dd8 --- /dev/null +++ b/packages/fhevm-sdk/src/vue/index.ts @@ -0,0 +1,4 @@ +export * from "./useFhevm"; +export * from "./useFHEEncryption"; +export * from "./useFHEDecrypt"; +export * from "./useInMemoryStorage"; diff --git a/packages/fhevm-sdk/src/vue/useFHEDecrypt.ts b/packages/fhevm-sdk/src/vue/useFHEDecrypt.ts new file mode 100644 index 00000000..8d976b91 --- /dev/null +++ b/packages/fhevm-sdk/src/vue/useFHEDecrypt.ts @@ -0,0 +1,156 @@ +import { computed, ref, shallowRef, unref } from "vue"; +import type { Ref } from "vue"; +import { ethers } from "ethers"; +import { FhevmDecryptionSignature } from "../FhevmDecryptionSignature.js"; +import { GenericStringStorage } from "../storage/GenericStringStorage.js"; +import type { FhevmInstance } from "../fhevmTypes.js"; + +export type FHEDecryptRequest = { handle: string; contractAddress: `0x${string}` }; + +type MaybeRef = T | Ref; + +type DecryptResult = Record; + +export const useFHEDecrypt = (params: { + instance: MaybeRef; + ethersSigner: MaybeRef; + fhevmDecryptionSignatureStorage: MaybeRef; + chainId: MaybeRef; + requests: MaybeRef; +}) => { + const instanceRef = computed(() => unref(params.instance)); + const signerRef = computed(() => unref(params.ethersSigner)); + const storageRef = computed(() => unref(params.fhevmDecryptionSignatureStorage)); + const chainIdRef = computed(() => unref(params.chainId)); + const requestsRef = computed(() => unref(params.requests)); + + const isDecrypting = ref(false); + const message = ref(""); + const results = shallowRef({}); + const error = ref(null); + + let isDecryptingGuard = false; + const lastRequestsKey = ref(""); + + const requestsKey = computed(() => { + const requests = requestsRef.value; + if (!requests || requests.length === 0) return ""; + + const sorted = [...requests].sort((a, b) => + (a.handle + a.contractAddress).localeCompare(b.handle + b.contractAddress), + ); + return JSON.stringify(sorted); + }); + + const canDecrypt = computed(() => { + const instance = instanceRef.value; + const signer = signerRef.value; + const requests = requestsRef.value; + return Boolean(instance && signer && requests && requests.length > 0 && !isDecrypting.value); + }); + + const decrypt = async () => { + if (isDecryptingGuard) return; + + const instance = instanceRef.value; + const signer = signerRef.value; + const requests = requestsRef.value; + const storage = storageRef.value; + const chainId = chainIdRef.value; + + if (!instance || !signer || !requests || requests.length === 0) return; + + lastRequestsKey.value = requestsKey.value; + + isDecryptingGuard = true; + isDecrypting.value = true; + message.value = "Start decrypt"; + error.value = null; + + const isStale = () => + chainId !== chainIdRef.value || signer !== signerRef.value || requestsKey.value !== lastRequestsKey.value; + + try { + const uniqueAddresses = Array.from(new Set(requests.map((r: FHEDecryptRequest) => r.contractAddress))); + const sig = await FhevmDecryptionSignature.loadOrSign( + instance, + uniqueAddresses as `0x${string}`[], + signer, + storage, + ); + + if (!sig) { + message.value = "Unable to build FHEVM decryption signature"; + error.value = "SIGNATURE_ERROR: Failed to create decryption signature"; + return; + } + + if (isStale()) { + message.value = "Ignore FHEVM decryption"; + return; + } + + message.value = "Call FHEVM userDecrypt..."; + + const mutableReqs = requests.map((r: FHEDecryptRequest) => ({ handle: r.handle, contractAddress: r.contractAddress })); + let decryptResult: DecryptResult = {}; + try { + decryptResult = await instance.userDecrypt( + mutableReqs, + sig.privateKey, + sig.publicKey, + sig.signature, + sig.contractAddresses, + sig.userAddress, + sig.startTimestamp, + sig.durationDays, + ); + } catch (e) { + const err = e as { name?: string; message?: string } | undefined; + const code = err && err.name ? err.name : "DECRYPT_ERROR"; + const msg = err && err.message ? err.message : "Decryption failed"; + error.value = `${code}: ${msg}`; + message.value = "FHEVM userDecrypt failed"; + return; + } + + message.value = "FHEVM userDecrypt completed!"; + + if (isStale()) { + message.value = "Ignore FHEVM decryption"; + return; + } + + results.value = decryptResult; + } catch (e) { + const err = e as { name?: string; message?: string } | undefined; + const code = err && err.name ? err.name : "UNKNOWN_ERROR"; + const msg = err && err.message ? err.message : "Unknown error"; + error.value = `${code}: ${msg}`; + message.value = "FHEVM decryption errored"; + } finally { + isDecryptingGuard = false; + isDecrypting.value = false; + lastRequestsKey.value = requestsKey.value; + } + }; + + const setMessage = (value: string) => { + message.value = value; + }; + + const setError = (value: string | null) => { + error.value = value; + }; + + return { + canDecrypt, + decrypt, + isDecrypting, + message, + results, + error, + setMessage, + setError, + } as const; +}; diff --git a/packages/fhevm-sdk/src/vue/useFHEEncryption.ts b/packages/fhevm-sdk/src/vue/useFHEEncryption.ts new file mode 100644 index 00000000..df7795f3 --- /dev/null +++ b/packages/fhevm-sdk/src/vue/useFHEEncryption.ts @@ -0,0 +1,47 @@ +import { computed, unref } from "vue"; +import type { Ref } from "vue"; +import type { Signer } from "ethers"; +import type { FhevmInstance } from "../fhevmTypes.js"; +import type { EncryptResult } from "../internal/encryptionUtils.js"; + +export { buildParamsFromAbi, getEncryptionMethod, toHex } from "../internal/encryptionUtils.js"; +export type { EncryptResult } from "../internal/encryptionUtils.js"; + +type MaybeRef = T | Ref; + +type RelayerEncryptedInputLike = { + encrypt(): Promise; +}; + +export const useFHEEncryption = (params: { + instance: MaybeRef; + ethersSigner: MaybeRef; + contractAddress: MaybeRef<`0x${string}` | undefined>; +}) => { + const instanceRef = computed(() => unref(params.instance)); + const signerRef = computed(() => unref(params.ethersSigner)); + const contractAddressRef = computed(() => unref(params.contractAddress)); + + const canEncrypt = computed(() => Boolean(instanceRef.value && signerRef.value && contractAddressRef.value)); + + const encryptWith = async ( + buildFn: (builder: RelayerEncryptedInputLike) => void, + ): Promise => { + const instance = instanceRef.value; + const signer = signerRef.value; + const contractAddress = contractAddressRef.value; + + if (!instance || !signer || !contractAddress) return undefined; + + const userAddress = await signer.getAddress(); + const input = instance.createEncryptedInput(contractAddress, userAddress) as RelayerEncryptedInputLike; + buildFn(input); + const enc = await input.encrypt(); + return enc; + }; + + return { + canEncrypt, + encryptWith, + } as const; +}; diff --git a/packages/fhevm-sdk/src/vue/useFhevm.ts b/packages/fhevm-sdk/src/vue/useFhevm.ts new file mode 100644 index 00000000..17c6edf7 --- /dev/null +++ b/packages/fhevm-sdk/src/vue/useFhevm.ts @@ -0,0 +1,158 @@ +import { computed, onScopeDispose, ref, shallowRef, unref, watch } from "vue"; +import type { Ref } from "vue"; +import type { FhevmInstance } from "../fhevmTypes.js"; +import { createFhevmInstance } from "../internal/fhevm.js"; + +function assert(condition: boolean, message?: string): asserts condition { + if (!condition) { + const m = message ? `Assertion failed: ${message}` : `Assertion failed.`; + throw new Error(m); + } +} + +export type FhevmGoState = "idle" | "loading" | "ready" | "error"; + +type MaybeRef = T | Ref; +type ProviderLike = string | { request?: (...args: any[]) => Promise } | undefined; + +export interface UseFhevmParameters { + provider: MaybeRef; + chainId: MaybeRef; + enabled?: MaybeRef; + initialMockChains?: MaybeRef> | undefined>; +} + +export const useFhevm = (parameters: UseFhevmParameters) => { + const providerValue = computed(() => unref(parameters.provider)); + const chainIdValue = computed(() => unref(parameters.chainId)); + const enabledValue = computed(() => { + const raw = parameters.enabled === undefined ? undefined : unref(parameters.enabled); + return raw ?? true; + }); + const mockChainsValue = computed(() => + parameters.initialMockChains === undefined ? undefined : unref(parameters.initialMockChains) ?? undefined, + ); + + const instance = shallowRef(undefined); + const status = ref("idle"); + const error = shallowRef(undefined); + + const isRunning = ref(enabledValue.value ?? true); + const providerVersion = ref(0); + + const abortController = shallowRef(null); + const providerRef = shallowRef(undefined); + const chainIdRef = ref(undefined); + const mockChainsRef = shallowRef | undefined>(undefined); + + const refresh = () => { + if (abortController.value) { + providerRef.value = undefined; + chainIdRef.value = undefined; + abortController.value.abort(); + abortController.value = null; + } + + providerRef.value = providerValue.value; + chainIdRef.value = chainIdValue.value; + mockChainsRef.value = mockChainsValue.value ? { ...mockChainsValue.value } : undefined; + + instance.value = undefined; + error.value = undefined; + status.value = "idle"; + + if (providerValue.value !== undefined) { + providerVersion.value += 1; + } + }; + + watch( + [providerValue, chainIdValue, mockChainsValue], + () => { + refresh(); + }, + { immediate: true }, + ); + + watch( + enabledValue, + (value: boolean | undefined) => { + isRunning.value = value ?? true; + }, + { immediate: true }, + ); + + watch( + [isRunning, providerVersion], + async ([running]: [boolean, number]) => { + if (!running) { + if (abortController.value) { + abortController.value.abort(); + abortController.value = null; + } + instance.value = undefined; + error.value = undefined; + status.value = "idle"; + return; + } + + if (providerRef.value === undefined) { + instance.value = undefined; + error.value = undefined; + status.value = "idle"; + return; + } + + if (!abortController.value || abortController.value.signal.aborted) { + abortController.value = new AbortController(); + } + + const controller = abortController.value; + assert(controller !== null, "AbortController not initialized"); + assert(!controller.signal.aborted, "!controller.signal.aborted"); + + status.value = "loading"; + error.value = undefined; + + const currentProvider = providerRef.value; + const currentMockChains = mockChainsRef.value; + + try { + const inst = await createFhevmInstance({ + signal: controller.signal, + provider: currentProvider as any, + mockChains: currentMockChains as any, + onStatusChange: s => console.log(`[useFhevm][vue] createFhevmInstance status changed: ${s}`), + }); + + if (controller.signal.aborted) return; + assert(currentProvider === providerRef.value, "currentProvider === providerRef.value"); + + instance.value = inst; + status.value = "ready"; + } catch (err) { + if (controller.signal.aborted) return; + assert(currentProvider === providerRef.value, "currentProvider === providerRef.value"); + + instance.value = undefined; + error.value = err instanceof Error ? err : new Error(String(err)); + status.value = "error"; + } + }, + { immediate: true }, + ); + + onScopeDispose(() => { + if (abortController.value) { + abortController.value.abort(); + abortController.value = null; + } + }); + + return { + instance, + refresh, + error, + status, + } as const; +}; diff --git a/packages/fhevm-sdk/src/vue/useInMemoryStorage.ts b/packages/fhevm-sdk/src/vue/useInMemoryStorage.ts new file mode 100644 index 00000000..5d532885 --- /dev/null +++ b/packages/fhevm-sdk/src/vue/useInMemoryStorage.ts @@ -0,0 +1,26 @@ +import { inject, provide } from "vue"; +import type { InjectionKey } from "vue"; +import { GenericStringInMemoryStorage, GenericStringStorage } from "../storage/GenericStringStorage.js"; + +interface UseInMemoryStorageState { + storage: GenericStringStorage; +} + +const InMemoryStorageSymbol: InjectionKey = Symbol("fhevm-sdk:InMemoryStorage"); + +export const provideInMemoryStorage = (storage?: GenericStringStorage): UseInMemoryStorageState => { + const storageInstance = storage ?? new GenericStringInMemoryStorage(); + const state: UseInMemoryStorageState = { storage: storageInstance }; + provide(InMemoryStorageSymbol, state); + return state; +}; + +export const useInMemoryStorage = (): UseInMemoryStorageState => { + const context = inject(InMemoryStorageSymbol, null); + if (!context) { + throw new Error("useInMemoryStorage must be used after calling provideInMemoryStorage in the current component setup"); + } + return context; +}; + +export const createInMemoryStorage = () => new GenericStringInMemoryStorage(); diff --git a/packages/fhevm-sdk/test/exports.test.ts b/packages/fhevm-sdk/test/exports.test.ts index d51dbd09..7d982a77 100644 --- a/packages/fhevm-sdk/test/exports.test.ts +++ b/packages/fhevm-sdk/test/exports.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from "vitest"; import * as main from "../src"; import * as react from "../src/react"; +import * as vue from "../src/vue"; import * as core from "../src/core"; import * as storage from "../src/storage"; @@ -13,6 +14,10 @@ describe("exports", () => { expect(react).toBeTruthy(); }); + it("vue exports are present", () => { + expect(vue).toBeTruthy(); + }); + it("core exports are present", () => { expect(core).toBeTruthy(); }); diff --git a/packages/fhevm-sdk/test/vue.test.ts b/packages/fhevm-sdk/test/vue.test.ts new file mode 100644 index 00000000..41851690 --- /dev/null +++ b/packages/fhevm-sdk/test/vue.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, vi } from "vitest"; +import { createApp, defineComponent, h } from "vue"; +import type { Signer } from "ethers"; +import { provideInMemoryStorage, useInMemoryStorage, useFHEEncryption } from "../src/vue"; + +describe("vue exports", () => { + it("provides and consumes in-memory storage", () => { + const results: Array = []; + + const Child = defineComponent({ + name: "ChildConsumer", + setup() { + const { storage } = useInMemoryStorage(); + results.push(storage.getItem("demo") as string | null); + return () => null; + }, + }); + + const Parent = defineComponent({ + name: "ParentProvider", + setup() { + const { storage } = provideInMemoryStorage(); + storage.setItem("demo", "value"); + return () => h(Child); + }, + }); + + const app = createApp(Parent); + const mountPoint = globalThis.document.createElement("div"); + app.mount(mountPoint); + + expect(results).toEqual(["value"]); + + app.unmount(); + }); + + it("encryptWith delegates to provided instance", async () => { + const encryptionResult = { handles: [new Uint8Array([1])], inputProof: new Uint8Array([2]) }; + const encryptMock = vi.fn(async () => encryptionResult); + const createEncryptedInput = vi.fn(() => ({ encrypt: encryptMock })); + + const instance = { + createEncryptedInput, + } as unknown as { createEncryptedInput: (contractAddress: `0x${string}`, userAddress: string) => unknown }; + + const signer = { + getAddress: vi.fn(async () => "0x0000000000000000000000000000000000000001"), + } as unknown as Signer; + + const { canEncrypt, encryptWith } = useFHEEncryption({ + instance, + ethersSigner: signer, + contractAddress: "0x0000000000000000000000000000000000000001", + }); + + expect(canEncrypt.value).toBe(true); + + const buildFn = vi.fn(); + const result = await encryptWith(buildFn); + + expect(buildFn).toHaveBeenCalledTimes(1); + expect(encryptMock).toHaveBeenCalledTimes(1); + expect(result).toEqual(encryptionResult); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45434acc..490afdf5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,7 +42,7 @@ importers: version: 19.0.14 '@vitest/coverage-v8': specifier: 2.1.9 - version: 2.1.9(vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10))(lightningcss@1.29.2)) + version: 2.1.9(vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10))(lightningcss@1.29.2)) '@zama-fhe/relayer-sdk': specifier: 0.2.0 version: 0.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -54,7 +54,7 @@ importers: version: 6.0.1 jsdom: specifier: ^27.0.0 - version: 27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10) + version: 27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10) react: specifier: ~19.0.0 version: 19.0.0 @@ -63,7 +63,10 @@ importers: version: 5.8.3 vitest: specifier: ~2.1.8 - version: 2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10))(lightningcss@1.29.2) + version: 2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10))(lightningcss@1.29.2) + vue: + specifier: ^3.4.0 + version: 3.5.22(typescript@5.8.3) packages/hardhat: dependencies: @@ -254,7 +257,7 @@ importers: version: 4.0.15 '@trivago/prettier-plugin-sort-imports': specifier: ~4.3.0 - version: 4.3.0(prettier@3.5.3) + version: 4.3.0(@vue/compiler-sfc@3.5.22)(prettier@3.5.3) '@types/node': specifier: ~18.19.50 version: 18.19.127 @@ -2243,6 +2246,35 @@ packages: '@vitest/utils@2.1.9': resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vue/compiler-core@3.5.22': + resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==} + + '@vue/compiler-dom@3.5.22': + resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==} + + '@vue/compiler-sfc@3.5.22': + resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==} + + '@vue/compiler-ssr@3.5.22': + resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==} + + '@vue/reactivity@3.5.22': + resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==} + + '@vue/runtime-core@3.5.22': + resolution: {integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==} + + '@vue/runtime-dom@3.5.22': + resolution: {integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==} + + '@vue/server-renderer@3.5.22': + resolution: {integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==} + peerDependencies: + vue: 3.5.22 + + '@vue/shared@3.5.22': + resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} + '@wagmi/connectors@5.9.4': resolution: {integrity: sha512-k/GSdYS6nuL0zLq5H7XasPmKr3Gnvplq+yQhTfRBu/o5Bh+B3aH3Jfq1lUh+t3z5kQcyKJIXw/bZIZ7lVS7UhA==} peerDependencies: @@ -3395,6 +3427,10 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} @@ -5742,6 +5778,10 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + preact@10.24.2: resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} @@ -7025,6 +7065,14 @@ packages: jsdom: optional: true + vue@3.5.22: + resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -7559,9 +7607,9 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.4.49)': + '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)': dependencies: - postcss: 8.4.49 + postcss: 8.5.6 '@csstools/css-tokenizer@3.0.4': {} @@ -9495,7 +9543,7 @@ snapshots: '@tootallnate/once@2.0.0': {} - '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.5.3)': + '@trivago/prettier-plugin-sort-imports@4.3.0(@vue/compiler-sfc@3.5.22)(prettier@3.5.3)': dependencies: '@babel/generator': 7.17.7 '@babel/parser': 7.28.4 @@ -9504,6 +9552,8 @@ snapshots: javascript-natural-sort: 0.7.1 lodash: 4.17.21 prettier: 3.5.3 + optionalDependencies: + '@vue/compiler-sfc': 3.5.22 transitivePeerDependencies: - supports-color @@ -10067,7 +10117,7 @@ snapshots: json-schema-to-ts: 1.6.4 ts-morph: 12.0.0 - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10))(lightningcss@1.29.2))': + '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10))(lightningcss@1.29.2))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -10081,7 +10131,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10))(lightningcss@1.29.2) + vitest: 2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10))(lightningcss@1.29.2) transitivePeerDependencies: - supports-color @@ -10125,6 +10175,60 @@ snapshots: loupe: 3.2.1 tinyrainbow: 1.2.0 + '@vue/compiler-core@3.5.22': + dependencies: + '@babel/parser': 7.28.4 + '@vue/shared': 3.5.22 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.22': + dependencies: + '@vue/compiler-core': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/compiler-sfc@3.5.22': + dependencies: + '@babel/parser': 7.28.4 + '@vue/compiler-core': 3.5.22 + '@vue/compiler-dom': 3.5.22 + '@vue/compiler-ssr': 3.5.22 + '@vue/shared': 3.5.22 + estree-walker: 2.0.2 + magic-string: 0.30.19 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.22': + dependencies: + '@vue/compiler-dom': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/reactivity@3.5.22': + dependencies: + '@vue/shared': 3.5.22 + + '@vue/runtime-core@3.5.22': + dependencies: + '@vue/reactivity': 3.5.22 + '@vue/shared': 3.5.22 + + '@vue/runtime-dom@3.5.22': + dependencies: + '@vue/reactivity': 3.5.22 + '@vue/runtime-core': 3.5.22 + '@vue/shared': 3.5.22 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.22(vue@3.5.22(typescript@5.8.3))': + dependencies: + '@vue/compiler-ssr': 3.5.22 + '@vue/shared': 3.5.22 + vue: 3.5.22(typescript@5.8.3) + + '@vue/shared@3.5.22': {} + '@wagmi/connectors@5.9.4(@types/react@19.0.14)(@wagmi/core@2.19.0(@tanstack/query-core@5.59.20)(@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.34.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(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.34.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': dependencies: '@base-org/account': 1.1.1(@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@3.25.76) @@ -11557,10 +11661,10 @@ snapshots: cssesc@3.0.0: {} - cssstyle@5.3.1(postcss@8.4.49): + cssstyle@5.3.1(postcss@8.5.6): dependencies: '@asamuzakjp/css-color': 4.0.5 - '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.4.49) + '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6) css-tree: 3.1.0 transitivePeerDependencies: - postcss @@ -11825,6 +11929,8 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + entities@4.5.0: {} + entities@6.0.1: {} env-paths@2.2.1: {} @@ -13573,10 +13679,10 @@ snapshots: jsbi@3.2.5: {} - jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10): + jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10): dependencies: '@asamuzakjp/dom-selector': 6.5.6 - cssstyle: 5.3.1(postcss@8.4.49) + cssstyle: 5.3.1(postcss@8.5.6) data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 @@ -14562,6 +14668,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + preact@10.24.2: {} preact@10.27.2: {} @@ -15971,7 +16083,7 @@ snapshots: fsevents: 2.3.3 lightningcss: 1.29.2 - vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10))(lightningcss@1.29.2): + vitest@2.1.9(@edge-runtime/vm@3.2.0)(@types/node@18.19.127)(jsdom@27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10))(lightningcss@1.29.2): dependencies: '@vitest/expect': 2.1.9 '@vitest/mocker': 2.1.9(vite@5.4.20(@types/node@18.19.127)(lightningcss@1.29.2)) @@ -15996,7 +16108,7 @@ snapshots: optionalDependencies: '@edge-runtime/vm': 3.2.0 '@types/node': 18.19.127 - jsdom: 27.0.0(bufferutil@4.0.9)(postcss@8.4.49)(utf-8-validate@5.0.10) + jsdom: 27.0.0(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@5.0.10) transitivePeerDependencies: - less - lightningcss @@ -16008,6 +16120,16 @@ snapshots: - supports-color - terser + vue@3.5.22(typescript@5.8.3): + dependencies: + '@vue/compiler-dom': 3.5.22 + '@vue/compiler-sfc': 3.5.22 + '@vue/runtime-dom': 3.5.22 + '@vue/server-renderer': 3.5.22(vue@3.5.22(typescript@5.8.3)) + '@vue/shared': 3.5.22 + optionalDependencies: + typescript: 5.8.3 + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0