diff --git a/examples/taco/react-viem/README.md b/examples/taco/react-viem/README.md
new file mode 100644
index 000000000..575fbc572
--- /dev/null
+++ b/examples/taco/react-viem/README.md
@@ -0,0 +1,17 @@
+# `react-viem-taco` integration example
+
+Shows how to integrate `@nucypher/taco` into a React application using viem.
+## Usage
+
+```bash
+pnpm install
+pnpm start
+```
+
+Next, go to [http://127.0.0.1:3000/](http://127.0.0.1:8080/) in your browser and
+inspect the UI and the JS console.
+
+## Learn more
+
+Please find developer documentation for
+TACo [here](https://docs.taco.build/).
diff --git a/examples/taco/react-viem/package.json b/examples/taco/react-viem/package.json
new file mode 100644
index 000000000..c0e4b13a7
--- /dev/null
+++ b/examples/taco/react-viem/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "taco-react-example",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "build": "react-scripts build",
+ "check": "pnpm type-check && pnpm build",
+ "eject": "react-scripts eject",
+ "start": "react-scripts start",
+ "test": "react-scripts test",
+ "type-check": "tsc -p tsconfig.build.json"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "dependencies": {
+ "@nucypher/shared": "workspace:*",
+ "@nucypher/taco": "workspace:*",
+ "@nucypher/taco-auth": "workspace:*",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/node": "^20.17.28",
+ "@types/react": "^18.3.20",
+ "@types/react-dom": "^18.3.5",
+ "react-scripts": "^5.0.1"
+ },
+ "peerDependencies": {
+ "ethers": "^5.7.2",
+ "viem": "^2.0.0"
+ }
+}
diff --git a/examples/taco/react-viem/public/favicon.ico b/examples/taco/react-viem/public/favicon.ico
new file mode 100644
index 000000000..a11777cc4
Binary files /dev/null and b/examples/taco/react-viem/public/favicon.ico differ
diff --git a/examples/taco/react-viem/public/index.html b/examples/taco/react-viem/public/index.html
new file mode 100644
index 000000000..e65acb3de
--- /dev/null
+++ b/examples/taco/react-viem/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ React App
+
+
+
+
+
+
+
diff --git a/examples/taco/react-viem/public/logo192.png b/examples/taco/react-viem/public/logo192.png
new file mode 100644
index 000000000..fc44b0a37
Binary files /dev/null and b/examples/taco/react-viem/public/logo192.png differ
diff --git a/examples/taco/react-viem/public/logo512.png b/examples/taco/react-viem/public/logo512.png
new file mode 100644
index 000000000..a4e47a654
Binary files /dev/null and b/examples/taco/react-viem/public/logo512.png differ
diff --git a/examples/taco/react-viem/public/manifest.json b/examples/taco/react-viem/public/manifest.json
new file mode 100644
index 000000000..080d6c77a
--- /dev/null
+++ b/examples/taco/react-viem/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/examples/taco/react-viem/public/robots.txt b/examples/taco/react-viem/public/robots.txt
new file mode 100644
index 000000000..e9e57dc4d
--- /dev/null
+++ b/examples/taco/react-viem/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/examples/taco/react-viem/src/App.tsx b/examples/taco/react-viem/src/App.tsx
new file mode 100644
index 000000000..06f2254fd
--- /dev/null
+++ b/examples/taco/react-viem/src/App.tsx
@@ -0,0 +1,164 @@
+import { fromHexString } from '@nucypher/shared';
+import { conditions, domains, fromBytes, toHexString } from '@nucypher/taco';
+import { hexlify } from 'ethers/lib/utils';
+import { useEffect, useState } from 'react';
+import {
+ createPublicClient,
+ createWalletClient,
+ custom,
+ PublicClient,
+ WalletClient
+} from 'viem';
+import { polygonAmoy } from 'viem/chains';
+
+import useTaco from './hooks/useTaco';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+declare const window: any;
+
+const ritualId = 6; // Replace with your own ritual ID
+const domain = domains.TESTNET;
+
+function App() {
+ const [publicClient, setPublicClient] = useState();
+ const [walletClient, setWalletClient] = useState();
+ const [message, setMessage] = useState('this is a secret');
+ const [encrypting, setEncrypting] = useState(false);
+ const [encryptedText, setEncryptedText] = useState('');
+ const [decrypting, setDecrypting] = useState(false);
+ const [decryptedMessage, setDecryptedMessage] = useState(
+ '',
+ );
+
+ const loadWeb3Provider = async () => {
+ if (!window.ethereum) {
+ console.error('You need to connect to your wallet first');
+ return;
+ }
+
+ // Request account access
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
+
+ // Create public client for reading data
+ const publicClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: custom(window.ethereum),
+ });
+
+ // Get the accounts from the provider
+ const [account] = await window.ethereum.request({
+ method: 'eth_accounts'
+ });
+ // Create wallet client for signing the message
+ const walletClient = createWalletClient({
+ account,
+ chain: polygonAmoy,
+ transport: custom(window.ethereum),
+ });
+
+ const chainId = await publicClient.getChainId();
+ const amoyChainId = 80002;
+ if (chainId !== amoyChainId) {
+ // Switch to Polygon Amoy testnet
+ await window.ethereum.request({
+ method: 'wallet_switchEthereumChain',
+ params: [{ chainId: hexlify(amoyChainId) }],
+ });
+ }
+
+ setPublicClient(publicClient);
+ setWalletClient(walletClient);
+ };
+
+ useEffect(() => {
+ loadWeb3Provider();
+ }, []);
+
+ const { isInit, encryptDataToBytes, decryptDataFromBytes } = useTaco({
+ domain,
+ publicClient,
+ walletClient,
+ ritualId,
+ });
+
+ if (!isInit || !publicClient || !walletClient) {
+ return Loading...
;
+ }
+
+ const encryptMessage = async () => {
+ if (!walletClient) {
+ return;
+ }
+ setEncrypting(true);
+ try {
+ const hasPositiveBalance = new conditions.base.rpc.RpcCondition({
+ chain: 80002,
+ method: 'eth_getBalance',
+ parameters: [':userAddress', 'latest'],
+ returnValueTest: {
+ comparator: '>=',
+ value: 0,
+ },
+ });
+
+ console.log('Encrypting message...');
+ const encryptedBytes = await encryptDataToBytes(
+ message,
+ hasPositiveBalance,
+ );
+ if (encryptedBytes) {
+ setEncryptedText(toHexString(encryptedBytes));
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ setEncrypting(false);
+ };
+
+ const decryptMessage = async () => {
+ if (!encryptedText || !walletClient) {
+ return;
+ }
+ try {
+ setDecrypting(true);
+
+ console.log('Decrypting message...');
+ const decryptedMessage = await decryptDataFromBytes(
+ fromHexString(encryptedText),
+ );
+ if (decryptedMessage) {
+ setDecryptedMessage(fromBytes(decryptedMessage));
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ setDecrypting(false);
+ };
+
+ return (
+
+
+ Secret message:{' '}
+ setMessage(e.target.value)}
+ onClick={encryptMessage}
+ />{' '}
+ {' '}
+ {encrypting && 'Encrypting...'}
+
+
+ Encrypted message:{' '}
+ setEncryptedText(e.target.value)}
+ />{' '}
+ {' '}
+ {decrypting && 'Decrypting...'}
+
+ {decryptedMessage && Decrypted message: {decryptedMessage}
}
+
+ );
+}
+
+export default App;
diff --git a/examples/taco/react-viem/src/hooks/useTaco.ts b/examples/taco/react-viem/src/hooks/useTaco.ts
new file mode 100644
index 000000000..1ca25d786
--- /dev/null
+++ b/examples/taco/react-viem/src/hooks/useTaco.ts
@@ -0,0 +1,104 @@
+import {
+ conditions,
+ decrypt,
+ Domain,
+ encrypt,
+ initialize,
+ ThresholdMessageKit,
+} from '@nucypher/taco';
+import {
+ EIP4361AuthProvider,
+ USER_ADDRESS_PARAM_DEFAULT,
+} from '@nucypher/taco-auth';
+import { useCallback, useEffect, useState } from 'react';
+import { createPublicClient, http, PublicClient, WalletClient } from 'viem';
+import { polygonAmoy } from 'viem/chains';
+
+export default function useTaco({
+ ritualId,
+ domain,
+ publicClient,
+ walletClient,
+}: {
+ ritualId: number;
+ domain: Domain;
+ publicClient: PublicClient | undefined;
+ walletClient: WalletClient | undefined;
+}) {
+ const [isInit, setIsInit] = useState(false);
+
+ useEffect(() => {
+ initialize().then(() => setIsInit(true));
+ }, []);
+
+ const decryptDataFromBytes = useCallback(
+ async (encryptedBytes: Uint8Array) => {
+ if (!isInit || !publicClient || !walletClient) {
+ return;
+ }
+
+ const messageKit = ThresholdMessageKit.fromBytes(encryptedBytes);
+ const authProvider = new EIP4361AuthProvider(publicClient, walletClient);
+ const conditionContext =
+ conditions.context.ConditionContext.fromMessageKit(messageKit);
+ conditionContext.addAuthProvider(
+ USER_ADDRESS_PARAM_DEFAULT,
+ authProvider,
+ );
+ let message;
+ try {
+ message = await decrypt(
+ publicClient,
+ domain,
+ messageKit,
+ conditionContext,
+ );
+ } catch (error) {
+ if (
+ error instanceof Error &&
+ JSON.stringify(error).includes('missing trie node')
+ ) {
+ // Using MetaMask Provider may cause "missing trie node".
+ // Which typically occurs when attempting to query a blockchain state for a specific block that has been pruned from the node's storage.
+ // To avoid that, a custom public client is created.
+ const remotePublicClient = createPublicClient({
+ chain: polygonAmoy, // Using Polygon Amoy for testnet/devnet
+ transport: http('https://rpc-amoy.polygon.technology'),
+ });
+ message = await decrypt(
+ remotePublicClient,
+ domain,
+ messageKit,
+ conditionContext,
+ );
+ } else {
+ throw error;
+ }
+ }
+ return message;
+ },
+ [isInit, publicClient, walletClient, domain],
+ );
+
+ const encryptDataToBytes = useCallback(
+ async (message: string, condition: conditions.condition.Condition) => {
+ if (!isInit || !publicClient || !walletClient) return;
+ const messageKit = await encrypt(
+ publicClient,
+ domain,
+ message,
+ condition,
+ ritualId,
+ walletClient,
+ );
+ return messageKit.toBytes();
+ },
+ [isInit, publicClient, walletClient, domain, ritualId],
+ );
+
+ return {
+ isInit,
+ decryptDataFromBytes,
+ encryptDataToBytes,
+ };
+}
diff --git a/examples/taco/react-viem/src/index.css b/examples/taco/react-viem/src/index.css
new file mode 100644
index 000000000..8439a5936
--- /dev/null
+++ b/examples/taco/react-viem/src/index.css
@@ -0,0 +1,7 @@
+input {
+ font-size: 20px;
+}
+
+button {
+ font-size: 15px;
+}
diff --git a/examples/taco/react-viem/src/index.tsx b/examples/taco/react-viem/src/index.tsx
new file mode 100644
index 000000000..3f83eeaea
--- /dev/null
+++ b/examples/taco/react-viem/src/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+
+import App from './App';
+import './index.css';
+
+const root = ReactDOM.createRoot(
+ document.getElementById('root') as HTMLElement,
+);
+root.render(
+
+
+ ,
+);
diff --git a/examples/taco/react-viem/src/react-app-env.d.ts b/examples/taco/react-viem/src/react-app-env.d.ts
new file mode 100644
index 000000000..8f8826530
--- /dev/null
+++ b/examples/taco/react-viem/src/react-app-env.d.ts
@@ -0,0 +1,8 @@
+///
+import { ExternalProvider } from '@ethersproject/providers';
+
+declare global {
+ interface Window {
+ ethereum?: ExternalProvider;
+ }
+}
diff --git a/examples/taco/react-viem/tsconfig.build.json b/examples/taco/react-viem/tsconfig.build.json
new file mode 100644
index 000000000..7213342cb
--- /dev/null
+++ b/examples/taco/react-viem/tsconfig.build.json
@@ -0,0 +1,14 @@
+{
+ "extends": "./tsconfig.json",
+ "include": ["src"],
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src",
+ "noEmit": true
+ },
+ "references": [
+ {
+ "path": "../../../packages/taco/tsconfig.es.json"
+ },
+ ]
+}
diff --git a/examples/taco/react-viem/tsconfig.json b/examples/taco/react-viem/tsconfig.json
new file mode 100644
index 000000000..aca306ec8
--- /dev/null
+++ b/examples/taco/react-viem/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ },
+ "include": ["src"],
+}
diff --git a/package.json b/package.json
index e5c742053..7ec9ce79f 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"lint": "pnpm run --parallel --aggregate-output --reporter append-only lint",
"lint:fix": "pnpm --parallel --aggregate-output --reporter append-only lint:fix",
"type-check": "pnpm --parallel --aggregate-output --reporter append-only type-check",
- "build": "tsc --build --verbose ./tsconfig.prod.json",
+ "build": "pnpm -r prebuild && tsc --build --verbose ./tsconfig.prod.json && pnpm -r postbuild",
"watch": "tsc --build --verbose --watch ./tsconfig.prod.json",
"test": "pnpm build && vitest run",
"package:check": "pnpm run --parallel --aggregate-output --reporter append-only --filter './packages/**' package-check",
@@ -50,6 +50,7 @@
"sort-package-json": "^2.15.1",
"ts-node": "^10.9.2",
"ts-unused-exports": "^10.1.0",
+ "tsx": "^4.20.5",
"typedoc": "^0.25.13",
"typedoc-plugin-coverage": "^2.2.0",
"typedoc-plugin-missing-exports": "^2.3.0",
diff --git a/packages/pre/package.json b/packages/pre/package.json
index 6aa3ccf7f..c2aef11d6 100644
--- a/packages/pre/package.json
+++ b/packages/pre/package.json
@@ -14,6 +14,7 @@
"author": "Piotr Roslaniec ",
"exports": {
".": {
+ "types": "./dist/cjs/index.d.ts",
"import": "./dist/es/index.js",
"require": "./dist/cjs/index.js"
}
@@ -26,9 +27,9 @@
],
"scripts": {
"prebuild": "pnpm clean",
- "build": "pnpm build:module && pnpm build:cjs",
+ "build": "pnpm build:es && pnpm build:cjs",
"build:cjs": "tsc --build ./tsconfig.cjs.json --verbose",
- "build:module": "tsc --build ./tsconfig.es.json --verbose",
+ "build:es": "tsc --build ./tsconfig.es.json --verbose",
"clean": "rm -rf dist",
"exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts",
"lint": "eslint --ext .ts src test",
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 79d0d3ea1..fcaf0c736 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -15,6 +15,7 @@
"author": "NuCypher ",
"exports": {
".": {
+ "types": "./dist/cjs/index.d.ts",
"import": "./dist/es/index.js",
"require": "./dist/cjs/index.js"
}
@@ -26,11 +27,14 @@
"dist"
],
"scripts": {
- "prebuild": "pnpm typechain && pnpm clean",
- "build": "pnpm build:module && pnpm build:cjs",
+ "prebuild": "pnpm clean && pnpm typechain && pnpm ensure-es-compatible-imports",
+ "build": "pnpm build:es && pnpm build:cjs",
+ "postbuild": "pnpm ensure-es-package-type",
"build:cjs": "tsc --build ./tsconfig.cjs.json --verbose",
- "build:module": "tsc --build ./tsconfig.es.json --verbose",
+ "build:es": "tsc --build ./tsconfig.es.json --verbose",
"clean": "rm -rf dist",
+ "ensure-es-compatible-imports": "pnpm tsx ../../scripts/ensure-es-compatible-imports.ts",
+ "ensure-es-package-type": "pnpm tsx ../../scripts/ensure-es-package-type.ts",
"exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts",
"lint": "eslint --ext .ts src",
"lint:fix": "pnpm lint --fix",
@@ -58,7 +62,16 @@
"@types/tmp": "^0.2.6",
"cz-conventional-changelog": "^3.3.0",
"standard-version": "^9.5.0",
- "typechain": "^8.3.2"
+ "typechain": "^8.3.2",
+ "viem": "^2.0.0"
+ },
+ "peerDependencies": {
+ "viem": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "viem": {
+ "optional": true
+ }
},
"engines": {
"node": ">=18",
diff --git a/packages/shared/src/adapters.ts b/packages/shared/src/adapters.ts
new file mode 100644
index 000000000..e6b1a8d54
--- /dev/null
+++ b/packages/shared/src/adapters.ts
@@ -0,0 +1,45 @@
+import { ethers } from 'ethers';
+
+import type { TacoSigner } from './taco-signer.js';
+import { ProviderLike, SignerLike } from './types.js';
+import { viemClientToProvider } from './viem/ethers-adapter.js';
+import { ViemSignerAdapter } from './viem/signer-adapter.js';
+import { isViemClient, isViemSignerAccount } from './viem/type-guards.js';
+
+/**
+ * Convert ethers Signer or viem SignerAccount (LocalAccount or WalletClient) to TacoSigner.
+ *
+ * This is the main entry point for creating signers for internal TACo use.
+ * Unlike toEthersProvider which creates actual ethers objects,
+ * this creates minimal adapters implementing only what TACo needs.
+ *
+ * @param signerLike - Either an ethers Signer or a viem SignerAccount (LocalAccount or WalletClient)
+ * @returns A TacoSigner interface implementation
+ */
+export function toTacoSigner(signerLike: SignerLike): TacoSigner {
+ if (isViemSignerAccount(signerLike)) {
+ return new ViemSignerAdapter(signerLike);
+ } else {
+ return signerLike;
+ }
+}
+
+/**
+ * Convert viem client to ethers provider or return existing ethers provider.
+ *
+ * This is the main entry point for converting providers.
+ * It handles both viem clients (converting them to ethers providers)
+ * and existing ethers providers (returning them unchanged).
+ *
+ * @param providerLike - Either a viem PublicClient or an ethers.providers.Provider
+ * @returns An actual ethers.providers.Provider instance
+ */
+export function toEthersProvider(
+ providerLike: ProviderLike,
+): ethers.providers.Provider {
+ if (isViemClient(providerLike)) {
+ return viemClientToProvider(providerLike);
+ } else {
+ return providerLike;
+ }
+}
diff --git a/packages/shared/src/contracts/agents/coordinator.ts b/packages/shared/src/contracts/agents/coordinator.ts
index ab50500c3..2f2391b0d 100644
--- a/packages/shared/src/contracts/agents/coordinator.ts
+++ b/packages/shared/src/contracts/agents/coordinator.ts
@@ -6,12 +6,12 @@ import {
} from '@nucypher/nucypher-core';
import { BigNumberish, ethers } from 'ethers';
-import { Domain } from '../../porter';
-import { ChecksumAddress } from '../../types';
-import { fromHexString } from '../../utils';
-import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const';
-import { Coordinator__factory } from '../ethers-typechain';
-import { BLS12381, Coordinator } from '../ethers-typechain/Coordinator';
+import { Domain } from '../../porter.js';
+import { ChecksumAddress } from '../../types.js';
+import { fromHexString } from '../../utils.js';
+import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const.js';
+import { BLS12381, Coordinator } from '../ethers-typechain/Coordinator.js';
+import { Coordinator__factory } from '../ethers-typechain/index.js';
export interface CoordinatorRitual {
initiator: string;
diff --git a/packages/shared/src/contracts/agents/global-allow-list.ts b/packages/shared/src/contracts/agents/global-allow-list.ts
index fc09d5946..2cc3407c2 100644
--- a/packages/shared/src/contracts/agents/global-allow-list.ts
+++ b/packages/shared/src/contracts/agents/global-allow-list.ts
@@ -1,10 +1,10 @@
import { getContract } from '@nucypher/nucypher-contracts';
import { ethers } from 'ethers';
-import { Domain } from '../../porter';
-import { ChecksumAddress } from '../../types';
-import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const';
-import { GlobalAllowList, GlobalAllowList__factory } from '../ethers-typechain';
+import { Domain } from '../../porter.js';
+import { ChecksumAddress } from '../../types.js';
+import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const.js';
+import { GlobalAllowList, GlobalAllowList__factory } from '../ethers-typechain/index.js';
export class GlobalAllowListAgent {
public static async registerEncrypters(
diff --git a/packages/shared/src/contracts/agents/index.ts b/packages/shared/src/contracts/agents/index.ts
index 61e29c0e6..2dea7b365 100644
--- a/packages/shared/src/contracts/agents/index.ts
+++ b/packages/shared/src/contracts/agents/index.ts
@@ -1,3 +1,3 @@
-export * from './coordinator';
-export * from './global-allow-list';
-export * from './subscription-manager';
+export * from './coordinator.js';
+export * from './global-allow-list.js';
+export * from './subscription-manager.js';
diff --git a/packages/shared/src/contracts/agents/subscription-manager.ts b/packages/shared/src/contracts/agents/subscription-manager.ts
index 20f787300..2820f1c4a 100644
--- a/packages/shared/src/contracts/agents/subscription-manager.ts
+++ b/packages/shared/src/contracts/agents/subscription-manager.ts
@@ -6,13 +6,13 @@ import {
utils as ethersUtils,
} from 'ethers';
-import { Domain } from '../../porter';
-import { ChecksumAddress } from '../../types';
-import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const';
+import { Domain } from '../../porter.js';
+import { ChecksumAddress } from '../../types.js';
+import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const.js';
import {
SubscriptionManager,
SubscriptionManager__factory,
-} from '../ethers-typechain';
+} from '../ethers-typechain/index.js';
export class PreSubscriptionManagerAgent {
public static async createPolicy(
diff --git a/packages/shared/src/contracts/ethers-typechain/Coordinator.ts b/packages/shared/src/contracts/ethers-typechain/Coordinator.ts
index 21619add1..5f4d4dc82 100644
--- a/packages/shared/src/contracts/ethers-typechain/Coordinator.ts
+++ b/packages/shared/src/contracts/ethers-typechain/Coordinator.ts
@@ -24,7 +24,7 @@ import type {
TypedEvent,
TypedEventFilter,
TypedListener,
-} from './common';
+} from './common.js';
export declare namespace BLS12381 {
export type G2PointStruct = {
diff --git a/packages/shared/src/contracts/ethers-typechain/GlobalAllowList.ts b/packages/shared/src/contracts/ethers-typechain/GlobalAllowList.ts
index 3d03a022c..11685e19c 100644
--- a/packages/shared/src/contracts/ethers-typechain/GlobalAllowList.ts
+++ b/packages/shared/src/contracts/ethers-typechain/GlobalAllowList.ts
@@ -24,7 +24,7 @@ import type {
TypedEvent,
TypedEventFilter,
TypedListener,
-} from './common';
+} from './common.js';
export interface GlobalAllowListInterface extends utils.Interface {
functions: {
diff --git a/packages/shared/src/contracts/ethers-typechain/SubscriptionManager.ts b/packages/shared/src/contracts/ethers-typechain/SubscriptionManager.ts
index b91b79a13..51fb58f02 100644
--- a/packages/shared/src/contracts/ethers-typechain/SubscriptionManager.ts
+++ b/packages/shared/src/contracts/ethers-typechain/SubscriptionManager.ts
@@ -25,7 +25,7 @@ import type {
TypedEvent,
TypedEventFilter,
TypedListener,
-} from './common';
+} from './common.js';
export declare namespace SubscriptionManager {
export type PolicyStruct = {
diff --git a/packages/shared/src/contracts/ethers-typechain/factories/Coordinator__factory.ts b/packages/shared/src/contracts/ethers-typechain/factories/Coordinator__factory.ts
index e3800e887..1fd17973a 100644
--- a/packages/shared/src/contracts/ethers-typechain/factories/Coordinator__factory.ts
+++ b/packages/shared/src/contracts/ethers-typechain/factories/Coordinator__factory.ts
@@ -4,7 +4,7 @@
import type { Provider } from '@ethersproject/providers';
import { Contract, Signer, utils } from 'ethers';
-import type { Coordinator, CoordinatorInterface } from '../Coordinator';
+import type { Coordinator, CoordinatorInterface } from '../Coordinator.js';
const _abi = [
{
diff --git a/packages/shared/src/contracts/ethers-typechain/factories/GlobalAllowList__factory.ts b/packages/shared/src/contracts/ethers-typechain/factories/GlobalAllowList__factory.ts
index 59e19af4f..520fcf2e4 100644
--- a/packages/shared/src/contracts/ethers-typechain/factories/GlobalAllowList__factory.ts
+++ b/packages/shared/src/contracts/ethers-typechain/factories/GlobalAllowList__factory.ts
@@ -7,7 +7,7 @@ import { Contract, Signer, utils } from 'ethers';
import type {
GlobalAllowList,
GlobalAllowListInterface,
-} from '../GlobalAllowList';
+} from '../GlobalAllowList.js';
const _abi = [
{
diff --git a/packages/shared/src/contracts/ethers-typechain/factories/SubscriptionManager__factory.ts b/packages/shared/src/contracts/ethers-typechain/factories/SubscriptionManager__factory.ts
index 74413c5fc..c161ac932 100644
--- a/packages/shared/src/contracts/ethers-typechain/factories/SubscriptionManager__factory.ts
+++ b/packages/shared/src/contracts/ethers-typechain/factories/SubscriptionManager__factory.ts
@@ -7,7 +7,7 @@ import { Contract, Signer, utils } from 'ethers';
import type {
SubscriptionManager,
SubscriptionManagerInterface,
-} from '../SubscriptionManager';
+} from '../SubscriptionManager.js';
const _abi = [
{
diff --git a/packages/shared/src/contracts/ethers-typechain/factories/index.ts b/packages/shared/src/contracts/ethers-typechain/factories/index.ts
index 8187c7c16..a6d99ea71 100644
--- a/packages/shared/src/contracts/ethers-typechain/factories/index.ts
+++ b/packages/shared/src/contracts/ethers-typechain/factories/index.ts
@@ -1,6 +1,6 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
-export { Coordinator__factory } from './Coordinator__factory';
-export { GlobalAllowList__factory } from './GlobalAllowList__factory';
-export { SubscriptionManager__factory } from './SubscriptionManager__factory';
+export { Coordinator__factory } from './Coordinator__factory.js';
+export { GlobalAllowList__factory } from './GlobalAllowList__factory.js';
+export { SubscriptionManager__factory } from './SubscriptionManager__factory.js';
diff --git a/packages/shared/src/contracts/ethers-typechain/index.ts b/packages/shared/src/contracts/ethers-typechain/index.ts
index e0b86d8c9..4d32ba0f6 100644
--- a/packages/shared/src/contracts/ethers-typechain/index.ts
+++ b/packages/shared/src/contracts/ethers-typechain/index.ts
@@ -1,10 +1,10 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
-export type { Coordinator } from './Coordinator';
-export * as factories from './factories';
-export { Coordinator__factory } from './factories/Coordinator__factory';
-export { GlobalAllowList__factory } from './factories/GlobalAllowList__factory';
-export { SubscriptionManager__factory } from './factories/SubscriptionManager__factory';
-export type { GlobalAllowList } from './GlobalAllowList';
-export type { SubscriptionManager } from './SubscriptionManager';
+export type { Coordinator } from './Coordinator.js';
+export * as factories from './factories/index.js';
+export { Coordinator__factory } from './factories/Coordinator__factory.js';
+export { GlobalAllowList__factory } from './factories/GlobalAllowList__factory.js';
+export { SubscriptionManager__factory } from './factories/SubscriptionManager__factory.js';
+export type { GlobalAllowList } from './GlobalAllowList.js';
+export type { SubscriptionManager } from './SubscriptionManager.js';
diff --git a/packages/shared/src/contracts/index.ts b/packages/shared/src/contracts/index.ts
index 6fb2308f0..9f2a45d20 100644
--- a/packages/shared/src/contracts/index.ts
+++ b/packages/shared/src/contracts/index.ts
@@ -1,2 +1,2 @@
-export * from './agents';
-export * from './ethers-typechain';
+export * from './agents/index.js';
+export * from './ethers-typechain/index.js';
diff --git a/packages/shared/src/domain.ts b/packages/shared/src/domain.ts
new file mode 100644
index 000000000..8789ff3f7
--- /dev/null
+++ b/packages/shared/src/domain.ts
@@ -0,0 +1,48 @@
+export type DomainName = 'lynx' | 'tapir' | 'mainnet';
+
+/**
+ * TACo domain configuration with essential network data
+ *
+ * Contains domain names, chain IDs, and core infrastructure information
+ * needed for TACo operations across different networks.
+ *
+ * @example
+ * ```typescript
+ * // Get domain information
+ * const lynxInfo = DOMAINS.DEVNET;
+ * console.log(lynxInfo.domain); // 'lynx'
+ * console.log(lynxInfo.chainId); // 80002
+ * ```
+ */
+export const DOMAINS = {
+ // lynx - DEVNET: Bleeding-edge developer network
+ DEVNET: {
+ domain: 'lynx',
+ chainId: 80002,
+ },
+ // tapir - TESTNET: Stable testnet for current TACo release
+ TESTNET: {
+ domain: 'tapir',
+ chainId: 80002,
+ },
+ // mainnet - MAINNET: Production network
+ MAINNET: {
+ domain: 'mainnet',
+ chainId: 137,
+ },
+} as const;
+
+/**
+ * TACo Domain Name Constants
+ *
+ * Convenient constants for referencing TACo domain names in a type-safe manner.
+ * Use these constants instead of hardcoded strings for better maintainability.
+ */
+export const DOMAIN_NAMES = {
+ /** DEVNET domain ('lynx') - Bleeding-edge developer network */
+ DEVNET: 'lynx',
+ /** TESTNET domain ('tapir') - Stable testnet for current TACo release */
+ TESTNET: 'tapir',
+ /** MAINNET domain ('mainnet') - Production network */
+ MAINNET: 'mainnet',
+} as const;
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 526d9cc5b..8f0b026c4 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,9 +1,13 @@
-export * from './contracts';
-export * from './porter';
-export * from './schemas';
-export type * from './types';
-export * from './utils';
-export * from './web3';
+export * from './adapters.js';
+export * from './contracts/index.js';
+export * from './domain.js';
+export * from './porter.js';
+export * from './schemas.js';
+export * from './taco-signer.js';
+export type * from './types.js';
+export * from './utils.js';
+export * from './viem/index.js';
+export * from './web3.js';
// Re-exports
export {
diff --git a/packages/shared/src/porter.ts b/packages/shared/src/porter.ts
index 0a13050d3..1cfce4aad 100644
--- a/packages/shared/src/porter.ts
+++ b/packages/shared/src/porter.ts
@@ -13,8 +13,8 @@ import axios, {
} from 'axios';
import qs from 'qs';
-import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from './types';
-import { fromBase64, fromHexString, toBase64, toHexString } from './utils';
+import { Base64EncodedBytes, ChecksumAddress, HexEncodedBytes } from './types.js';
+import { fromBase64, fromHexString, toBase64, toHexString } from './utils.js';
const defaultPorterUri: Record = {
mainnet: 'https://porter.nucypher.io',
diff --git a/packages/shared/src/taco-signer.ts b/packages/shared/src/taco-signer.ts
new file mode 100644
index 000000000..24b0fe6f3
--- /dev/null
+++ b/packages/shared/src/taco-signer.ts
@@ -0,0 +1,20 @@
+/**
+ * Minimal TACo signer interface.
+ *
+ * This interface defines only the essential methods that TACo operations require:
+ * - `getAddress()`: Get the signer's address
+ * - `signMessage()`: Sign a message (string or bytes)
+ *
+ * Future signer adapters can implement this same minimal interface
+ */
+export interface TacoSigner {
+ /**
+ * Get the address of this signer
+ */
+ getAddress(): Promise;
+
+ /**
+ * Sign a message
+ */
+ signMessage(message: string | Uint8Array): Promise;
+}
diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts
index 02603b3c2..8100d70b2 100644
--- a/packages/shared/src/types.ts
+++ b/packages/shared/src/types.ts
@@ -1,3 +1,16 @@
+import { ethers } from 'ethers';
+
+import { PublicClient, SignerAccount } from './viem/types.js';
+
export type ChecksumAddress = `0x${string}`;
export type HexEncodedBytes = string;
export type Base64EncodedBytes = string;
+
+export type ProviderLike = ethers.providers.Provider | PublicClient;
+
+/**
+ * Signer-like union for TACo operations.
+ *
+ * Accepts either ethers Signer or viem SignerAccount (LocalAccount or WalletClient)
+ */
+export type SignerLike = ethers.Signer | SignerAccount;
diff --git a/packages/shared/src/viem/ethers-adapter.ts b/packages/shared/src/viem/ethers-adapter.ts
new file mode 100644
index 000000000..25b22910d
--- /dev/null
+++ b/packages/shared/src/viem/ethers-adapter.ts
@@ -0,0 +1,128 @@
+import { Networkish } from '@ethersproject/providers';
+import { ethers } from 'ethers';
+
+import type { Chain, PublicClient } from './types.js';
+
+/**
+ * Create ethers network object from viem chain
+ */
+function createNetworkish(chain: Chain): ethers.providers.Networkish {
+ const networkish: Networkish = {
+ chainId: chain.id,
+ name: chain.name,
+ };
+
+ // Add ENS registry address if available
+ if (chain.contracts?.ensRegistry?.address) {
+ networkish.ensAddress = chain.contracts.ensRegistry.address;
+ } else {
+ console.warn(
+ `No ENS registry found on chain ${chain.name} (chainId=${chain.id}).\n` +
+ `With the current configuration, ENS resolution will not work on the created ethers Provider.\n` +
+ `To fix this either:\n` +
+ ` - Set chain.contracts.ensRegistry.address to the correct ENS registry address, or\n` +
+ ` - Or omit the \`chain\` data to allow automatic ENS registry detection from the provider.`,
+ );
+ }
+
+ return networkish;
+}
+
+/**
+ * Viem to Ethers.js Adapter for Providers
+ *
+ * This adapter converts viem PublicClients to actual ethers.js Provider instances.
+ * Unlike the signer adapter which implements a minimal internal interface,
+ * this creates real ethers.providers.Provider objects that can be passed to
+ * third-party libraries expecting ethers.js providers.
+ *
+ * Key differences from signer adapter:
+ * - Creates ethers.providers.Provider instances
+ * - Currently handles only transport type: http
+ * - Required for external library compatibility
+ */
+/**
+ * Static method to directly convert client to provider (convenience method)
+ */
+export function viemClientToProvider(
+ client: PublicClient,
+): ethers.providers.Provider {
+ const { chain, transport }: PublicClient = client;
+
+ let networkish: ethers.providers.Networkish | undefined;
+ if (chain) {
+ networkish = createNetworkish(chain);
+ }
+
+ // Note: We read minimal, commonly-present properties from transport.
+ // viem's transport internals are not a public API and may change.
+ // Also the we are taking only the first transport available.
+ // This adapter focuses on best-effort extraction of an RPC URL or EIP-1193 provider.
+
+ // fallback transport (multiple RPC endpoints)
+ if (transport?.type === 'fallback') {
+ throw new Error(
+ 'Fallback transport not supported. Please use a single HTTP transport instead.',
+ );
+ // TODO: implement with a code like the following:
+ // Note: The following takes only the first url of the transports urls.
+ // const items = transport.transports as ReturnType[];
+ // const providers = items.map((t, i) => {
+ // const url = t?.value?.url;
+ // if (typeof url !== 'string' || url.length === 0) {
+ // throw new Error(
+ // `Fallback transport missing URL at index ${i} (chainId=${chain?.id ?? 'unknown'} name=${chain?.name ?? 'unknown'})`,
+ // );
+ // }
+ // return new ethers.providers.JsonRpcProvider(url, networkish);
+ // });
+
+ // return new ethers.providers.FallbackProvider(providers);
+ }
+
+ // websocket transport
+ if (transport?.type === 'webSocket') {
+ throw new Error('WebSocket transport not supported');
+ // TODO: implement with a code like the following:
+ // const url = transport?.url as string | undefined;
+ // if (!url) {
+ // throw new Error(
+ // `Transport must have a URL (type=webSocket, chainId=${chain?.id ?? 'unknown'} name=${chain?.name ?? 'unknown'})`,
+ // );
+ // }
+ // return new ethers.providers.WebSocketProvider(url, networkish);
+ }
+
+ // TODO: this needs to be a lot better tested
+ // custom (EIP-1193) transport
+ if (transport?.type === 'custom') {
+ const value = transport?.value;
+ const provider = value?.provider ?? value ?? transport;
+
+ // Check if it's an EIP-1193 provider (e.g., MetaMask, WalletConnect)
+ if (provider && typeof provider.request === 'function') {
+ return new ethers.providers.Web3Provider(provider, networkish);
+ }
+
+ // If custom but no EIP-1193 provider found, try URL if present
+
+ throw new Error('Custom non-EIP-1193 provider transport not supported');
+ // TODO: implement with a code like the following:
+ // const url = value?.url ?? transport?.url;
+ // if (typeof url === 'string' && url.length > 0) {
+ // return new ethers.providers.JsonRpcProvider(url, networkish);
+ // }
+ // throw new Error(
+ // `Custom transport missing EIP-1193 provider or URL (chainId=${chain?.id ?? 'unknown'} name=${chain?.name ?? 'unknown'})`,
+ // );
+ }
+
+ // Default: assume HTTP-like with a URL
+ const url = transport?.url as string | undefined;
+ if (!url) {
+ throw new Error(
+ `Transport must have a URL (type=${transport?.type ?? 'unknown'}, chainId=${chain?.id ?? 'unknown'}, name=${chain?.name ?? 'unknown'})`,
+ );
+ }
+ return new ethers.providers.JsonRpcProvider(url, networkish);
+}
diff --git a/packages/shared/src/viem/index.ts b/packages/shared/src/viem/index.ts
new file mode 100644
index 000000000..45c45f3ed
--- /dev/null
+++ b/packages/shared/src/viem/index.ts
@@ -0,0 +1,2 @@
+export * from './type-guards.js';
+export type * from './types.js';
diff --git a/packages/shared/src/viem/signer-adapter.ts b/packages/shared/src/viem/signer-adapter.ts
new file mode 100644
index 000000000..00aac3444
--- /dev/null
+++ b/packages/shared/src/viem/signer-adapter.ts
@@ -0,0 +1,61 @@
+import { ethers } from 'ethers';
+
+import { type TacoSigner } from '../taco-signer.js';
+
+import { type Address, type SignerAccount } from './types.js';
+
+/**
+ * Viem Signer Adapter
+ *
+ * This adapter implements the minimal TacoSigner interface for internal library use.
+ */
+export class ViemSignerAdapter implements TacoSigner {
+ protected viemAccount: SignerAccount;
+
+ constructor(viemAccount: SignerAccount) {
+ this.viemAccount = viemAccount;
+ }
+
+ async getAddress(): Promise {
+ let address: Address | undefined;
+ if ('address' in this.viemAccount) {
+ // viemAccount is a LocalAccount
+ address = this.viemAccount.address;
+ } else if (
+ 'account' in this.viemAccount &&
+ this.viemAccount.account &&
+ 'address' in this.viemAccount.account
+ ) {
+ // viemAccount is a WalletClient
+ address = this.viemAccount.account.address;
+ }
+ if (address) {
+ // Get the checksummed address to avoid getting
+ // "invalid EIP-55 address - 0x31663c14545df87044d2c5407ad0c2696b6d1402"
+ // that might be thrown at package siwe-parser while perform decryption
+ return ethers.utils.getAddress(address) as Address;
+ }
+ throw new Error(
+ 'Unable to retrieve address from viem account. Expected a LocalAccount with "address" property or WalletClient with "account.address" property.',
+ );
+ }
+
+ async signMessage(message: string | Uint8Array): Promise {
+ if (!this.viemAccount.signMessage) {
+ throw new Error(
+ 'Account does not support message signing. Expected a LocalAccount or a WalletClient with signing capability.',
+ );
+ }
+ if (typeof message === 'string') {
+ return await this.viemAccount.signMessage({
+ account: await this.getAddress(),
+ message,
+ });
+ } else {
+ return await this.viemAccount.signMessage({
+ account: await this.getAddress(),
+ message: { raw: message },
+ });
+ }
+ }
+}
diff --git a/packages/shared/src/viem/type-guards.ts b/packages/shared/src/viem/type-guards.ts
new file mode 100644
index 000000000..796258e8f
--- /dev/null
+++ b/packages/shared/src/viem/type-guards.ts
@@ -0,0 +1,59 @@
+import { ethers } from 'ethers';
+
+import { ProviderLike, SignerLike } from '../types.js';
+
+import { PublicClient, SignerAccount } from './types.js';
+
+/**
+ * Type guard to determine if the provider-like is a viem PublicClient
+ *
+ * Checks for:
+ * - Ensures it's not an ethers provider instance
+ * - Presence of viem-specific properties (chain)
+ * - Presence of viem-specific methods (getChainId)
+ */
+export function isViemClient(
+ providerLike: ProviderLike,
+): providerLike is PublicClient {
+ // Early return if it's an ethers provider
+ if (providerLike instanceof ethers.providers.BaseProvider) {
+ return false;
+ }
+
+ // Check for viem-specific properties and methods
+ const hasChainProperty = 'chain' in providerLike;
+ const hasGetChainId =
+ typeof (providerLike as { getChainId: () => Promise })
+ .getChainId === 'function';
+
+ return hasChainProperty || hasGetChainId;
+}
+
+/**
+ * Type guard to determine if the signer is a viem account that can sign: LocalAccount or WalletClient
+ * Note: might need modification when supporting viem SmartAccount
+ * Checks for:
+ * - Ensures it's not an ethers Signer instance
+ * - Absence of ethers-specific properties (provider)
+ * - Presence of viem Local Account properties (address as string) or viem Wallet Client properties (account.address as string)
+ */
+export function isViemSignerAccount(
+ signer: SignerLike,
+): signer is SignerAccount {
+ if (signer instanceof ethers.Signer || 'provider' in signer) {
+ return false;
+ }
+
+ // Check for viem SignerAccount properties
+ const hasLocalAccountProperties =
+ // Local Account:
+ 'address' in signer &&
+ typeof (signer as { address: string }).address === 'string';
+ const hasWalletClientProperties =
+ // Wallet Client:
+ 'account' in signer &&
+ typeof (signer as { account: { address: string } }).account.address ===
+ 'string';
+
+ return hasLocalAccountProperties || hasWalletClientProperties;
+}
diff --git a/packages/shared/src/viem/types.ts b/packages/shared/src/viem/types.ts
new file mode 100644
index 000000000..5a378b62e
--- /dev/null
+++ b/packages/shared/src/viem/types.ts
@@ -0,0 +1,79 @@
+/**
+ * Shared viem utilities for TACo packages
+ *
+ * Features:
+ * - Optional viem dependency handling with helpful error messages
+ * - Dynamic import pattern that's webpack-compatible
+ * - Centralized type definitions for viem objects
+ * - Runtime availability checking with caching
+ * - Common wrapper implementations for providers and signers
+ *
+ * Usage:
+ * ```typescript
+ * import { type PublicClient } from '@nucypher/shared';
+ *
+ * // Use viem clients directly with TACo adapters
+ * const tacoProvider = await toEthersProvider(publicClient);
+ * ```
+ */
+
+// Type helper: Use the real type from 'viem' if available, otherwise fallback to 'any'.
+// This is because viem is an optional dependency, so the viem types may or may not be present.
+// This pattern preserves type safety for consumers who have 'viem' installed, but does not break for others.
+// See: https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1367016530
+// Dynamic imports resolve to 'unknown' when module is not available, no compile-time errors occur
+type _Address = import('viem').Address;
+type _ViemPublicClient = import('viem').PublicClient;
+type _LocalAccount = import('viem').LocalAccount;
+type _ViemChain = import('viem').Chain;
+type _ViemTransport = import('viem').Transport;
+type _WalletClient = import('viem').WalletClient;
+
+/**
+ * Viem Address type (`0x${string}`)
+ */
+// Fallback to hex string
+export type Address = [unknown] extends [_Address] ? `0x${string}` : _Address;
+
+/**
+ * Viem PublicClient type for read operations
+ * @see https://viem.sh/docs/clients/public
+ */
+export type PublicClient = [unknown] extends [_ViemPublicClient]
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ any
+ : _ViemPublicClient;
+
+export type LocalAccount = [unknown] extends [_LocalAccount]
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ any
+ : _LocalAccount;
+
+export type WalletClient = [unknown] extends [_WalletClient]
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ any
+ : _WalletClient;
+
+/**
+ * Viem signer account type for signing operations (LocalAccount or WalletClient)
+ * Note: SmartAccount is not supported yet
+ * @see https://viem.sh/docs/accounts/local
+ * @see https://viem.sh/docs/clients/wallet
+ */
+export type SignerAccount = LocalAccount | WalletClient;
+
+/**
+ * Viem Chain type for network metadata
+ * @see https://viem.sh/docs/glossary/types#chain
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type Chain = [unknown] extends [_ViemChain] ? any : _ViemChain;
+
+/**
+ * Viem Transport type for network metadata
+ * @see https://viem.sh/docs/glossary/types#transport
+ */
+export type Transport = [unknown] extends [_ViemTransport]
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ any
+ : _ViemTransport;
diff --git a/packages/shared/src/web3.ts b/packages/shared/src/web3.ts
index 97655c765..0179262bf 100644
--- a/packages/shared/src/web3.ts
+++ b/packages/shared/src/web3.ts
@@ -1,4 +1,4 @@
-import { fromHexString } from './utils';
+import { fromHexString } from './utils.js';
export enum ChainId {
POLYGON = 137,
diff --git a/packages/shared/test/viem-ethers-adapter.test.ts b/packages/shared/test/viem-ethers-adapter.test.ts
new file mode 100644
index 000000000..776432a1d
--- /dev/null
+++ b/packages/shared/test/viem-ethers-adapter.test.ts
@@ -0,0 +1,308 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// @ts-nocheck
+import { ethers } from 'ethers';
+import { privateKeyToAccount } from 'viem/accounts';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { createPublicClient, fallback, http, webSocket } from 'viem';
+import { fromHexString } from '../src';
+import { toEthersProvider, toTacoSigner } from '../src/adapters';
+import { viemClientToProvider } from '../src/viem/ethers-adapter';
+import { ViemSignerAdapter } from '../src/viem/signer-adapter';
+import { isViemClient, isViemSignerAccount } from '../src/viem/type-guards';
+
+describe('viem ethers adapter', () => {
+ const PRIVATE_KEY =
+ '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; // 32-byte hex
+
+ describe('viemClientToProvider', () => {
+ let viemClientConfig: any;
+
+ beforeEach(() => {
+ viemClientConfig = {
+ chain: {
+ id: 80002,
+ name: 'Polygon Amoy',
+ contracts: {
+ ensRegistry: { address: '0x123' },
+ },
+ },
+ transport: http('https://rpc.ankr.com/polygon_amoy'),
+ };
+ });
+
+ it('should convert to ethers provider with single transport', () => {
+ const viemClient = createPublicClient(viemClientConfig);
+ const provider = new viemClientToProvider(viemClient);
+ expect(provider).toBeInstanceOf(ethers.providers.JsonRpcProvider);
+ expect(provider.connection.url).toBe('https://rpc.ankr.com/polygon_amoy');
+ expect(provider.network.chainId).toBe(80002);
+ expect(provider.network.name).toBe('Polygon Amoy');
+ expect(provider.network.ensAddress).toBe('0x123');
+ });
+
+ it('should throw error when converting to ethers provider with fallback transport', () => {
+ const fallbackClientConfig = {
+ ...viemClientConfig,
+ transport: fallback([
+ http('https://rpc1.example.com'),
+ http('https://rpc2.example.com'),
+ ]),
+ };
+ const fallbackClient = createPublicClient(fallbackClientConfig);
+ expect(() => viemClientToProvider(fallbackClient)).toThrow(
+ 'Fallback transport not supported',
+ );
+ });
+
+ it('should throw error when converting to ethers provider with webSocket transport', () => {
+ const webSocketClientConfig = {
+ ...viemClientConfig,
+ transport: webSocket('wss://example.com'),
+ };
+ const webSocketClient = createPublicClient(webSocketClientConfig);
+ expect(() => viemClientToProvider(webSocketClient)).toThrow(
+ 'WebSocket transport not supported',
+ );
+ });
+
+ // TODO: this needs to be better tested i.e. tested with an actual custom transport from viem that uses EIP1193
+ it('should convert to ethers provider with custom transport (browser injected)', () => {
+ const mockEIP1193Provider = {
+ request: vi.fn(),
+ };
+
+ const mockCustomClient = {
+ ...viemClientConfig,
+ transport: {
+ type: 'custom',
+ value: {
+ provider: mockEIP1193Provider,
+ },
+ },
+ };
+
+ const provider = viemClientToProvider(mockCustomClient);
+ expect(provider).toBeDefined();
+ expect(provider.constructor.name).toBe('Web3Provider');
+ });
+
+ // TODO: this needs to be better tested i.e. tested with an actual custom transport from viem
+ it('should throw error for custom transport without provider or URL', () => {
+ const mockCustomClient = {
+ ...viemClientConfig,
+ transport: {
+ type: 'custom',
+ value: {},
+ },
+ };
+
+ expect(() => viemClientToProvider(mockCustomClient)).toThrow(
+ 'Custom non-EIP-1193 provider transport not supported',
+ );
+ });
+
+ it('should handle missing chain', () => {
+ const clientWithoutChainConfig = {
+ ...viemClientConfig,
+ chain: undefined,
+ };
+
+ const clientWithoutChain = createPublicClient(clientWithoutChainConfig);
+ expect(() => viemClientToProvider(clientWithoutChain)).not.toThrow();
+ });
+
+ it('should handle missing transport URL', () => {
+ const clientWithoutUrlConfig = {
+ ...viemClientConfig,
+ transport: undefined, // empty string URL
+ };
+ expect(() => viemClientToProvider(clientWithoutUrlConfig)).toThrow(
+ 'Transport must have a URL',
+ );
+ });
+ });
+
+ describe('ViemSignerAdapter', () => {
+ const viemAccount = privateKeyToAccount(PRIVATE_KEY);
+ const ethersSigner = new ethers.Wallet(PRIVATE_KEY);
+
+ it('should create signer without provider', () => {
+ const viemAdaptedSigner = new ViemSignerAdapter(viemAccount);
+ expect(viemAdaptedSigner).toBeInstanceOf(ViemSignerAdapter);
+ });
+
+ it('should get address from viem account', async () => {
+ const viemAdaptedSigner = new ViemSignerAdapter(viemAccount);
+
+ const address = await viemAdaptedSigner.getAddress();
+ expect(address).toBe(ethersSigner.address);
+ });
+
+ it('should sign string message', async () => {
+ const message = 'test message';
+ const viemAdaptedSigner = new ViemSignerAdapter(viemAccount);
+ const viemSignature = await viemAdaptedSigner.signMessage(message);
+
+ const ethersSignature = await ethersSigner.signMessage(message);
+ expect(viemSignature).toBe(ethersSignature);
+ });
+
+ it('should sign Uint8Array message', async () => {
+ const viemAdaptedSigner = new ViemSignerAdapter(viemAccount);
+ const messageBytes = fromHexString('0xdeadbeef');
+
+ const viemSignature = await viemAdaptedSigner.signMessage(messageBytes);
+
+ const ethersSignature = await ethersSigner.signMessage(messageBytes);
+ expect(viemSignature).toBe(ethersSignature);
+ });
+
+ it('should throw error if account does not support signing', async () => {
+ const accountWithoutSigning = {
+ address: '0x742d35Cc6632C0532c718F63b1a8D7d8a7fAd3b2',
+ // no signMessage method
+ };
+
+ const signer = new ViemSignerAdapter(accountWithoutSigning);
+
+ await expect(signer.signMessage('test')).rejects.toThrow(
+ 'Account does not support message signing',
+ );
+ });
+ });
+
+ describe('toEthersProvider', () => {
+ it('should create provider from viem client', async () => {
+ const viemClient = createPublicClient({
+ chain: {
+ id: 80002,
+ name: 'Polygon Amoy',
+ },
+ transport: http('https://test.com'),
+ });
+ const provider = toEthersProvider(viemClient);
+ expect(provider).toBeInstanceOf(ethers.providers.JsonRpcProvider);
+ });
+
+ it('should return ethers provider unchanged', () => {
+ const ethersProvider = new ethers.providers.JsonRpcProvider(
+ 'https://test.com',
+ );
+ const result = toEthersProvider(ethersProvider);
+ expect(result).toBe(ethersProvider);
+ });
+
+ it('should handle non-viem provider correctly', () => {
+ const nonViemProvider = {
+ send: vi.fn(),
+ // This will make it fail the isViemClient check
+ } as any;
+
+ const result = toEthersProvider(nonViemProvider);
+ expect(result).toBe(nonViemProvider);
+ });
+ });
+
+ describe('toTacoSigner', () => {
+ const PRIVATE_KEY =
+ '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; // 32-byte hex
+
+ it('should create signer from viem account', async () => {
+ const viemAccount = privateKeyToAccount(PRIVATE_KEY);
+ const signer = toTacoSigner(viemAccount);
+
+ expect(signer).toBeInstanceOf(ViemSignerAdapter);
+
+ const address = await signer.getAddress();
+ expect(address).toBe(viemAccount.address);
+ });
+
+ it('should return ethers signer unchanged', () => {
+ const ethersSigner = new ethers.Wallet(PRIVATE_KEY);
+ const result = toTacoSigner(ethersSigner);
+
+ expect(result).toBe(ethersSigner);
+ });
+
+ it('should handle non-viem signer correctly', () => {
+ const nonViemSigner = {
+ getAddress: vi.fn(),
+ provider: {}, // This will make it fail the isViemSignerAccount check
+ } as any;
+
+ const result = toTacoSigner(nonViemSigner);
+ expect(result).toBe(nonViemSigner);
+ });
+ });
+
+ describe('type guards', () => {
+ describe('isViemClient', () => {
+ it('should identify actual viem client', () => {
+ const viemClient = createPublicClient({
+ chain: {
+ id: 80002,
+ name: 'Polygon Amoy',
+ },
+ transport: http('https://test.com'),
+ });
+ expect(isViemClient(viemClient)).toBe(true);
+ });
+ it('should identify viem client by chain property', () => {
+ const viemClientByChain = {
+ chain: { id: 1, name: 'mainnet' },
+ getChainId: vi.fn(),
+ };
+ expect(isViemClient(viemClientByChain)).toBe(true);
+ });
+
+ it('should identify viem client by getChainId method', () => {
+ const viemClientByGetChainId = {
+ getChainId: vi.fn(),
+ };
+
+ expect(isViemClient(viemClientByGetChainId)).toBe(true);
+ });
+
+ it('should reject ethers provider', () => {
+ const ethersProvider = new ethers.providers.JsonRpcProvider();
+ expect(isViemClient(ethersProvider)).toBe(false);
+ });
+
+ it('should reject object without viem properties', () => {
+ const notViemClient = {
+ send: vi.fn(),
+ };
+ expect(isViemClient(notViemClient)).toBe(false);
+ });
+ });
+
+ describe('isViemSignerAccount', () => {
+ it('should identify actual viem account', () => {
+ const viemAccount = privateKeyToAccount(PRIVATE_KEY);
+ expect(isViemSignerAccount(viemAccount)).toBe(true);
+ });
+ it('should identify viem account by address property', () => {
+ const viemAccountByAddress = {
+ address: '0x742d35Cc6632C0532c718F63b1a8D7d8a7fAd3b2',
+ };
+
+ expect(isViemSignerAccount(viemAccountByAddress)).toBe(true);
+ });
+
+ it('should reject ethers signer', () => {
+ const ethersSigner = new ethers.Wallet(PRIVATE_KEY);
+ expect(isViemSignerAccount(ethersSigner)).toBe(false);
+ });
+
+ it('should reject object with provider property', () => {
+ const notViemAccount = {
+ address: '0x742d35Cc6632C0532c718F63b1a8D7d8a7fAd3b2',
+ provider: {}, // This makes it look like an ethers signer
+ };
+
+ expect(isViemSignerAccount(notViemAccount)).toBe(false);
+ });
+ });
+ });
+});
diff --git a/packages/shared/test/viem-types.test.ts b/packages/shared/test/viem-types.test.ts
new file mode 100644
index 000000000..a066d8cd4
--- /dev/null
+++ b/packages/shared/test/viem-types.test.ts
@@ -0,0 +1,27 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { describe, expect, it } from 'vitest';
+
+import type { PublicClient, SignerAccount } from '@nucypher/shared';
+
+describe('viem types', () => {
+ it('should support viem-like client objects', () => {
+ const viemLikeClient: PublicClient = {
+ getChainId: () => Promise.resolve(80002),
+ call: (params: any) => Promise.resolve('0x1234'),
+ chain: { name: 'Polygon Amoy', id: 80002 },
+ } as any;
+
+ expect(viemLikeClient.getChainId).toBeDefined();
+ expect(viemLikeClient.call).toBeDefined();
+ });
+
+ it('should support viem-like account objects', () => {
+ const viemLikeAccount: SignerAccount = {
+ address: '0x742d35Cc6632C0532c718F63b1a8D7d8a7fAd3b2',
+ signMessage: () => Promise.resolve('0xsignature'),
+ } as any;
+
+ expect(viemLikeAccount.address).toBeDefined();
+ expect(viemLikeAccount.signMessage).toBeDefined();
+ });
+});
diff --git a/packages/taco-auth/package.json b/packages/taco-auth/package.json
index 2e68475af..ebd406dc1 100644
--- a/packages/taco-auth/package.json
+++ b/packages/taco-auth/package.json
@@ -15,6 +15,7 @@
"author": "NuCypher ",
"exports": {
".": {
+ "types": "./dist/cjs/index.d.ts",
"import": "./dist/es/index.js",
"require": "./dist/cjs/index.js"
}
@@ -26,11 +27,14 @@
"dist"
],
"scripts": {
- "prebuild": "pnpm clean",
- "build": "pnpm build:module && pnpm build:cjs",
+ "prebuild": "pnpm clean && pnpm ensure-es-compatible-imports",
+ "build": "pnpm build:es && pnpm build:cjs",
+ "postbuild": "pnpm ensure-es-package-type",
"build:cjs": "tsc --build ./tsconfig.cjs.json --verbose",
- "build:module": "tsc --build ./tsconfig.es.json --verbose",
+ "build:es": "tsc --build ./tsconfig.es.json --verbose",
"clean": "rm -rf dist",
+ "ensure-es-compatible-imports": "pnpm tsx ../../scripts/ensure-es-compatible-imports.ts",
+ "ensure-es-package-type": "pnpm tsx ../../scripts/ensure-es-package-type.ts",
"exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts",
"lint": "eslint --ext .ts src test",
"lint:fix": "pnpm lint --fix",
diff --git a/packages/taco-auth/src/auth-provider.ts b/packages/taco-auth/src/auth-provider.ts
index cc91f1765..a2f8bfabf 100644
--- a/packages/taco-auth/src/auth-provider.ts
+++ b/packages/taco-auth/src/auth-provider.ts
@@ -1,4 +1,4 @@
-import { AuthSignature } from './auth-sig';
+import { AuthSignature } from './auth-sig.js';
export interface AuthProvider {
getOrCreateAuthSignature(): Promise;
diff --git a/packages/taco-auth/src/auth-sig.ts b/packages/taco-auth/src/auth-sig.ts
index 92720c750..8fe74e907 100644
--- a/packages/taco-auth/src/auth-sig.ts
+++ b/packages/taco-auth/src/auth-sig.ts
@@ -1,8 +1,8 @@
import { EthAddressSchema } from '@nucypher/shared';
import { z } from 'zod';
-import { EIP1271AuthSignature } from './providers/eip1271/auth';
-import { EIP4361AuthSignature } from './providers/eip4361/auth';
+import { EIP1271AuthSignature } from './providers/eip1271/auth.js';
+import { EIP4361AuthSignature } from './providers/eip4361/auth.js';
export const baseAuthSignatureSchema = z.object({
signature: z.string(),
diff --git a/packages/taco-auth/src/index.ts b/packages/taco-auth/src/index.ts
index 2791ece39..056db656e 100644
--- a/packages/taco-auth/src/index.ts
+++ b/packages/taco-auth/src/index.ts
@@ -1,3 +1,3 @@
-export * from './auth-provider';
-export * from './auth-sig';
-export * from './providers';
+export * from './auth-provider.js';
+export * from './auth-sig.js';
+export * from './providers/index.js';
diff --git a/packages/taco-auth/src/providers/eip1271/auth.ts b/packages/taco-auth/src/providers/eip1271/auth.ts
index 4d8c7593c..1ebf0ad91 100644
--- a/packages/taco-auth/src/providers/eip1271/auth.ts
+++ b/packages/taco-auth/src/providers/eip1271/auth.ts
@@ -1,6 +1,6 @@
import { z } from 'zod';
-import { baseAuthSignatureSchema } from '../../auth-sig';
+import { baseAuthSignatureSchema } from '../../auth-sig.js';
export const EIP1271_AUTH_METHOD = 'EIP1271';
diff --git a/packages/taco-auth/src/providers/eip1271/eip1271.ts b/packages/taco-auth/src/providers/eip1271/eip1271.ts
index 43d0c1561..5a9c3433d 100644
--- a/packages/taco-auth/src/providers/eip1271/eip1271.ts
+++ b/packages/taco-auth/src/providers/eip1271/eip1271.ts
@@ -1,6 +1,6 @@
-import { AuthProvider } from '../../auth-provider';
+import { AuthProvider } from '../../auth-provider.js';
-import { EIP1271_AUTH_METHOD, EIP1271AuthSignature } from './auth';
+import { EIP1271_AUTH_METHOD, EIP1271AuthSignature } from './auth.js';
/**
* EIP1271AuthProvider handles EIP-1271 contract-based authentication.
diff --git a/packages/taco-auth/src/providers/eip4361/auth.ts b/packages/taco-auth/src/providers/eip4361/auth.ts
index 2c67d04cc..4541ec98d 100644
--- a/packages/taco-auth/src/providers/eip4361/auth.ts
+++ b/packages/taco-auth/src/providers/eip4361/auth.ts
@@ -1,7 +1,7 @@
import { SiweMessage } from 'siwe';
import { z } from 'zod';
-import { baseAuthSignatureSchema } from '../../auth-sig';
+import { baseAuthSignatureSchema } from '../../auth-sig.js';
export const EIP4361_AUTH_METHOD = 'EIP4361';
diff --git a/packages/taco-auth/src/providers/eip4361/eip4361.ts b/packages/taco-auth/src/providers/eip4361/eip4361.ts
index 0a7481ecb..571e7aebb 100644
--- a/packages/taco-auth/src/providers/eip4361/eip4361.ts
+++ b/packages/taco-auth/src/providers/eip4361/eip4361.ts
@@ -1,14 +1,23 @@
+import {
+ ProviderLike,
+ PublicClient,
+ SignerAccount,
+ SignerLike,
+ TacoSigner,
+ toEthersProvider,
+ toTacoSigner,
+} from '@nucypher/shared';
import { ethers } from 'ethers';
import { SiweMessage } from 'siwe';
-import { AuthProvider } from '../../auth-provider';
-import { LocalStorage } from '../../storage';
+import { AuthProvider } from '../../auth-provider.js';
+import { LocalStorage } from '../../storage.js';
import {
EIP4361_AUTH_METHOD,
EIP4361AuthSignature,
eip4361AuthSignatureSchema,
-} from './auth';
+} from './auth.js';
export type EIP4361AuthProviderParams = {
domain: string;
@@ -31,14 +40,32 @@ const TACO_DEFAULT_URI = 'https://taco.build';
*
* Messages are valid for 2 hours from creation and stored locally keyed by the signer's address.
*
+ * Supports both ethers.js and viem.
+ *
* @implements {AuthProvider}
+ *
+ * @example Ethers.js usage
+ * ```typescript
+ * const provider = new ethers.providers.JsonRpcProvider();
+ * const signer = new ethers.Wallet(privateKey, provider);
+ * const authProvider = new EIP4361AuthProvider(provider, signer);
+ * ```
+ *
+ * @example Viem usage
+ * ```typescript
+ * const publicClient = createPublicClient({ chain: polygon, transport: http() });
+ * const account = privateKeyToAccount('0x...');
+ * const authProvider = new EIP4361AuthProvider(publicClient, account);
+ * ```
*/
export class EIP4361AuthProvider implements AuthProvider {
private readonly storage: LocalStorage;
private readonly providerParams: EIP4361AuthProviderParams;
+ private readonly provider: ethers.providers.Provider;
+ private readonly signer: TacoSigner;
/**
- * Creates a new EIP4361AuthProvider instance.
+ * Creates a new EIP4361AuthProvider instance with ethers.js objects.
*
* @param provider - Ethers provider used to fetch the current chainId
* @param signer - Ethers signer used to sign SIWE messages
@@ -56,11 +83,43 @@ export class EIP4361AuthProvider implements AuthProvider {
* - Nonce: Auto-generated
*/
constructor(
- private readonly provider: ethers.providers.Provider,
- private readonly signer: ethers.Signer,
+ provider: ethers.providers.Provider,
+ signer: ethers.Signer,
+ providerParams?: EIP4361AuthProviderParams,
+ );
+
+ /**
+ * Creates a new EIP4361AuthProvider instance with viem objects.
+ *
+ * @param publicClient - Viem public client used to fetch the current chainId
+ * @param account - Viem account used to sign SIWE messages
+ * @param providerParams - Optional SIWE message configuration
+ * @param providerParams.domain - Domain name for the signing request (e.g. 'app.example.com').
+ * Defaults to current website domain or 'taco.build'
+ * @param providerParams.uri - Full URI of signing request origin (e.g. 'https://app.example.com').
+ * Defaults to current website URL or 'https://taco.build'
+ *
+ * The SIWE message will include:
+ * - A human-readable statement: "{domain} wants you to sign in with your Ethereum account: {address}"
+ * - Version: "1"
+ * - 2 hour expiration from creation time
+ * - Chain ID from the provided provider
+ * - Nonce: Auto-generated
+ */
+ constructor(
+ publicClient: PublicClient,
+ account: SignerAccount,
+ providerParams?: EIP4361AuthProviderParams,
+ );
+
+ constructor(
+ providerLike: ProviderLike,
+ signerLike: SignerLike,
providerParams?: EIP4361AuthProviderParams,
) {
this.storage = new LocalStorage(eip4361AuthSignatureSchema);
+ this.provider = toEthersProvider(providerLike);
+ this.signer = toTacoSigner(signerLike);
if (providerParams) {
this.providerParams = providerParams;
} else {
diff --git a/packages/taco-auth/src/providers/eip4361/external-eip4361.ts b/packages/taco-auth/src/providers/eip4361/external-eip4361.ts
index 8f1eab7f9..2abf0cdfd 100644
--- a/packages/taco-auth/src/providers/eip4361/external-eip4361.ts
+++ b/packages/taco-auth/src/providers/eip4361/external-eip4361.ts
@@ -1,9 +1,9 @@
import { SiweMessage } from 'siwe';
-import { AuthProvider } from '../../auth-provider';
+import { AuthProvider } from '../../auth-provider.js';
-import { EIP4361_AUTH_METHOD, EIP4361AuthSignature } from './auth';
-import { FRESHNESS_IN_MILLISECONDS } from './eip4361';
+import { EIP4361_AUTH_METHOD, EIP4361AuthSignature } from './auth.js';
+import { FRESHNESS_IN_MILLISECONDS } from './eip4361.js';
async function generateAndVerifySiweMessage(
message: string,
diff --git a/packages/taco-auth/src/providers/index.ts b/packages/taco-auth/src/providers/index.ts
index 76b739f00..6607e2b31 100644
--- a/packages/taco-auth/src/providers/index.ts
+++ b/packages/taco-auth/src/providers/index.ts
@@ -1,9 +1,9 @@
// TODO: should we export with package names?
-export { EIP1271AuthSignature } from './eip1271/auth';
-export * from './eip1271/eip1271';
+export { EIP1271AuthSignature } from './eip1271/auth.js';
+export * from './eip1271/eip1271.js';
export {
EIP4361AuthSignature,
USER_ADDRESS_PARAM_DEFAULT,
-} from './eip4361/auth';
-export * from './eip4361/eip4361';
-export * from './eip4361/external-eip4361';
+} from './eip4361/auth.js';
+export * from './eip4361/eip4361.js';
+export * from './eip4361/external-eip4361.js';
diff --git a/packages/taco-auth/src/storage.ts b/packages/taco-auth/src/storage.ts
index fc3ef4165..24e48af1f 100644
--- a/packages/taco-auth/src/storage.ts
+++ b/packages/taco-auth/src/storage.ts
@@ -1,6 +1,6 @@
import { z } from 'zod';
-import { AuthSignature } from './index';
+import { AuthSignature } from './index.js';
interface IStorage {
getItem(key: string): string | null;
diff --git a/packages/taco/README.md b/packages/taco/README.md
index 0fd7afc6f..5eeecb82f 100644
--- a/packages/taco/README.md
+++ b/packages/taco/README.md
@@ -64,6 +64,156 @@ const decryptedMessage = await decrypt(
);
```
+## Viem Support
+
+The TACo SDK supports both [ethers.js](https://docs.ethers.org/) natively, and [viem](https://viem.sh). The same `encrypt` and `decrypt` functions work with both libraries. Here is how to use them with viem:
+
+```bash
+$ yarn add @nucypher/taco viem
+```
+
+```typescript
+import { encrypt, decrypt, conditions, domains, initialize } from '@nucypher/taco';
+import { createPublicClient, http } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
+
+// Initialize TACo
+await initialize();
+
+const viemClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: http(),
+});
+const viemAccount = privateKeyToAccount('0x...');
+
+const ownsNFT = new conditions.predefined.ERC721Ownership({
+ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
+ parameters: [3591],
+ chain: 5,
+});
+
+// Same function names work with viem - TypeScript automatically selects the right overload
+const messageKit = await encrypt(
+ viemClient, // viem PublicClient
+ domains.TESTNET,
+ 'my secret message',
+ ownsNFT,
+ ritualId,
+ viemAccount, // viem Signer Account (`LocalAccount` or `WalletClient`)
+);
+
+// Decrypt with viem
+const decryptedMessage = await decrypt(
+ viemClient,
+ domains.TESTNET,
+ messageKit,
+);
+```
+
+### Automatic Library Detection
+
+TypeScript automatically detects which library objects you're passing and works seamlessly:
+
+```typescript
+// Using ethers.js - automatically uses ethers implementation
+const ethersEncrypted = await encrypt(
+ ethersProvider, // ethers.providers.Provider
+ domains.TESTNET,
+ message,
+ condition,
+ ritualId,
+ ethersSigner // ethers.Signer
+);
+
+// Using viem - automatically uses viem implementation
+const viemEncrypted = await encrypt(
+ publicClient, // viem PublicClient
+ domains.TESTNET,
+ message,
+ condition,
+ ritualId,
+ viemAccount // viem Signer Account (`LocalAccount` or `WalletClient`)
+);
+```
+
+For detailed viem documentation, see [VIEM_SUPPORT.md](./VIEM_SUPPORT.md).
+
+## AccessClient - Object-Oriented Interface
+
+For applications requiring multiple TACo cryptographic operations or complex configuration management, the TACo SDK provides an optional object-oriented interface through the `AccessClient` class. This provides a stateful, higher-level abstraction over the functional API.
+
+The Object-Oriented API is fully backward compatible - you can use both APIs in
+the same application as needed. Except that the AccessClient has additional validations
+and hence throws some errors earlier with different error messages.
+
+NOTE: Using `AccessClient` is equivalent to using the functional API.
+There are no specific recommendations on which approach to use.
+Choose the one that best suits your development preferences.
+
+### Basic Usage
+
+```typescript
+import { AccessClient, ConditionContext, DOMAIN_NAMES } from '@nucypher/taco';
+import { createPublicClient, http } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
+
+// Initialize TACo
+await initialize();
+
+// Set up viem client and account
+const viemClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: http(),
+});
+const viemAccount = privateKeyToAccount('0x...');
+
+// Create AccessClient instance with domain constants
+const accessClient = new AccessClient({
+ domain: DOMAIN_NAMES.TESTNET, // TESTNET -> 'tapir'
+ ritualId: 6,
+ viemClient,
+ viemAccount
+});
+
+// Encrypt data
+const messageKit = await accessClient.encrypt('Hello, secret!', condition);
+
+// Decrypt
+const conditionContext = ConditionContext.fromMessageKit(messageKit);
+
+// if needed Add authentication for ":userAddress" in condition...
+
+const decryptedMessage = await accessClient.decrypt(messageKit, conditionContext);
+// OR with encrypted bytes:
+// const decryptedMessage = await accessClient.decrypt(messageKit.toBytes(), conditionContext);
+```
+
+### Dual Configuration Support
+
+AccessClient supports both viem and ethers.js configurations:
+
+```typescript
+import { AccessClient, DOMAIN_NAMES } from '@nucypher/taco';
+
+// With viem
+const accessClientViem = new AccessClient({
+ domain: DOMAIN_NAMES.TESTNET,
+ ritualId: 6,
+ viemClient,
+ viemAccount
+});
+
+// With ethers.js
+const accessClientEthers = new AccessClient({
+ domain: DOMAIN_NAMES.TESTNET,
+ ritualId: 6,
+ ethersProvider,
+ ethersSigner
+});
+```
+
## Learn more
Please find developer documentation for
diff --git a/packages/taco/VIEM_SUPPORT.md b/packages/taco/VIEM_SUPPORT.md
new file mode 100644
index 000000000..4fbcf4533
--- /dev/null
+++ b/packages/taco/VIEM_SUPPORT.md
@@ -0,0 +1,134 @@
+# Viem Support
+
+The TACo SDK provides unified `encrypt` and `decrypt` functions that work
+seamlessly with both [ethers.js](https://docs.ethers.org/) and
+[viem](https://viem.sh) through TypeScript function overloads. The same function
+names automatically detect which library you're using based on parameter types.
+
+## Installation
+
+Viem is optional. Install it only if you want to use viem instead of ethers.js:
+
+```bash
+npm install viem
+```
+
+### For Authentication Providers
+
+If you need authentication providers that work with viem, install the taco-auth
+package:
+
+```bash
+npm install @nucypher/taco-auth viem
+```
+
+## Supported Libraries
+
+The same `encrypt` and `decrypt` functions work with both ethers.js and viem.
+
+Here is how to use them with viem:
+
+```typescript
+import {
+ encrypt,
+ decrypt,
+ conditions,
+ domains,
+ initialize,
+} from '@nucypher/taco';
+import { createPublicClient, http } from 'viem';
+import { polygonAmoy } from 'viem/chains';
+import { privateKeyToAccount } from 'viem/accounts';
+
+// Initialize TACo
+await initialize();
+
+// Create viem public client
+const publicClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: http(),
+});
+// Create account
+const viemAccount = privateKeyToAccount('0x...');
+
+// Create access condition
+const condition = new conditions.predefined.erc20.ERC20Balance({
+ contractAddress: '0x...',
+ chain: 80002,
+ parameters: [':userAddress'],
+ returnValueTest: {
+ comparator: '>',
+ value: 0,
+ },
+});
+
+// Same function names work with viem - TypeScript automatically detects the right overload
+const encryptedKit = await encrypt(
+ publicClient, // viem PublicClient
+ domains.DEVNET, // or 'lynx'
+ 'Hello, secret!',
+ condition,
+ 27, // ritual ID
+ viemAccount, // viem Signer Account (`LocalAccount` or `WalletClient`)
+);
+
+// Same decrypt function works with viem
+const decryptedMessage = await decrypt(
+ publicClient,
+ domains.DEVNET,
+ encryptedKit,
+);
+
+console.log(new TextDecoder().decode(decryptedMessage)); // "Hello, secret!"
+```
+
+## Authentication Providers
+
+For applications that need authentication providers compatible with viem, use
+the `@nucypher/taco-auth` package:
+
+### EIP4361AuthProvider
+
+`EIP4361AuthProvider` also supports both ethers.js and viem:
+
+```typescript
+import { createPublicClient, http } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
+import { EIP4361AuthProvider } from '@nucypher/taco-auth';
+
+const publicClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: http(),
+});
+const account = privateKeyToAccount('0x...');
+
+// Viem usage
+const authProvider = new EIP4361AuthProvider(publicClient, account, {
+ domain: 'my-app.com',
+ uri: 'https://my-app.com',
+});
+
+const authSignature = await authProvider.getOrCreateAuthSignature();
+```
+
+**Ethers.js Usage (for comparison):**
+
+```typescript
+import { ethers } from 'ethers';
+import { EIP4361AuthProvider } from '@nucypher/taco-auth';
+
+const provider = new ethers.providers.JsonRpcProvider();
+const signer = new ethers.Wallet('0x...', provider);
+
+// Ethers usage
+const authProvider = new EIP4361AuthProvider(provider, signer);
+```
+
+## Installation
+
+Use the appropriate package based on your needs:
+
+- **Encryption only**: Install `@nucypher/taco` + `viem`
+- **Authentication required**: Install both `@nucypher/taco` +
+ `@nucypher/taco-auth` + `viem`
diff --git a/packages/taco/integration-test/access-client.test.ts b/packages/taco/integration-test/access-client.test.ts
new file mode 100644
index 000000000..dc9721f5a
--- /dev/null
+++ b/packages/taco/integration-test/access-client.test.ts
@@ -0,0 +1,252 @@
+import { beforeAll, describe, expect, test } from 'vitest';
+
+import { fromBytes, toBytes } from '@nucypher/shared';
+import {
+ EIP4361AuthProvider,
+ USER_ADDRESS_PARAM_DEFAULT,
+} from '@nucypher/taco-auth';
+import { ethers } from 'ethers';
+import { createPublicClient, http, LocalAccount } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
+import {
+ AccessClient,
+ AccessClientEthersConfig,
+ AccessClientViemConfig,
+ conditions,
+ initialize,
+ ThresholdMessageKit,
+} from '../src';
+import { DkgClient } from '../src/dkg';
+
+const RPC_PROVIDER_URL = 'https://rpc-amoy.polygon.technology';
+const ENCRYPTOR_PRIVATE_KEY =
+ '0x900edb9e8214b2353f82aa195e915128f419a92cfb8bbc0f4784f10ef4112b86';
+const CONSUMER_PRIVATE_KEY =
+ '0xf307e165339cb5deb2b8ec59c31a5c0a957b8e8453ce7fe8a19d9a4c8acf36d4';
+const DOMAIN = 'tapir';
+const RITUAL_ID = 6;
+const CHAIN_ID = 80002; // Polygon Amoy
+
+// temp type-safe configuration interfaces just for this testing file
+type ViemTestConfig = Omit;
+type EthersTestConfig = Omit;
+
+describe.skipIf(!process.env.RUNNING_IN_CI)(
+ 'AccessClient Integration Test',
+ () => {
+ // Create viem accounts from private keys
+ const encryptorAccount = privateKeyToAccount(
+ ENCRYPTOR_PRIVATE_KEY as `0x${string}`,
+ );
+ const consumerAccount = privateKeyToAccount(
+ CONSUMER_PRIVATE_KEY as `0x${string}`,
+ );
+
+ // Create viem clients for correct network (Polygon Amoy)
+ const viemPublicClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: http(RPC_PROVIDER_URL),
+ });
+ const viemTestConfig: ViemTestConfig = {
+ viemClient: viemPublicClient,
+ viemSignerAccount: encryptorAccount,
+ };
+
+ // Create ethers clients for correct network (Polygon Amoy)
+ const ethersProvider = new ethers.providers.JsonRpcProvider(
+ RPC_PROVIDER_URL,
+ );
+ const encryptorSigner = new ethers.Wallet(
+ ENCRYPTOR_PRIVATE_KEY,
+ ethersProvider,
+ );
+ const consumerSigner = new ethers.Wallet(
+ CONSUMER_PRIVATE_KEY,
+ ethersProvider,
+ );
+
+ const ethersTestConfig: EthersTestConfig = {
+ ethersProvider,
+ ethersSigner: encryptorSigner,
+ };
+
+ beforeAll(async () => {
+ // Initialize the TACo library
+ await initialize();
+
+ // Verify both clients are connected to the correct network
+ const [viemChainId, ethersNetwork] = await Promise.all([
+ viemPublicClient.getChainId(),
+ ethersProvider.getNetwork(),
+ ]);
+
+ if (viemChainId !== CHAIN_ID) {
+ throw new Error(
+ `Viem client connected to wrong network. Expected ${CHAIN_ID}, got ${viemChainId}`,
+ );
+ }
+
+ if (ethersNetwork.chainId !== CHAIN_ID) {
+ throw new Error(
+ `Ethers provider connected to wrong network. Expected ${CHAIN_ID}, got ${ethersNetwork.chainId}`,
+ );
+ }
+ }, 10000);
+
+ const createTestCondition = () => {
+ return new conditions.base.rpc.RpcCondition({
+ chain: CHAIN_ID,
+ method: 'eth_getBalance',
+ parameters: [':userAddress', 'latest'],
+ returnValueTest: {
+ comparator: '>=',
+ value: 0,
+ },
+ });
+ };
+
+ const createAuthProvider = (
+ config: ViemTestConfig | EthersTestConfig,
+ customSigner?: ethers.Wallet | LocalAccount,
+ ) => {
+ const provider =
+ (config as EthersTestConfig).ethersProvider ??
+ (config as ViemTestConfig).viemClient;
+ const signerToUse =
+ customSigner ??
+ (config as EthersTestConfig).ethersSigner ??
+ (config as ViemTestConfig).viemSignerAccount;
+
+ return new EIP4361AuthProvider(provider, signerToUse as any);
+ };
+
+ const setupConditionContext = async (
+ messageKit: ThresholdMessageKit,
+ config: ViemTestConfig | EthersTestConfig,
+ customSigner?: ethers.Wallet | LocalAccount,
+ ) => {
+ const conditionContext =
+ conditions.context.ConditionContext.fromMessageKit(messageKit);
+
+ const authProvider = createAuthProvider(config, customSigner);
+ conditionContext.addAuthProvider(
+ USER_ADDRESS_PARAM_DEFAULT,
+ authProvider,
+ );
+
+ return conditionContext;
+ };
+
+ describe
+ .skipIf(!process.env.RUNNING_IN_CI)
+ .each<
+ | ['ethers', EthersTestConfig, ethers.Wallet]
+ | ['viem', ViemTestConfig, LocalAccount]
+ >([
+ ['ethers', ethersTestConfig, consumerSigner],
+ ['viem', viemTestConfig, consumerAccount],
+ ])('TacoClient with %s', (label, objects, consumerSigner) => {
+ test('should encrypt and decrypt a message using standard encrypt method', async () => {
+ // Setup
+ const accessClient = new AccessClient({
+ domain: DOMAIN,
+ ritualId: RITUAL_ID,
+ ...objects,
+ });
+
+ // Create test message and condition
+ const messageString = `This is a secret message from TacoClient with ${label} 🤐`;
+ const message = toBytes(messageString);
+ const condition = createTestCondition();
+
+ // Encrypt the message
+ const messageKit = await accessClient.encrypt(message, condition);
+ expect(messageKit).toBeInstanceOf(ThresholdMessageKit);
+ expect(messageKit.toBytes()).toBeInstanceOf(Uint8Array);
+
+ // Setup condition context for decryption
+ const conditionContext = await setupConditionContext(
+ messageKit,
+ objects,
+ consumerSigner,
+ );
+
+ // Test decryption with Uint8Array input
+ const decryptedBytes = await accessClient.decrypt(
+ messageKit.toBytes(),
+ conditionContext,
+ );
+ const decryptedMessageString = fromBytes(decryptedBytes);
+ expect(decryptedMessageString).toEqual(messageString);
+
+ // Test decryption with MessageKit object input
+ const decryptedBytes2 = await accessClient.decrypt(
+ messageKit,
+ conditionContext,
+ );
+ const decryptedMessageString2 = fromBytes(decryptedBytes2);
+ expect(decryptedMessageString2).toEqual(messageString);
+ }, 30000);
+
+ test('should encrypt and decrypt using offline encryptWithPublicKey method', async () => {
+ // Create AccessClient using the test configuration
+ const accessClient = new AccessClient({
+ domain: DOMAIN,
+ ritualId: RITUAL_ID,
+ ...objects,
+ });
+
+ const messageString = 'This is an offline encrypted message 🔐';
+ const message = toBytes(messageString);
+ const condition = createTestCondition();
+
+ // Get DKG public key from ritual for offline encryption
+ const dkgRitual = await DkgClient.getActiveRitual(
+ ethersProvider,
+ DOMAIN,
+ RITUAL_ID,
+ );
+ const dkgPublicKey = dkgRitual.dkgPublicKey;
+ expect(dkgPublicKey).toBeDefined();
+
+ // Perform offline encryption with DKG public key
+ const messageKit = await accessClient.encryptWithPublicKey(
+ message,
+ condition,
+ dkgPublicKey,
+ );
+ expect(messageKit).toBeInstanceOf(ThresholdMessageKit);
+
+ // Setup condition context with consumer signer for decryption
+ const conditionContext = await setupConditionContext(
+ messageKit,
+ objects,
+ consumerSigner,
+ );
+
+ // Decrypt and verify
+ const decryptedBytes = await accessClient.decrypt(
+ messageKit,
+ conditionContext,
+ );
+ const decryptedMessageString = fromBytes(decryptedBytes);
+ expect(decryptedMessageString).toEqual(messageString);
+ }, 15000);
+
+ test('should successfully validate network configuration', async () => {
+ // Setup
+ const accessClient = new AccessClient({
+ domain: DOMAIN,
+ ritualId: RITUAL_ID,
+ ...objects,
+ });
+
+ // Validate configuration with network calls
+ const validation = accessClient.validateConfig();
+
+ expect(validation).resolves.not.toThrow();
+ }, 10000);
+ });
+ },
+);
diff --git a/packages/taco/integration-test/encrypt-decrypt.test.ts b/packages/taco/integration-test/encrypt-decrypt.test.ts
index db1482940..eb1e02cd7 100644
--- a/packages/taco/integration-test/encrypt-decrypt.test.ts
+++ b/packages/taco/integration-test/encrypt-decrypt.test.ts
@@ -6,6 +6,9 @@ import {
USER_ADDRESS_PARAM_DEFAULT,
} from '@nucypher/taco-auth';
import { ethers } from 'ethers';
+import { createPublicClient, http, LocalAccount, PublicClient } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
import {
conditions,
decrypt,
@@ -25,31 +28,49 @@ const DOMAIN = 'lynx';
const RITUAL_ID = 27;
const CHAIN_ID = 80002;
-describe.skipIf(!process.env.RUNNING_IN_CI)(
+describe
+ .skipIf(!process.env.RUNNING_IN_CI)
+ .each<
+ | ['ethers', ethers.providers.Provider, ethers.Wallet, ethers.Wallet]
+ | ['viem', PublicClient, LocalAccount, LocalAccount]
+ >([
+ [
+ 'ethers',
+ new ethers.providers.JsonRpcProvider(RPC_PROVIDER_URL),
+ new ethers.Wallet(ENCRYPTOR_PRIVATE_KEY),
+ new ethers.Wallet(CONSUMER_PRIVATE_KEY),
+ ],
+ [
+ 'viem',
+ createPublicClient({
+ chain: polygonAmoy,
+ transport: http(RPC_PROVIDER_URL),
+ }),
+ privateKeyToAccount(ENCRYPTOR_PRIVATE_KEY),
+ privateKeyToAccount(CONSUMER_PRIVATE_KEY),
+ ],
+ ])(
'Taco Encrypt/Decrypt Integration Test',
- () => {
- let provider: ethers.providers.JsonRpcProvider;
- let encryptorSigner: ethers.Wallet;
- let consumerSigner: ethers.Wallet;
-
+ (label, provider, encryptorSigner, consumerSigner) => {
beforeAll(async () => {
- provider = new ethers.providers.JsonRpcProvider(RPC_PROVIDER_URL);
- encryptorSigner = new ethers.Wallet(ENCRYPTOR_PRIVATE_KEY, provider);
- consumerSigner = new ethers.Wallet(CONSUMER_PRIVATE_KEY, provider);
-
// Initialize the library
await initialize();
// Verify network connection
- const network = await provider.getNetwork();
- if (network.chainId !== CHAIN_ID) {
+ let chainId: number;
+ if (provider instanceof ethers.providers.Provider) {
+ chainId = (await provider.getNetwork()).chainId;
+ } else {
+ chainId = provider.chain!.id as number;
+ }
+ if (chainId !== CHAIN_ID) {
throw new Error(
- `Provider connected to wrong network. Expected ${CHAIN_ID}, got ${network.chainId}`,
+ `Provider connected to wrong network. Expected ${CHAIN_ID}, got ${chainId}`,
);
}
});
- test('should encrypt and decrypt a message with large condition values', async (value) => {
+ test(`should encrypt and decrypt a message with large condition values using ${label}`, async () => {
// Create test message
const messageString = 'This is a secret 🤐';
const message = toBytes(messageString);
@@ -106,10 +127,12 @@ describe.skipIf(!process.env.RUNNING_IN_CI)(
USER_ADDRESS_PARAM_DEFAULT,
)
) {
- const authProvider = new EIP4361AuthProvider(provider, consumerSigner, {
- domain: 'localhost',
- uri: 'http://localhost:3000',
- });
+ const authProvider = new EIP4361AuthProvider(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ provider as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ consumerSigner as any,
+ );
conditionContext.addAuthProvider(
USER_ADDRESS_PARAM_DEFAULT,
authProvider,
diff --git a/packages/taco/integration-test/viem-to-ethers-ens.test.ts b/packages/taco/integration-test/viem-to-ethers-ens.test.ts
new file mode 100644
index 000000000..204af4faf
--- /dev/null
+++ b/packages/taco/integration-test/viem-to-ethers-ens.test.ts
@@ -0,0 +1,95 @@
+import { describe, expect, test, vi } from 'vitest';
+
+import { toEthersProvider } from '@nucypher/shared';
+import { ethers } from 'ethers';
+import { createPublicClient, http } from 'viem';
+import { mainnet } from 'viem/chains';
+
+describe.skipIf(!process.env.RUNNING_IN_CI)(
+ 'Viem-Ethers Adapter ENS Integration Tests',
+ () => {
+ test('should properly resolve ENS name to address when no viem chain object is provided', async () => {
+ // Test with chain that has ENS registry (mainnet)
+ // Note: mainnet from viem/chains includes ENS registry configuration
+ const mainnetViemClient = createPublicClient({
+ transport: http('https://eth.llamarpc.com'),
+ });
+
+ // Convert to ethers provider to verify ENS address mapping
+ const ethersProvider = toEthersProvider(mainnetViemClient);
+
+ // Test actual ENS name resolution to verify functionality
+ const resolvedAddress = await ethersProvider.resolveName('vitalik.eth');
+ expect(resolvedAddress).toBeTruthy();
+ // Currently vitalik.eth is "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".
+ // But it may change in the future. So we only check if it's a valid Ethereum address.
+ expect(ethers.utils.isAddress(resolvedAddress)).toBe(true); // Valid Ethereum address format
+
+ // Currently ethersProvider.network.ensAddress on mainnet is "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e".
+ expect(ethersProvider.network.ensAddress).toBeTruthy();
+ }, 15000);
+
+ test('should properly handle viem chain with explicit ENS registry configuration', async () => {
+ const chainWithEns = {
+ ...mainnet,
+ contracts: {
+ ...mainnet.contracts,
+ ensRegistry: {
+ // Manually providing ENS registry since viem/chains doesn't include it
+ address:
+ '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' as `0x${string}`,
+ },
+ },
+ };
+
+ const mainnetViemClient = createPublicClient({
+ chain: chainWithEns,
+ transport: http(),
+ });
+
+ // Convert to ethers provider to verify ENS address mapping
+ const ethersProvider = toEthersProvider(mainnetViemClient);
+
+ // Don't check for console.warn since the behavior might vary
+ // The important part is that ENS registry is properly set when provided
+
+ // Verify ENS registry address is properly set
+ expect(ethersProvider.network.ensAddress).toBe(
+ '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
+ );
+
+ // Test actual ENS name resolution to verify functionality
+ const resolvedAddress = await ethersProvider.resolveName('vitalik.eth');
+ expect(resolvedAddress).toBeTruthy();
+ expect(ethers.utils.isAddress(resolvedAddress)).toBe(true); // Valid Ethereum address format
+ }, 15000);
+
+ test('should warn when viem chain lacks ENS registry configuration', async () => {
+ // mainnet from viem/chains does NOT include ensRegistry, only ensUniversalResolver
+ const mainnetViemClient = createPublicClient({
+ chain: mainnet,
+ transport: http(),
+ });
+
+ const consoleSpy = vi.spyOn(console, 'warn');
+
+ // Convert to ethers provider
+ const ethersProvider = toEthersProvider(mainnetViemClient);
+
+ // Should trigger warning since mainnet doesn't have ensRegistry in viem/chains
+ expect(consoleSpy).toHaveBeenCalledWith(
+ expect.stringContaining('No ENS registry found on chain'),
+ );
+ // Clean up the spy
+ consoleSpy.mockRestore();
+
+ // ENS address is expected to not be set
+ expect(ethersProvider.network.ensAddress).toBeUndefined();
+
+ // ENS operation is expected to fail
+ expect(ethersProvider.resolveName('vitalik.eth')).rejects.toThrow(
+ 'network does not support ENS',
+ );
+ });
+ },
+);
diff --git a/packages/taco/package.json b/packages/taco/package.json
index 39fcf9f7e..6e35115d0 100644
--- a/packages/taco/package.json
+++ b/packages/taco/package.json
@@ -14,6 +14,7 @@
"author": "NuCypher ",
"exports": {
".": {
+ "types": "./dist/cjs/index.d.ts",
"import": "./dist/es/index.js",
"require": "./dist/cjs/index.js"
}
@@ -25,14 +26,17 @@
"dist"
],
"scripts": {
- "prebuild": "pnpm clean",
- "build": "pnpm build:module && pnpm build:cjs",
+ "prebuild": "pnpm clean && pnpm ensure-es-compatible-imports",
+ "build": "pnpm build:es && pnpm build:cjs",
+ "postbuild": "pnpm ensure-es-package-type",
"build:cjs": "tsc --build ./tsconfig.cjs.json --verbose",
- "build:module": "tsc --build ./tsconfig.es.json --verbose",
+ "build:es": "tsc --build ./tsconfig.es.json --verbose",
"clean": "rm -rf dist",
+ "ensure-es-compatible-imports": "pnpm tsx ../../scripts/ensure-es-compatible-imports.ts",
+ "ensure-es-package-type": "pnpm tsx ../../scripts/ensure-es-package-type.ts",
"exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts",
"generate-zod-docs": "pnpm dlx tsx scripts/schema-docs-generation.ts",
- "integration-test": "vitest run --config integration-test/vitest.config.ts",
+ "integration-test": "export RUNNING_IN_CI=true && vitest run --config integration-test/vitest.config.ts",
"lint": "eslint --ext .ts src test",
"lint:fix": "pnpm lint --fix",
"package-check": "package-check",
@@ -54,7 +58,8 @@
"@types/semver": "^7.7.0",
"dotenv": "^16.5.0",
"glob": "^11.0.1",
- "modified-zod2md": "0.1.5-modified.4"
+ "modified-zod2md": "0.1.5-modified.4",
+ "viem": "^2.0.0"
},
"engines": {
"node": ">=18",
diff --git a/packages/taco/src/access-client/client.ts b/packages/taco/src/access-client/client.ts
new file mode 100644
index 000000000..2743a74b2
--- /dev/null
+++ b/packages/taco/src/access-client/client.ts
@@ -0,0 +1,283 @@
+import {
+ DkgPublicKey,
+ initialize,
+ ThresholdMessageKit,
+} from '@nucypher/nucypher-core';
+
+import { Condition } from '../conditions/condition.js';
+import { ConditionContext } from '../conditions/context/index.js';
+import { decrypt, encrypt, encryptWithPublicKey } from '../taco.js';
+
+import { AccessConfigValidator } from './config-validator.js';
+import {
+ type AccessClientConfig,
+ type AccessClientEthersConfig,
+ type AccessClientViemConfig,
+} from './config.js';
+
+/**
+ * AccessClient provides an object-oriented interface for TACo cryptographic operations
+ *
+ * This class encapsulates TACo access-control configuration and provides simplified methods
+ * for encryption and decryption operations. It handles configuration validation,
+ * automatic WASM initialization, and provides enhanced error messages.
+ *
+ * @example Using with viem:
+ * ```typescript
+ * import { AccessClient, DOMAIN_NAMES } from '@nucypher/taco';
+ * import { createPublicClient, http } from 'viem';
+ * import { polygonAmoy } from 'viem/chains';
+ * import { privateKeyToAccount } from 'viem/accounts';
+ *
+ * // Create viem client and account
+ * const viemClient = createPublicClient({
+ * chain: polygonAmoy,
+ * transport: http()
+ * });
+ * const viemAccount = privateKeyToAccount('0x...');
+ *
+ * // Create AccessClient - WASM initializes automatically
+ * const accessClient = new AccessClient({
+ * domain: DOMAIN_NAMES.TESTNET, // 'tapir'
+ * ritualId: 6,
+ * viemClient,
+ * viemAccount
+ * });
+ *
+ * // Operations wait for initialization automatically
+ * const messageKit = await accessClient.encrypt('Hello, secret!', condition);
+ * const decrypted = await accessClient.decrypt(messageKit, conditionContext);
+ * ```
+ *
+ * @example Using with ethers.js:
+ * ```typescript
+ * import { AccessClient, DOMAIN_NAMES } from '@nucypher/taco';
+ * import { ethers } from 'ethers';
+ *
+ * // Create ethers provider and signer
+ * const ethersProvider = new ethers.providers.JsonRpcProvider('https://rpc-amoy.polygon.technology');
+ * const ethersSigner = new ethers.Wallet('0x...', ethersProvider);
+ *
+ * // Create AccessClient - WASM initializes automatically
+ * const accessClient = new AccessClient({
+ * domain: DOMAIN_NAMES.TESTNET,
+ * ritualId: 6,
+ * ethersProvider,
+ * ethersSigner
+ * });
+ *
+ * // Operations are safe and wait for readiness
+ * const messageKit = await accessClient.encrypt('Hello, secret!', condition);
+ * const decrypted = await accessClient.decrypt(messageKit, conditionContext);
+ * ```
+ */
+export class AccessClient {
+ private config: AccessClientConfig;
+ private static initializationPromise: Promise;
+
+ /**
+ * Initialize TACo WASM globally (singleton pattern)
+ *
+ * This method ensures TACo WASM is initialized exactly once across all AccessClient instances.
+ * Initialization happens automatically when creating clients or calling operations, but you can
+ * call this explicitly for performance optimization or error handling.
+ *
+ * @returns {Promise} Promise that resolves when TACo WASM is initialized
+ *
+ * @example
+ * ```typescript
+ * // Optional: Pre-initialize for better performance
+ * await AccessClient.initialize();
+ *
+ * // All AccessClient instances share the same initialization
+ * const client1 = new AccessClient(config1);
+ * const client2 = new AccessClient(config2);
+ *
+ * // Operations automatically wait for initialization
+ * const encrypted = await client1.encrypt(data, condition);
+ * ```
+ */
+ static async initialize(): Promise {
+ if (!AccessClient.initializationPromise) {
+ AccessClient.initializationPromise = (async () => {
+ try {
+ await initialize();
+ } catch (error) {
+ console.error(`TACo initialization failed: ${error}`);
+ throw error; // Re-throw to maintain error propagation
+ }
+ })();
+ }
+ return AccessClient.initializationPromise;
+ }
+
+ /**
+ * Create a new AccessClient instance
+ *
+ * @param {AccessClientConfig} config - Configuration for the AccessClient
+ * @throws {Error} If configuration is invalid
+ */
+ constructor(config: AccessClientConfig) {
+ // Validate configuration using AccessConfig
+ const result = AccessConfigValidator.validateFast(config);
+ if (!result.isValid) {
+ throw new Error(`Invalid configuration: ${result.errors.join(', ')}`);
+ }
+
+ this.config = config;
+
+ AccessClient.initialize();
+ }
+
+ /**
+ * Fully validate the configuration including network provider checks
+ *
+ * This method performs comprehensive validation including:
+ * - Domain and ritual ID validation
+ * - Provider/signer configuration validation
+ * - Network compatibility check (calls provider to verify chain ID matches domain)
+ *
+ * @returns {Promise} Promise resolving to validation result with isValid boolean and errors array
+ * @throws {Error} If configuration validation fails
+ *
+ * @example
+ * ```typescript
+ * try {
+ * await accessClient.validateConfig();
+ * console.log('Configuration is valid.');
+ * } catch (error) {
+ * console.error('Configuration validation failed:', error.message);
+ * }
+ * ```
+ */
+ async validateConfig(): Promise {
+ const validationResult = await AccessConfigValidator.validate(this.config);
+ if (!validationResult.isValid) {
+ throw new Error(
+ `Invalid configuration: ${validationResult.errors.join(', ')}`,
+ );
+ }
+ }
+
+ /**
+ * Encrypt data with the given access condition
+ *
+ * @param {string | Uint8Array} data - String or Uint8Array to encrypt
+ * @param {Condition} accessCondition - Access condition for decryption
+ * @returns {Promise} Encrypted message kit
+ * @throws {Error} If encryption fails
+ *
+ * @example
+ * ```typescript
+ * const messageKit = await accessClient.encrypt('Hello, secret!', condition);
+ * ```
+ */
+ async encrypt(
+ data: string | Uint8Array,
+ accessCondition: Condition,
+ ): Promise {
+ await AccessClient.initialize();
+
+ const messageKit = await encrypt(
+ (this.config as AccessClientEthersConfig).ethersProvider ||
+ (this.config as AccessClientViemConfig).viemClient,
+ this.config.domain,
+ data,
+ accessCondition,
+ this.config.ritualId,
+ (this.config as AccessClientEthersConfig).ethersSigner ||
+ (this.config as AccessClientViemConfig).viemSignerAccount,
+ );
+
+ return messageKit;
+ }
+
+ /**
+ * Encrypt data with a provided DKG public key under a specified condition
+ *
+ * This method can be used offline since it doesn't require network access to fetch
+ * the DKG public key (unlike the `encrypt` method which fetches it from the ritual).
+ *
+ * @param {string | Uint8Array} data - String or Uint8Array to encrypt
+ * @param {Condition} accessCondition - Access condition for decryption
+ * @param {DkgPublicKey} dkgPublicKey - The DKG public key to use for encryption
+ * @returns {Promise} Encrypted message kit
+ * @throws {Error} If encryption fails
+ *
+ * @example
+ * ```typescript
+ * // Get DKG public key from ritual or cache
+ * const dkgPublicKey = await getDkgPublicKey(domain, ritualId);
+ *
+ * // Encrypt offline using the public key
+ * const messageKit = await accessClient.encryptWithPublicKey('Hello, secret!', condition, dkgPublicKey);
+ * ```
+ */
+ async encryptWithPublicKey(
+ data: string | Uint8Array,
+ accessCondition: Condition,
+ dkgPublicKey: DkgPublicKey,
+ ): Promise {
+ await AccessClient.initialize();
+
+ const messageKit = await encryptWithPublicKey(
+ data,
+ accessCondition,
+ dkgPublicKey,
+ (this.config as AccessClientEthersConfig).ethersSigner ||
+ (this.config as AccessClientViemConfig).viemSignerAccount,
+ );
+
+ return messageKit;
+ }
+
+ /**
+ * Decrypt data using TACo
+ *
+ * @param {ThresholdMessageKit | Uint8Array} encryptedData - Either a ThresholdMessageKit or encrypted bytes (Uint8Array)
+ * @param {ConditionContext} [conditionContext] - Optional condition context for time-based conditions
+ * @returns {Promise} Decrypted data
+ * @throws {Error} If decryption fails
+ *
+ * @example
+ * ```typescript
+ * // With messageKit
+ * const decrypted = await accessClient.decrypt(messageKit, conditionContext);
+ *
+ * // With encrypted bytes
+ * const decrypted = await accessClient.decrypt(encryptedBytes, conditionContext);
+ * ```
+ */
+ async decrypt(
+ encryptedData: ThresholdMessageKit | Uint8Array,
+ conditionContext?: ConditionContext,
+ ): Promise {
+ await AccessClient.initialize();
+
+ // Handle both messageKit and encrypted bytes
+ const messageKit =
+ encryptedData instanceof ThresholdMessageKit
+ ? encryptedData
+ : ThresholdMessageKit.fromBytes(encryptedData);
+
+ const decrypted = await decrypt(
+ (this.config as AccessClientEthersConfig).ethersProvider ||
+ (this.config as AccessClientViemConfig).viemClient,
+ this.config.domain,
+ messageKit,
+ conditionContext,
+ this.config.porterUris,
+ );
+
+ return decrypted;
+ }
+
+ /**
+ * Get current client configuration
+ *
+ * @returns {Readonly} Client configuration
+ */
+ getConfig(): Readonly {
+ return Object.freeze({ ...this.config });
+ }
+}
diff --git a/packages/taco/src/access-client/config-validator.ts b/packages/taco/src/access-client/config-validator.ts
new file mode 100644
index 000000000..d929643d5
--- /dev/null
+++ b/packages/taco/src/access-client/config-validator.ts
@@ -0,0 +1,272 @@
+/**
+ * TACo Domain Configuration and Validation
+ *
+ * This module provides domain configuration management, validation utilities,
+ * and configuration processing for TACo operations across different networks.
+ */
+
+import {
+ DomainName,
+ DOMAINS,
+ isViemClient,
+ ProviderLike,
+} from '@nucypher/shared';
+import { ethers } from 'ethers';
+import type { PublicClient } from 'viem';
+
+import {
+ type AccessClientConfig,
+ isEthersAccessClientConfig,
+ isViemAccessClientConfig,
+} from './index.js';
+
+/**
+ * Generic validation result interface
+ */
+export interface ValidationResult {
+ isValid: boolean;
+ errors: string[];
+}
+
+/**
+ * Access Configuration Validator
+ *
+ * Validates Access client configurations, domains, and provider compatibility.
+ * Provides both fast and full validation methods for TACo operations.
+ */
+export class AccessConfigValidator {
+ /**
+ * Get all supported TACo domain names
+ * @returns {DomainName[]} Array of supported TACo domain names ('lynx', 'tapir', 'mainnet')
+ */
+ static getSupportedDomains(): DomainName[] {
+ return Object.values(DOMAINS).map((domain) => domain.domain);
+ }
+
+ /**
+ * Check if domain is valid
+ * @param {DomainName} domain - TACo domain name to check ('lynx', 'tapir', 'mainnet')
+ * @returns {boolean} True if domain exists
+ */
+ static isValidDomain(domain: DomainName): boolean {
+ return !!domain && this.getSupportedDomains().includes(domain);
+ }
+
+ /**
+ * Get expected chain ID for domain from DOMAINS configuration
+ * @param {DomainName} domain - Domain name to look up
+ * @returns {number | undefined} Chain ID for the domain, undefined if not found
+ * @private
+ */
+ private static getExpectedChainId(domain: DomainName): number | undefined {
+ const domainEntry = Object.values(DOMAINS).find(
+ (domainConfig) => domainConfig.domain === domain,
+ );
+ return domainEntry?.chainId;
+ }
+
+ /**
+ * Validate ritual ID (basic validation - positive integer or 0)
+ * @param {number} ritualId - Ritual ID to validate
+ * @returns {boolean} True if valid (positive integer or 0)
+ */
+ static isValidRitualId(ritualId: number): boolean {
+ return (
+ typeof ritualId === 'number' &&
+ Number.isInteger(ritualId) &&
+ ritualId >= 0
+ );
+ }
+
+ /**
+ * Validate provider compatibility with domain
+ * @param {DomainName} domain - Domain name
+ * @param {ProviderLike} provider - Provider to validate (ethers Provider or viem PublicClient)
+ * @returns {Promise} True if provider is valid for domain
+ */
+ static async isValidProvider(
+ domain: DomainName,
+ provider: ProviderLike,
+ ): Promise {
+ let chainId: number;
+
+ if (!provider || typeof provider !== 'object') {
+ // Invalid provider
+ return false;
+ }
+
+ // Try to detect provider type and get chain ID safely
+ try {
+ if (isViemClient(provider)) {
+ chainId = await (provider as PublicClient).getChainId();
+ } else {
+ const network = await (
+ provider as ethers.providers.Provider
+ ).getNetwork();
+ chainId = network.chainId;
+ }
+ } catch (error) {
+ // Error getting chain ID
+ return false;
+ }
+
+ // Check if the provider's chain ID matches the domain's expected chain ID
+ return (
+ Object.values(DOMAINS).find(
+ (domainInfo) =>
+ domainInfo.domain === domain && domainInfo.chainId === chainId,
+ ) !== undefined
+ );
+ }
+
+ /**
+ * Fast validation (everything except provider network checks)
+ *
+ * Performs synchronous validation of configuration including:
+ * - Domain name validation
+ * - Ritual ID validation to ensure it is a positive integer
+ * - Provider/signer presence validation
+ * - Chain compatibility check (if chain info is available synchronously)
+ *
+ * @param {TacoClientConfig} config - Configuration to validate
+ * @returns {ValidationResult} Validation result with isValid boolean and errors array
+ */
+ static validateFast(config: AccessClientConfig): ValidationResult {
+ const errors: string[] = [];
+
+ // Validate domain
+ if (!config.domain) {
+ errors.push('The property `domain` is required');
+ } else if (!this.isValidDomain(config.domain)) {
+ errors.push(
+ `Invalid domain name: ${config.domain}. Supported domains: ${this.getSupportedDomains().join(', ')}`,
+ );
+ }
+
+ // Validate ritual ID
+ if (!config.ritualId) {
+ errors.push('The property `ritualId` is required');
+ } else if (!this.isValidRitualId(config.ritualId)) {
+ errors.push(
+ `Invalid ritual ID: ${config.ritualId} for domain ${config.domain}`,
+ );
+ }
+
+ // Validate blockchain client configuration
+ if (isViemAccessClientConfig(config)) {
+ // Viem configuration
+ if (!config.viemClient) {
+ errors.push('viemClient is required for viem configuration');
+ }
+ if (!config.viemSignerAccount) {
+ errors.push('viemSignerAccount is required for viem configuration');
+ }
+ } else if (isEthersAccessClientConfig(config)) {
+ // Ethers configuration
+ if (!config.ethersProvider) {
+ errors.push('ethersProvider is required for ethers configuration');
+ }
+ if (!config.ethersSigner) {
+ errors.push('ethersSigner is required for ethers configuration');
+ }
+ } else {
+ errors.push(
+ 'Configuration must include either viem objects (viemClient + viemSignerAccount) or ethers objects (ethersProvider + ethersSigner)',
+ );
+ }
+
+ // Validate chain compatibility (synchronous check)
+ const chainValidation = this.validateChainCompatibility(config);
+ if (!chainValidation.isValid) {
+ errors.push(...chainValidation.errors);
+ }
+
+ return { isValid: errors.length === 0, errors };
+ }
+
+ /**
+ * Synchronous chain compatibility validation
+ *
+ * Validates provider chain compatibility with domain requirements using
+ * synchronously available chain information.
+ *
+ * @param {TacoClientConfig} config - Configuration to validate
+ * @returns {ValidationResult} Validation result
+ * @private
+ */
+ private static validateChainCompatibility(
+ config: AccessClientConfig,
+ ): ValidationResult {
+ const errors: string[] = [];
+
+ // Get expected chain ID for domain
+ const expectedChainId = this.getExpectedChainId(config.domain);
+ if (!expectedChainId) {
+ errors.push(`Unsupported domain: ${config.domain}`);
+ return { isValid: false, errors };
+ }
+
+ if (isViemAccessClientConfig(config) && config.viemClient) {
+ // Note: If viemClient.chain is undefined, we skip synchronous validation
+ // Full validation with validateFull() will perform the network check
+ const viemClient = config.viemClient as PublicClient;
+ if (viemClient.chain && viemClient.chain.id !== expectedChainId) {
+ errors.push(
+ `Provider chain mismatch: viem client chain ID ${viemClient.chain.id} does not match domain '${config.domain}' (expected ${expectedChainId})`,
+ );
+ }
+ } // No need to count for the other cases. The caller methods already handle them.
+
+ return { isValid: errors.length === 0, errors };
+ }
+
+ /**
+ * Full validation including async provider network checks
+ *
+ * Performs comprehensive validation including:
+ * - All fast validation checks
+ * - Async network calls to verify provider chain ID matches domain requirements
+ *
+ * Use this method when you need complete validation including network connectivity checks.
+ * For faster validation without network calls, use validateFast().
+ *
+ * @param {TacoClientConfig} config - Configuration to validate
+ * @returns {Promise} Promise resolving to validation result with isValid boolean and errors array
+ */
+ static async validate(config: AccessClientConfig): Promise {
+ // First run fast validation
+ const fastResult = this.validateFast(config);
+ if (!fastResult.isValid) {
+ return fastResult;
+ }
+
+ const errors: string[] = [];
+
+ // Additional async provider validation
+ let provider: PublicClient | ethers.providers.Provider | undefined;
+
+ if (isViemAccessClientConfig(config)) {
+ provider = config.viemClient;
+ } else if (isEthersAccessClientConfig(config)) {
+ provider = config.ethersProvider;
+ }
+
+ // Validate provider compatibility with domain (if both exist)
+ if (provider && config.domain) {
+ const isValidProvider = await this.isValidProvider(
+ config.domain,
+ provider,
+ );
+ if (!isValidProvider) {
+ errors.push(
+ `Invalid provider for domain: ${config.domain}. Provider chain ID does not match domain requirements.`,
+ );
+ }
+ }
+
+ return {
+ isValid: errors.length === 0,
+ errors,
+ };
+ }
+}
diff --git a/packages/taco/src/access-client/config.ts b/packages/taco/src/access-client/config.ts
new file mode 100644
index 000000000..7ce37cd4c
--- /dev/null
+++ b/packages/taco/src/access-client/config.ts
@@ -0,0 +1,74 @@
+/**
+ * AccessClient configuration types and utilities
+ *
+ * This module contains all configuration interfaces, type definitions, and utility functions
+ * for configuring AccessClient instances with different blockchain client libraries (viem, ethers.js).
+ */
+
+import {
+ DomainName,
+ type PublicClient,
+ type SignerAccount,
+} from '@nucypher/shared';
+import type { ethers } from 'ethers';
+
+/**
+ * Base configuration for AccessClient
+ */
+interface AccessClientBaseConfig {
+ /** TACo domain name (e.g., 'lynx', 'tapir', 'mainnet') */
+ domain: DomainName;
+ /** Ritual ID for the TACo operations */
+ ritualId: number;
+ /** Optional Porter URIs */
+ porterUris?: string[];
+}
+
+/**
+ * Viem configuration for AccessClient
+ */
+export interface AccessClientViemConfig extends AccessClientBaseConfig {
+ /** Viem PublicClient for blockchain operations */
+ viemClient: PublicClient;
+ /** Viem SignerAccount for signing operations */
+ viemSignerAccount: SignerAccount;
+}
+
+/**
+ * Ethers configuration for AccessClient
+ */
+export interface AccessClientEthersConfig extends AccessClientBaseConfig {
+ /** Ethers Provider for blockchain operations */
+ ethersProvider: ethers.providers.Provider;
+ /** Ethers Signer for signing operations */
+ ethersSigner: ethers.Signer;
+}
+
+/**
+ * Union type for AccessClient configuration - supports both viem and ethers.js
+ */
+export type AccessClientConfig =
+ | AccessClientViemConfig
+ | AccessClientEthersConfig;
+
+/**
+ * Type guard to check if config is viem-based
+ * @param config - AccessClient configuration to check
+ * @returns true if the configuration is for viem client
+ */
+export function isViemAccessClientConfig(
+ config: AccessClientConfig,
+): config is AccessClientViemConfig {
+ return 'viemClient' in config && 'viemSignerAccount' in config;
+}
+
+/**
+ * Type guard to check if config is ethers-based
+ * @param config - AccessClient configuration to check
+ * @returns true if the configuration is for ethers client
+ */
+export function isEthersAccessClientConfig(
+ config: AccessClientConfig,
+): config is AccessClientEthersConfig {
+ return 'ethersProvider' in config && 'ethersSigner' in config;
+}
diff --git a/packages/taco/src/access-client/index.ts b/packages/taco/src/access-client/index.ts
new file mode 100644
index 000000000..652b3f1c6
--- /dev/null
+++ b/packages/taco/src/access-client/index.ts
@@ -0,0 +1,2 @@
+export * from './client.js';
+export * from './config.js';
diff --git a/packages/taco/src/conditions/base/contract.ts b/packages/taco/src/conditions/base/contract.ts
index 54df510dd..bfc236234 100644
--- a/packages/taco/src/conditions/base/contract.ts
+++ b/packages/taco/src/conditions/base/contract.ts
@@ -1,17 +1,17 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
ContractConditionProps,
contractConditionSchema,
ContractConditionType,
-} from '../schemas/contract';
-import { OmitConditionType } from '../shared';
+} from '../schemas/contract.js';
+import { OmitConditionType } from '../shared.js';
export {
ContractConditionProps,
contractConditionSchema,
ContractConditionType,
FunctionAbiProps,
-} from '../schemas/contract';
+} from '../schemas/contract.js';
export class ContractCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/base/index.ts b/packages/taco/src/conditions/base/index.ts
index 6de843d86..65fd0836b 100644
--- a/packages/taco/src/conditions/base/index.ts
+++ b/packages/taco/src/conditions/base/index.ts
@@ -1,9 +1,9 @@
// Exporting classes here instead of their respective schema files to
// avoid circular dependency on Condition class.
-export * as contract from './contract';
-export * as jsonApi from './json-api';
-export * as jsonRpc from './json-rpc';
-export * as jwt from './jwt';
-export * as rpc from './rpc';
-export * as time from './time';
+export * as contract from './contract.js';
+export * as jsonApi from './json-api.js';
+export * as jsonRpc from './json-rpc.js';
+export * as jwt from './jwt.js';
+export * as rpc from './rpc.js';
+export * as time from './time.js';
diff --git a/packages/taco/src/conditions/base/json-api.ts b/packages/taco/src/conditions/base/json-api.ts
index c972b5868..c44088b04 100644
--- a/packages/taco/src/conditions/base/json-api.ts
+++ b/packages/taco/src/conditions/base/json-api.ts
@@ -1,16 +1,16 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
JsonApiConditionProps,
jsonApiConditionSchema,
JsonApiConditionType,
-} from '../schemas/json-api';
-import { OmitConditionType } from '../shared';
+} from '../schemas/json-api.js';
+import { OmitConditionType } from '../shared.js';
export {
JsonApiConditionProps,
jsonApiConditionSchema,
JsonApiConditionType,
-} from '../schemas/json-api';
+} from '../schemas/json-api.js';
export class JsonApiCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/base/json-rpc.ts b/packages/taco/src/conditions/base/json-rpc.ts
index 756e651c0..cbe681aa1 100644
--- a/packages/taco/src/conditions/base/json-rpc.ts
+++ b/packages/taco/src/conditions/base/json-rpc.ts
@@ -1,16 +1,16 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
JsonRpcConditionProps,
jsonRpcConditionSchema,
JsonRpcConditionType,
-} from '../schemas/json-rpc';
-import { OmitConditionType } from '../shared';
+} from '../schemas/json-rpc.js';
+import { OmitConditionType } from '../shared.js';
export {
JsonRpcConditionProps,
jsonRpcConditionSchema,
JsonRpcConditionType,
-} from '../schemas/json-rpc';
+} from '../schemas/json-rpc.js';
export class JsonRpcCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/base/jwt.ts b/packages/taco/src/conditions/base/jwt.ts
index 92469a4cb..4eaa428e0 100644
--- a/packages/taco/src/conditions/base/jwt.ts
+++ b/packages/taco/src/conditions/base/jwt.ts
@@ -1,17 +1,17 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
JWTConditionProps,
jwtConditionSchema,
JWTConditionType,
-} from '../schemas/jwt';
-import { OmitConditionType } from '../shared';
+} from '../schemas/jwt.js';
+import { OmitConditionType } from '../shared.js';
export {
JWT_PARAM_DEFAULT,
JWTConditionProps,
jwtConditionSchema,
JWTConditionType,
-} from '../schemas/jwt';
+} from '../schemas/jwt.js';
export class JWTCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/base/rpc.ts b/packages/taco/src/conditions/base/rpc.ts
index 8f73c79c1..8376e4a21 100644
--- a/packages/taco/src/conditions/base/rpc.ts
+++ b/packages/taco/src/conditions/base/rpc.ts
@@ -1,16 +1,16 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
RpcConditionProps,
rpcConditionSchema,
RpcConditionType,
-} from '../schemas/rpc';
-import { OmitConditionType } from '../shared';
+} from '../schemas/rpc.js';
+import { OmitConditionType } from '../shared.js';
export {
RpcConditionProps,
rpcConditionSchema,
RpcConditionType,
-} from '../schemas/rpc';
+} from '../schemas/rpc.js';
export class RpcCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/base/time.ts b/packages/taco/src/conditions/base/time.ts
index 2d8be8b63..96f49a105 100644
--- a/packages/taco/src/conditions/base/time.ts
+++ b/packages/taco/src/conditions/base/time.ts
@@ -1,17 +1,17 @@
-import { Condition } from '../condition';
+import { Condition } from '../condition.js';
import {
TimeConditionProps,
timeConditionSchema,
TimeConditionType,
-} from '../schemas/time';
-import { OmitConditionType } from '../shared';
+} from '../schemas/time.js';
+import { OmitConditionType } from '../shared.js';
export {
TimeConditionMethod,
TimeConditionProps,
timeConditionSchema,
TimeConditionType,
-} from '../schemas/time';
+} from '../schemas/time.js';
export class TimeCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/compound-condition.ts b/packages/taco/src/conditions/compound-condition.ts
index d7de784e0..87a2af387 100644
--- a/packages/taco/src/conditions/compound-condition.ts
+++ b/packages/taco/src/conditions/compound-condition.ts
@@ -1,16 +1,16 @@
-import { Condition, ConditionProps } from './condition';
+import { Condition, ConditionProps } from './condition.js';
import {
CompoundConditionProps,
compoundConditionSchema,
CompoundConditionType,
-} from './schemas/compound';
-import { OmitConditionType } from './shared';
+} from './schemas/compound.js';
+import { OmitConditionType } from './shared.js';
export {
CompoundConditionProps,
compoundConditionSchema,
CompoundConditionType,
-} from './schemas/compound';
+} from './schemas/compound.js';
export type ConditionOrProps = Condition | ConditionProps;
diff --git a/packages/taco/src/conditions/condition-expr.ts b/packages/taco/src/conditions/condition-expr.ts
index db1c0bed2..104a0b6c0 100644
--- a/packages/taco/src/conditions/condition-expr.ts
+++ b/packages/taco/src/conditions/condition-expr.ts
@@ -1,10 +1,10 @@
import { Conditions as CoreConditions } from '@nucypher/nucypher-core';
import { SemVer } from 'semver';
-import { fromJSON, toJSON } from '../utils';
+import { fromJSON, toJSON } from '../utils.js';
-import { Condition } from './condition';
-import { ConditionFactory } from './condition-factory';
+import { ConditionFactory } from './condition-factory.js';
+import { Condition } from './condition.js';
const ERR_VERSION = (provided: string, current: string) =>
`Version provided, ${provided}, is incompatible with current version, ${current}`;
diff --git a/packages/taco/src/conditions/condition-factory.ts b/packages/taco/src/conditions/condition-factory.ts
index ab84ae909..895ccae15 100644
--- a/packages/taco/src/conditions/condition-factory.ts
+++ b/packages/taco/src/conditions/condition-factory.ts
@@ -2,40 +2,40 @@ import {
ContractCondition,
ContractConditionProps,
ContractConditionType,
-} from './base/contract';
+} from './base/contract.js';
import {
JsonApiCondition,
JsonApiConditionProps,
JsonApiConditionType,
-} from './base/json-api';
+} from './base/json-api.js';
import {
JsonRpcCondition,
JsonRpcConditionProps,
JsonRpcConditionType,
-} from './base/json-rpc';
-import { JWTCondition, JWTConditionProps, JWTConditionType } from './base/jwt';
-import { RpcCondition, RpcConditionProps, RpcConditionType } from './base/rpc';
+} from './base/json-rpc.js';
+import { JWTCondition, JWTConditionProps, JWTConditionType } from './base/jwt.js';
+import { RpcCondition, RpcConditionProps, RpcConditionType } from './base/rpc.js';
import {
TimeCondition,
TimeConditionProps,
TimeConditionType,
-} from './base/time';
+} from './base/time.js';
import {
CompoundCondition,
CompoundConditionProps,
CompoundConditionType,
-} from './compound-condition';
-import { Condition, ConditionProps } from './condition';
+} from './compound-condition.js';
+import { Condition, ConditionProps } from './condition.js';
import {
IfThenElseCondition,
IfThenElseConditionProps,
IfThenElseConditionType,
-} from './if-then-else-condition';
+} from './if-then-else-condition.js';
import {
SequentialCondition,
SequentialConditionProps,
SequentialConditionType,
-} from './sequential';
+} from './sequential.js';
const ERR_INVALID_CONDITION_TYPE = (type: string) =>
`Invalid condition type: ${type}`;
diff --git a/packages/taco/src/conditions/condition.ts b/packages/taco/src/conditions/condition.ts
index 1db41e3b6..30f8648df 100644
--- a/packages/taco/src/conditions/condition.ts
+++ b/packages/taco/src/conditions/condition.ts
@@ -1,11 +1,11 @@
import { objectEquals } from '@nucypher/shared';
import { z } from 'zod';
-import { toJSON } from '../utils';
+import { toJSON } from '../utils.js';
-import { USER_ADDRESS_PARAMS } from './const';
+import { USER_ADDRESS_PARAMS } from './const.js';
-export { baseConditionSchema } from './schemas/common';
+export { baseConditionSchema } from './schemas/common.js';
type ConditionSchema = z.ZodSchema;
export type ConditionProps = z.infer;
diff --git a/packages/taco/src/conditions/context/context.ts b/packages/taco/src/conditions/context/context.ts
index c22993ac8..c66a28014 100644
--- a/packages/taco/src/conditions/context/context.ts
+++ b/packages/taco/src/conditions/context/context.ts
@@ -8,16 +8,16 @@ import {
USER_ADDRESS_PARAM_DEFAULT,
} from '@nucypher/taco-auth';
-import { CoreConditions, CoreContext } from '../../types';
-import { toJSON } from '../../utils';
-import { Condition, ConditionProps } from '../condition';
-import { ConditionExpression } from '../condition-expr';
+import { CoreConditions, CoreContext } from '../../types.js';
+import { toJSON } from '../../utils.js';
+import { ConditionExpression } from '../condition-expr.js';
+import { Condition, ConditionProps } from '../condition.js';
import {
CONTEXT_PARAM_FULL_MATCH_REGEXP,
CONTEXT_PARAM_PREFIX,
CONTEXT_PARAM_REGEXP,
USER_ADDRESS_PARAMS,
-} from '../const';
+} from '../const.js';
export type CustomContextParam =
| string
diff --git a/packages/taco/src/conditions/context/index.ts b/packages/taco/src/conditions/context/index.ts
index e18afda2a..5a1db3dfb 100644
--- a/packages/taco/src/conditions/context/index.ts
+++ b/packages/taco/src/conditions/context/index.ts
@@ -1 +1 @@
-export { ConditionContext, type CustomContextParam } from './context';
+export { ConditionContext, type CustomContextParam } from './context.js';
diff --git a/packages/taco/src/conditions/if-then-else-condition.ts b/packages/taco/src/conditions/if-then-else-condition.ts
index 79d99b49c..03635363e 100644
--- a/packages/taco/src/conditions/if-then-else-condition.ts
+++ b/packages/taco/src/conditions/if-then-else-condition.ts
@@ -1,16 +1,16 @@
-import { Condition } from './condition';
+import { Condition } from './condition.js';
import {
IfThenElseConditionProps,
ifThenElseConditionSchema,
IfThenElseConditionType,
-} from './schemas/if-then-else';
-import { OmitConditionType } from './shared';
+} from './schemas/if-then-else.js';
+import { OmitConditionType } from './shared.js';
export {
IfThenElseConditionProps,
ifThenElseConditionSchema,
IfThenElseConditionType,
-} from './schemas/if-then-else';
+} from './schemas/if-then-else.js';
export class IfThenElseCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/index.ts b/packages/taco/src/conditions/index.ts
index 0c8c3d5c8..be40bebc9 100644
--- a/packages/taco/src/conditions/index.ts
+++ b/packages/taco/src/conditions/index.ts
@@ -1,11 +1,11 @@
-import * as base from './base';
-import * as predefined from './predefined';
+import * as base from './base/index.js';
+import * as predefined from './predefined/index.js';
-export * as compound from './compound-condition';
-export * as condition from './condition';
-export * as conditionExpr from './condition-expr';
-export { ConditionFactory } from './condition-factory';
-export * as context from './context';
-export * as ifThenElse from './if-then-else-condition';
-export * as sequential from './sequential';
+export * as compound from './compound-condition.js';
+export * as condition from './condition.js';
+export * as conditionExpr from './condition-expr.js';
+export { ConditionFactory } from './condition-factory.js';
+export * as context from './context/index.js';
+export * as ifThenElse from './if-then-else-condition.js';
+export * as sequential from './sequential.js';
export { base, predefined };
diff --git a/packages/taco/src/conditions/multi-condition.ts b/packages/taco/src/conditions/multi-condition.ts
index 434ab812a..f2e3fb9fa 100644
--- a/packages/taco/src/conditions/multi-condition.ts
+++ b/packages/taco/src/conditions/multi-condition.ts
@@ -1,7 +1,7 @@
-import { CompoundConditionType } from './compound-condition';
-import { ConditionProps } from './condition';
-import { IfThenElseConditionType } from './if-then-else-condition';
-import { ConditionVariableProps, SequentialConditionType } from './sequential';
+import { CompoundConditionType } from './compound-condition.js';
+import { ConditionProps } from './condition.js';
+import { IfThenElseConditionType } from './if-then-else-condition.js';
+import { ConditionVariableProps, SequentialConditionType } from './sequential.js';
export const maxNestedDepth =
(maxDepth: number) =>
diff --git a/packages/taco/src/conditions/predefined/erc20.ts b/packages/taco/src/conditions/predefined/erc20.ts
index cd3087e7f..eeaa3ab70 100644
--- a/packages/taco/src/conditions/predefined/erc20.ts
+++ b/packages/taco/src/conditions/predefined/erc20.ts
@@ -4,7 +4,7 @@ import {
ContractCondition,
ContractConditionProps,
ContractConditionType,
-} from '../base/contract';
+} from '../base/contract.js';
type ERC20BalanceFields = 'contractAddress' | 'chain' | 'returnValueTest';
diff --git a/packages/taco/src/conditions/predefined/erc721.ts b/packages/taco/src/conditions/predefined/erc721.ts
index 90b2a33de..15ec2e20c 100644
--- a/packages/taco/src/conditions/predefined/erc721.ts
+++ b/packages/taco/src/conditions/predefined/erc721.ts
@@ -4,7 +4,7 @@ import {
ContractCondition,
ContractConditionProps,
ContractConditionType,
-} from '../base/contract';
+} from '../base/contract.js';
type ERC721OwnershipFields = 'contractAddress' | 'chain' | 'parameters';
diff --git a/packages/taco/src/conditions/predefined/index.ts b/packages/taco/src/conditions/predefined/index.ts
index 7d29ceb02..7309abda4 100644
--- a/packages/taco/src/conditions/predefined/index.ts
+++ b/packages/taco/src/conditions/predefined/index.ts
@@ -1,2 +1,2 @@
-export * as erc20 from './erc20';
-export * as erc721 from './erc721';
+export * as erc20 from './erc20.js';
+export * as erc721 from './erc721.js';
diff --git a/packages/taco/src/conditions/schemas/common.ts b/packages/taco/src/conditions/schemas/common.ts
index 6669e4d30..f4050d7f0 100644
--- a/packages/taco/src/conditions/schemas/common.ts
+++ b/packages/taco/src/conditions/schemas/common.ts
@@ -2,7 +2,7 @@ import { JSONPath } from '@astronautlabs/jsonpath';
import { USER_ADDRESS_PARAM_DEFAULT } from '@nucypher/taco-auth';
import { Primitive, z, ZodLiteral } from 'zod';
-import { CONTEXT_PARAM_PREFIX, CONTEXT_PARAM_REGEXP } from '../const';
+import { CONTEXT_PARAM_PREFIX, CONTEXT_PARAM_REGEXP } from '../const.js';
// We want to discriminate between ContextParams and plain strings
// If a string starts with `:`, it's a ContextParam
diff --git a/packages/taco/src/conditions/schemas/compound.ts b/packages/taco/src/conditions/schemas/compound.ts
index df8dd40ce..3c750aee6 100644
--- a/packages/taco/src/conditions/schemas/compound.ts
+++ b/packages/taco/src/conditions/schemas/compound.ts
@@ -1,9 +1,9 @@
import { z } from 'zod';
-import { maxNestedDepth } from '../multi-condition';
+import { maxNestedDepth } from '../multi-condition.js';
-import { baseConditionSchema } from './common';
-import { anyConditionSchema } from './utils';
+import { baseConditionSchema } from './common.js';
+import { anyConditionSchema } from './utils.js';
export const CompoundConditionType = 'compound';
diff --git a/packages/taco/src/conditions/schemas/context.ts b/packages/taco/src/conditions/schemas/context.ts
index d4925e8bb..9a423880f 100644
--- a/packages/taco/src/conditions/schemas/context.ts
+++ b/packages/taco/src/conditions/schemas/context.ts
@@ -1,8 +1,8 @@
import { z } from 'zod';
-import { CONTEXT_PARAM_FULL_MATCH_REGEXP } from '../const';
+import { CONTEXT_PARAM_FULL_MATCH_REGEXP } from '../const.js';
-import { plainStringSchema } from './common';
+import { plainStringSchema } from './common.js';
const UINT256_MAX = BigInt(
'115792089237316195423570985008687907853269984665640564039457584007913129639935',
diff --git a/packages/taco/src/conditions/schemas/contract.ts b/packages/taco/src/conditions/schemas/contract.ts
index 7ff99168a..5fcfd6160 100644
--- a/packages/taco/src/conditions/schemas/contract.ts
+++ b/packages/taco/src/conditions/schemas/contract.ts
@@ -2,8 +2,8 @@ import { EthAddressSchema } from '@nucypher/shared';
import { ethers } from 'ethers';
import { z } from 'zod';
-import { blockchainParamOrContextParamSchema } from './context';
-import { rpcConditionSchema } from './rpc';
+import { blockchainParamOrContextParamSchema } from './context.js';
+import { rpcConditionSchema } from './rpc.js';
// TODO: Consider replacing with `z.unknown`:
// Since Solidity types are tied to Solidity version, we may not be able to accurately represent them in Zod.
diff --git a/packages/taco/src/conditions/schemas/export-for-zod-doc-gen.ts b/packages/taco/src/conditions/schemas/export-for-zod-doc-gen.ts
index 36e6cfa24..b6441652a 100755
--- a/packages/taco/src/conditions/schemas/export-for-zod-doc-gen.ts
+++ b/packages/taco/src/conditions/schemas/export-for-zod-doc-gen.ts
@@ -4,18 +4,18 @@
* NOTE: The order of the exported Zod objects in this file dictates the order of the generated markdown.
*/
-export * from './utils';
+export * from './utils.js';
// ts-unused-exports:disable-next-line - this comment line is added to prevent lint from changing or objecting the export order.
-export * from './common';
-export * from './context';
+export * from './common.js';
+export * from './context.js';
// ts-unused-exports:disable-next-line - this comment line is added to prevent lint from changing or objecting the export order.
-export * from './compound';
-export * from './contract';
-export * from './if-then-else';
-export * from './json-api';
-export * from './json-rpc';
-export * from './jwt';
-export * from './return-value-test';
-export * from './rpc';
-export * from './sequential';
-export * from './time';
+export * from './compound.js';
+export * from './contract.js';
+export * from './if-then-else.js';
+export * from './json-api.js';
+export * from './json-rpc.js';
+export * from './jwt.js';
+export * from './return-value-test.js';
+export * from './rpc.js';
+export * from './sequential.js';
+export * from './time.js';
diff --git a/packages/taco/src/conditions/schemas/if-then-else.ts b/packages/taco/src/conditions/schemas/if-then-else.ts
index dc90ee992..39769fd47 100644
--- a/packages/taco/src/conditions/schemas/if-then-else.ts
+++ b/packages/taco/src/conditions/schemas/if-then-else.ts
@@ -1,9 +1,9 @@
import { z } from 'zod';
-import { maxNestedDepth } from '../multi-condition';
+import { maxNestedDepth } from '../multi-condition.js';
-import { baseConditionSchema } from './common';
-import { anyConditionSchema } from './utils';
+import { baseConditionSchema } from './common.js';
+import { anyConditionSchema } from './utils.js';
export const IfThenElseConditionType = 'if-then-else';
diff --git a/packages/taco/src/conditions/schemas/json-api.ts b/packages/taco/src/conditions/schemas/json-api.ts
index 8fa6e7928..a4b9a9f74 100644
--- a/packages/taco/src/conditions/schemas/json-api.ts
+++ b/packages/taco/src/conditions/schemas/json-api.ts
@@ -1,8 +1,8 @@
import { z } from 'zod';
-import { baseConditionSchema, httpsURLSchema, jsonPathSchema } from './common';
-import { contextParamSchema } from './context';
-import { returnValueTestSchema } from './return-value-test';
+import { baseConditionSchema, httpsURLSchema, jsonPathSchema } from './common.js';
+import { contextParamSchema } from './context.js';
+import { returnValueTestSchema } from './return-value-test.js';
export const JsonApiConditionType = 'json-api';
diff --git a/packages/taco/src/conditions/schemas/json-rpc.ts b/packages/taco/src/conditions/schemas/json-rpc.ts
index d945f2f5b..c0edc8a8c 100644
--- a/packages/taco/src/conditions/schemas/json-rpc.ts
+++ b/packages/taco/src/conditions/schemas/json-rpc.ts
@@ -1,8 +1,8 @@
import { z } from 'zod';
-import { baseConditionSchema, httpsURLSchema, jsonPathSchema } from './common';
-import { contextParamSchema } from './context';
-import { returnValueTestSchema } from './return-value-test';
+import { baseConditionSchema, httpsURLSchema, jsonPathSchema } from './common.js';
+import { contextParamSchema } from './context.js';
+import { returnValueTestSchema } from './return-value-test.js';
export const JsonRpcConditionType = 'json-rpc';
diff --git a/packages/taco/src/conditions/schemas/jwt.ts b/packages/taco/src/conditions/schemas/jwt.ts
index 34ef9457e..ab6d47534 100644
--- a/packages/taco/src/conditions/schemas/jwt.ts
+++ b/packages/taco/src/conditions/schemas/jwt.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { baseConditionSchema } from './common';
-import { contextParamSchema } from './context';
+import { baseConditionSchema } from './common.js';
+import { contextParamSchema } from './context.js';
export const JWT_PARAM_DEFAULT = ':jwtToken';
diff --git a/packages/taco/src/conditions/schemas/return-value-test.ts b/packages/taco/src/conditions/schemas/return-value-test.ts
index 17f168f6c..c48e15626 100644
--- a/packages/taco/src/conditions/schemas/return-value-test.ts
+++ b/packages/taco/src/conditions/schemas/return-value-test.ts
@@ -3,7 +3,7 @@ import { z } from 'zod';
import {
blockchainParamOrContextParamSchema,
paramOrContextParamSchema,
-} from './context';
+} from './context.js';
const returnValueTestBaseSchema = z.object({
index: z.number().int().nonnegative().optional(),
diff --git a/packages/taco/src/conditions/schemas/rpc.ts b/packages/taco/src/conditions/schemas/rpc.ts
index faf4c9da7..867c791a8 100644
--- a/packages/taco/src/conditions/schemas/rpc.ts
+++ b/packages/taco/src/conditions/schemas/rpc.ts
@@ -1,9 +1,9 @@
import { BlockIdentifierSchema, EthAddressSchema } from '@nucypher/shared';
import { z } from 'zod';
-import { baseConditionSchema, UserAddressSchema } from './common';
-import { contextParamSchema } from './context';
-import { blockchainReturnValueTestSchema } from './return-value-test';
+import { baseConditionSchema, UserAddressSchema } from './common.js';
+import { contextParamSchema } from './context.js';
+import { blockchainReturnValueTestSchema } from './return-value-test.js';
export const RpcConditionType = 'rpc';
diff --git a/packages/taco/src/conditions/schemas/sequential.ts b/packages/taco/src/conditions/schemas/sequential.ts
index 60942d047..e0cef964c 100644
--- a/packages/taco/src/conditions/schemas/sequential.ts
+++ b/packages/taco/src/conditions/schemas/sequential.ts
@@ -1,9 +1,9 @@
import { z } from 'zod';
-import { maxNestedDepth } from '../multi-condition';
+import { maxNestedDepth } from '../multi-condition.js';
-import { baseConditionSchema, plainStringSchema } from './common';
-import { anyConditionSchema } from './utils';
+import { baseConditionSchema, plainStringSchema } from './common.js';
+import { anyConditionSchema } from './utils.js';
export const SequentialConditionType = 'sequential';
diff --git a/packages/taco/src/conditions/schemas/time.ts b/packages/taco/src/conditions/schemas/time.ts
index 65e57ac95..012ddbfb9 100644
--- a/packages/taco/src/conditions/schemas/time.ts
+++ b/packages/taco/src/conditions/schemas/time.ts
@@ -1,6 +1,6 @@
import { z } from 'zod';
-import { rpcConditionSchema } from './rpc';
+import { rpcConditionSchema } from './rpc.js';
// TimeCondition is an RpcCondition with the method set to 'blocktime' and no parameters
diff --git a/packages/taco/src/conditions/schemas/utils.ts b/packages/taco/src/conditions/schemas/utils.ts
index abd14f36b..b08ed27b6 100644
--- a/packages/taco/src/conditions/schemas/utils.ts
+++ b/packages/taco/src/conditions/schemas/utils.ts
@@ -1,15 +1,15 @@
import { z } from 'zod';
-import { compoundConditionSchema } from '../compound-condition';
+import { compoundConditionSchema } from '../compound-condition.js';
-import { contractConditionSchema } from './contract';
-import { ifThenElseConditionSchema } from './if-then-else';
-import { jsonApiConditionSchema } from './json-api';
-import { jsonRpcConditionSchema } from './json-rpc';
-import { jwtConditionSchema } from './jwt';
-import { rpcConditionSchema } from './rpc';
-import { sequentialConditionSchema } from './sequential';
-import { timeConditionSchema } from './time';
+import { contractConditionSchema } from './contract.js';
+import { ifThenElseConditionSchema } from './if-then-else.js';
+import { jsonApiConditionSchema } from './json-api.js';
+import { jsonRpcConditionSchema } from './json-rpc.js';
+import { jwtConditionSchema } from './jwt.js';
+import { rpcConditionSchema } from './rpc.js';
+import { sequentialConditionSchema } from './sequential.js';
+import { timeConditionSchema } from './time.js';
export const anyConditionSchema: z.ZodSchema = z.lazy(() =>
z.union([
diff --git a/packages/taco/src/conditions/sequential.ts b/packages/taco/src/conditions/sequential.ts
index 65c111112..110aff2a7 100644
--- a/packages/taco/src/conditions/sequential.ts
+++ b/packages/taco/src/conditions/sequential.ts
@@ -1,17 +1,17 @@
-import { Condition } from './condition';
+import { Condition } from './condition.js';
import {
SequentialConditionProps,
sequentialConditionSchema,
SequentialConditionType,
-} from './schemas/sequential';
-import { OmitConditionType } from './shared';
+} from './schemas/sequential.js';
+import { OmitConditionType } from './shared.js';
export {
ConditionVariableProps,
SequentialConditionProps,
sequentialConditionSchema,
SequentialConditionType,
-} from './schemas/sequential';
+} from './schemas/sequential.js';
export class SequentialCondition extends Condition {
constructor(value: OmitConditionType) {
diff --git a/packages/taco/src/conditions/shared.ts b/packages/taco/src/conditions/shared.ts
index 1df855fed..8a054b581 100644
--- a/packages/taco/src/conditions/shared.ts
+++ b/packages/taco/src/conditions/shared.ts
@@ -3,11 +3,11 @@ export type OmitConditionType = Omit;
export {
contextParamSchema,
paramOrContextParamSchema,
-} from './schemas/context';
+} from './schemas/context.js';
export {
BlockchainReturnValueTestProps,
ReturnValueTestProps,
blockchainReturnValueTestSchema,
returnValueTestSchema,
-} from './schemas/return-value-test';
+} from './schemas/return-value-test.js';
diff --git a/packages/taco/src/index.ts b/packages/taco/src/index.ts
index 698685a1b..09b908491 100644
--- a/packages/taco/src/index.ts
+++ b/packages/taco/src/index.ts
@@ -9,6 +9,13 @@ export {
toHexString,
} from '@nucypher/shared';
-export * as conditions from './conditions';
+export * as conditions from './conditions/index.js';
-export { decrypt, encrypt, encryptWithPublicKey } from './taco';
+export { decrypt, encrypt, encryptWithPublicKey } from './taco.js';
+
+export {
+ AccessClient,
+ type AccessClientConfig,
+ type AccessClientEthersConfig,
+ type AccessClientViemConfig,
+} from './access-client/index.js';
diff --git a/packages/taco/src/taco.ts b/packages/taco/src/taco.ts
index da1fc9ce9..dfc1f2328 100644
--- a/packages/taco/src/taco.ts
+++ b/packages/taco/src/taco.ts
@@ -10,92 +10,156 @@ import {
fromHexString,
getPorterUris,
PorterClient,
+ ProviderLike,
+ PublicClient,
+ SignerAccount,
+ SignerLike,
toBytes,
+ toEthersProvider,
+ toTacoSigner,
} from '@nucypher/shared';
import { ethers } from 'ethers';
-import { keccak256 } from 'ethers/lib/utils';
-import { Condition } from './conditions/condition';
-import { ConditionExpression } from './conditions/condition-expr';
-import { ConditionContext } from './conditions/context';
-import { DkgClient } from './dkg';
-import { retrieveAndDecrypt } from './tdec';
+import { ConditionExpression } from './conditions/condition-expr.js';
+import { Condition } from './conditions/condition.js';
+import { ConditionContext } from './conditions/context/index.js';
+import { DkgClient } from './dkg.js';
+import { retrieveAndDecrypt } from './tdec.js';
/**
- * Encrypts a message under given conditions using a public key from an active DKG ritual.
+ * Encrypts a message gated by TACo Conditions using an ethers.js `Provider` and `Signer`.
+ *
+ * Use this overload when your application uses ethers.js.
*
* @export
- * @param {ethers.providers.Provider} provider - Instance of ethers provider which is used to interact with
- * your selected network.
- * @param {Domain} domain - Represents the logical network in which the encryption will be performed.
- * Must match the `ritualId`.
- * @param {Uint8Array | string} message - The message to be encrypted.
- * @param {Condition} condition - Condition under which the message will be encrypted. Those conditions must be
- * satisfied in order to decrypt the message.
- * @param {number} ritualId - The ID of the DKG Ritual to be used for encryption. The message will be encrypted
- * under the public key of this ritual.
- * @param {ethers.Signer} authSigner - The signer that will be used to sign the encrypter authorization.
- *
- * @returns {Promise} Returns Promise that resolves with an instance of ThresholdMessageKit.
- * It represents the encrypted message.
- *
- * @throws {Error} If the active DKG Ritual cannot be retrieved an error is thrown.
+ * @param {ethers.providers.Provider} provider - Ethers provider for network operations.
+ * @param {Domain} domain - Logical TACo domain in which encryption will be performed (must match the ritual's domain).
+ * @param {Uint8Array | string} message - The message to be encrypted.
+ * @param {Condition} condition - Access condition (single or composite) that must be satisfied at decryption time.
+ * @param {number} ritualId - ID of the DKG ritual whose public key will be used for encryption.
+ * @param {ethers.Signer} authSigner - Signer used to identify encryptor and verify authorization.
+ *
+ * @returns {Promise} Encrypted message kit representing the ciphertext and associated metadata.
+ *
+ * @throws {Error} If the ritual cannot be retrieved or encryption fails.
*/
-export const encrypt = async (
+// Function overloads for encrypt
+export async function encrypt(
provider: ethers.providers.Provider,
domain: Domain,
message: Uint8Array | string,
condition: Condition,
ritualId: number,
authSigner: ethers.Signer,
-): Promise => {
- // TODO(#264): Enable ritual initialization
- // if (ritualId === undefined) {
- // ritualId = await DkgClient.initializeRitual(
- // provider,
- // this.cohort.ursulaAddresses,
- // true
- // );
- // }
- // if (ritualId === undefined) {
- // // Given that we just initialized the ritual, this should never happen
- // throw new Error('Ritual ID is undefined');
- // }
- const dkgRitual = await DkgClient.getActiveRitual(provider, domain, ritualId);
+): Promise;
+
+/**
+ * Encrypts a message gated by TACo Conditions using a viem `PublicClient` and a Signer Account (`LocalAccount` or `WalletClient`).
+ *
+ * Use this overload when your application uses viem.
+ *
+ * @export
+ * @param {PublicClient} publicClient - Viem `PublicClient` for network operations.
+ * @param {Domain} domain - Logical TACo domain in which encryption will be performed (must match the ritual's domain).
+ * @param {Uint8Array | string} message - The message to be encrypted.
+ * @param {Condition} condition - Access condition (single or composite) that must be satisfied at decryption time.
+ * @param {number} ritualId - ID of the DKG ritual whose public key will be used for encryption.
+ * @param {SignerAccount} authAccount - Viem account used to identify encryptor and verify authorization.
+ *
+ * @returns {Promise} Encrypted message kit representing the ciphertext and associated metadata.
+ *
+ *
+ * @throws {Error} If the ritual cannot be retrieved or encryption fails.
+ */
+export async function encrypt(
+ publicClient: PublicClient,
+ domain: Domain,
+ message: Uint8Array | string,
+ condition: Condition,
+ ritualId: number,
+ authAccount: SignerAccount,
+): Promise;
+
+export async function encrypt(
+ providerLike: ProviderLike,
+ domain: Domain,
+ message: Uint8Array | string,
+ condition: Condition,
+ ritualId: number,
+ signerLike: SignerLike,
+): Promise {
+ // Create TACo provider and signer adapters from viem objects
+ const providerAdapter = toEthersProvider(providerLike);
+
+ const dkgRitual = await DkgClient.getActiveRitual(
+ providerAdapter,
+ domain,
+ ritualId,
+ );
return await encryptWithPublicKey(
message,
condition,
dkgRitual.dkgPublicKey,
- authSigner,
+ // Casting is needed because with the function definition of encryptWithPublicKey,
+ // this param can be either a Signer or a viem signer account. But not a type that is the union of both.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ signerLike as any,
);
-};
+}
/**
- * Encrypts a message with the given DKG public key under a specified condition.
+ * Encrypts a message with the given DKG public key gated by TACo Conditions.
*
* @export
- * @param {Uint8Array | string} message - The message to be encrypted.
- * @param {Condition} condition - Condition under which the message will be encrypted. Those conditions must be
- * satisfied in order to decrypt the message.
+ * @param {Uint8Array | string} message - The message to be encrypted.
+ * @param {Condition} condition - Access condition (single or composite) that must be satisfied at decryption time.
* @param {DkgPublicKey} dkgPublicKey - The public key of an active DKG Ritual to be used for encryption
- * @param {ethers.Signer} authSigner - The signer that will be used to sign the encrypter authorization.
+ * @param {Signer} authSigner - Signer used to identify encryptor and verify authorization. Accepts an ethers `Signer` or a viem Signer Account (`LocalAccount` or `WalletClient`).
*
- * @returns {Promise} Returns Promise that resolves with an instance of ThresholdMessageKit.
- * It represents the encrypted message.
+ * @returns {Promise} Encrypted message kit representing the ciphertext and associated metadata.
*
* @throws {Error} If the encryption process throws an error, an error is thrown.
*/
-export const encryptWithPublicKey = async (
+export async function encryptWithPublicKey(
message: Uint8Array | string,
condition: Condition,
dkgPublicKey: DkgPublicKey,
authSigner: ethers.Signer,
-): Promise => {
+): Promise;
+
+/**
+ * Encrypts a message with the given DKG public key gated by TACo Conditions.
+ *
+ * @export
+ * @param {Uint8Array | string} message - The message to be encrypted.
+ * @param {Condition} condition - Access condition (single or composite) that must be satisfied at decryption time.
+ * @param {DkgPublicKey} dkgPublicKey - The public key of an active DKG Ritual to be used for encryption
+ * @param {SignerAccount} authAccount - Viem account used to identify encryptor and verify authorization.
+ *
+ * @returns {Promise} Encrypted message kit representing the ciphertext and associated metadata.
+ *
+ * @throws {Error} If the encryption process throws an error, an error is thrown.
+ */
+export async function encryptWithPublicKey(
+ message: Uint8Array | string,
+ condition: Condition,
+ dkgPublicKey: DkgPublicKey,
+ authAccount: SignerAccount,
+): Promise;
+
+export async function encryptWithPublicKey(
+ message: Uint8Array | string,
+ condition: Condition,
+ dkgPublicKey: DkgPublicKey,
+ signerLike: SignerLike,
+): Promise {
if (typeof message === 'string') {
message = toBytes(message);
}
+ const signer = toTacoSigner(signerLike);
+
const conditionExpr = new ConditionExpression(condition);
const [ciphertext, authenticatedData] = encryptForDkg(
@@ -104,57 +168,85 @@ export const encryptWithPublicKey = async (
conditionExpr.toCoreCondition(),
);
- const headerHash = keccak256(ciphertext.header.toBytes());
- const authorization = await authSigner.signMessage(fromHexString(headerHash));
+ const headerHash = ethers.utils.keccak256(ciphertext.header.toBytes());
+ const authorization = await signer.signMessage(fromHexString(headerHash));
const acp = new AccessControlPolicy(
authenticatedData,
fromHexString(authorization),
);
return new ThresholdMessageKit(ciphertext, acp);
-};
+}
/**
- * Decrypts an encrypted message.
+ * Decrypts an encrypted message (ethers overload).
*
* @export
- * @param {ethers.providers.Provider} provider - Instance of ethers provider which is used to interact with
- * your selected network.
- * @param {Domain} domain - Represents the logical network in which the decryption will be performed.
- * Must match the `ritualId`.
- * @param {ThresholdMessageKit} messageKit - The kit containing the message to be decrypted
- * @param {ConditionContext} context - Optional context data used for decryption time values for the condition(s) within the `messageKit`.
- * @param {string[]} [porterUris] - Optional URI(s) for the Porter service. If not provided, a value will be obtained
- * from the Domain
- *
- * @returns {Promise} Returns Promise that resolves with a decrypted message
- *
- * @throws {Error} If the active DKG Ritual cannot be retrieved or decryption process throws an error,
- * an error is thrown.
+ * @param {ethers.providers.Provider} provider - Ethers provider for network operations.
+ * @param {Domain} domain - Logical TACo domain used for decryption.
+ * @param {ThresholdMessageKit} messageKit - The representation of the ciphertext and associated metadata.
+ * @param {ConditionContext} [context] - Optional context data required by conditions.
+ * @param {string[]} [porterUris] - Optional Porter service URI(s). If omitted, they are resolved via `getPorterUris(domain)`.
+ *
+ * @returns {Promise} The decrypted message bytes.
+ *
+ * @throws {Error} If the ritual cannot be resolved, Porter retrieval fails, or decryption fails.
*/
-export const decrypt = async (
+export function decrypt(
provider: ethers.providers.Provider,
domain: Domain,
messageKit: ThresholdMessageKit,
context?: ConditionContext,
porterUris?: string[],
-): Promise => {
+): Promise;
+
+/**
+ * Decrypts an encrypted message (viem overload).
+ *
+ * @export
+ * @param {PublicClient} publicClient - Viem `PublicClient` for network operations.
+ * @param {Domain} domain - Logical TACo domain used for decryption.
+ * @param {ThresholdMessageKit} messageKit - The kit containing the ciphertext and access policy.
+ * @param {ConditionContext} [context] - Optional context data required by conditions.
+ * @param {string[]} [porterUris] - Optional Porter service URI(s). If omitted, they are resolved via `getPorterUris(domain)`.
+ *
+ * @returns {Promise} The decrypted message bytes.
+ *
+ * @throws {Error} If the ritual cannot be resolved, Porter retrieval fails, or decryption fails.
+ */
+export function decrypt(
+ publicClient: PublicClient,
+ domain: Domain,
+ messageKit: ThresholdMessageKit,
+ context?: ConditionContext,
+ porterUris?: string[],
+): Promise;
+
+export async function decrypt(
+ providerLike: ProviderLike,
+ domain: Domain,
+ messageKit: ThresholdMessageKit,
+ context?: ConditionContext,
+ porterUris?: string[],
+): Promise {
const porterUrisFull: string[] = porterUris
? porterUris
: await getPorterUris(domain);
const porter = new PorterClient(porterUrisFull);
+ const providerAdapter = toEthersProvider(providerLike);
+
const ritualId = await DkgCoordinatorAgent.getRitualIdFromPublicKey(
- provider,
+ providerAdapter,
domain,
messageKit.acp.publicKey,
);
return retrieveAndDecrypt(
- provider,
+ providerAdapter,
domain,
porter,
messageKit,
ritualId,
context,
);
-};
+}
diff --git a/packages/taco/src/tdec.ts b/packages/taco/src/tdec.ts
index 97132437d..649eda8bd 100644
--- a/packages/taco/src/tdec.ts
+++ b/packages/taco/src/tdec.ts
@@ -20,11 +20,10 @@ import {
toBytes,
} from '@nucypher/shared';
import { ethers } from 'ethers';
-import { arrayify, keccak256 } from 'ethers/lib/utils';
-import { ConditionExpression } from './conditions/condition-expr';
-import { ConditionContext } from './conditions/context';
-import { DkgClient } from './dkg';
+import { ConditionExpression } from './conditions/condition-expr.js';
+import { ConditionContext } from './conditions/context/index.js';
+import { DkgClient } from './dkg.js';
const ERR_DECRYPTION_FAILED = (errors: unknown) =>
`Threshold of responses not met; TACo decryption failed with errors: ${JSON.stringify(
@@ -47,8 +46,10 @@ export const encryptMessage = async (
conditions.toCoreCondition(),
);
- const headerHash = keccak256(ciphertext.header.toBytes());
- const authorization = await authSigner.signMessage(arrayify(headerHash));
+ const headerHash = ethers.utils.keccak256(ciphertext.header.toBytes());
+ const authorization = await authSigner.signMessage(
+ ethers.utils.arrayify(headerHash),
+ );
const acp = new AccessControlPolicy(
authenticatedData,
toBytes(authorization),
diff --git a/packages/taco/test/access-client.test.ts b/packages/taco/test/access-client.test.ts
new file mode 100644
index 000000000..a44e650f1
--- /dev/null
+++ b/packages/taco/test/access-client.test.ts
@@ -0,0 +1,387 @@
+import { DOMAIN_NAMES, DomainName } from '@nucypher/shared';
+import {
+ fakeProvider,
+ fakeViemAccount,
+ fakeViemPublicClient,
+} from '@nucypher/test-utils';
+import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+
+import {
+ AccessClient,
+ type AccessClientConfig,
+ type AccessClientEthersConfig,
+ type AccessClientViemConfig,
+} from '../src';
+import { AccessConfigValidator } from '../src/access-client/config-validator';
+
+describe('AccessConfigValidator', () => {
+ describe('Domain Management', () => {
+ it('should return all supported domain names', () => {
+ const domains = AccessConfigValidator.getSupportedDomains();
+ expect(domains).toEqual(['lynx', 'tapir', 'mainnet']);
+ });
+
+ it.each([
+ [DOMAIN_NAMES.TESTNET, 'valid testnet domain'],
+ [DOMAIN_NAMES.DEVNET, 'valid devnet domain'],
+ [DOMAIN_NAMES.MAINNET, 'valid production domain'],
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ])('should validate domain "%s" as %s', (domain: DomainName, _: string) => {
+ expect(AccessConfigValidator.isValidDomain(domain)).toBe(true);
+ });
+
+ it.each([
+ ['INVALID', 'invalid domain name'],
+ ['', 'empty domain name'],
+ ['testnet', 'legacy domain key (not domain name)'],
+ ])('should validate domain "%s" as %s', (domain: string) => {
+ expect(AccessConfigValidator.isValidDomain(domain as DomainName)).toBe(
+ false,
+ );
+ });
+
+ it.each([
+ [0, 'minimum valid ritual ID'],
+ [27, 'default devnet ritual ID'],
+ [6, 'default testnet ritual ID'],
+ [42, 'custom mainnet ritual ID'],
+ [999, 'large ritual ID for devnet'],
+ ])('should validate ritual ID %d (%s)', (ritualId: number) => {
+ expect(AccessConfigValidator.isValidRitualId(ritualId)).toBe(true);
+ });
+
+ it.each([
+ [-1, 'negative ritual ID'],
+ [5.4, 'floating point ritual ID'],
+ ])('should invalidate ritual ID %d (%s)', (ritualId: number) => {
+ expect(AccessConfigValidator.isValidRitualId(ritualId)).toBe(false);
+ });
+ });
+
+ describe('Fast Configuration Validation', () => {
+ it('should create AccessClient with viem configuration', () => {
+ const result = AccessConfigValidator.validateFast({
+ domain: 'tapir',
+ ritualId: 6,
+ viemClient: fakeViemPublicClient(),
+ viemSignerAccount: fakeViemAccount(),
+ });
+
+ expect(result.isValid).toBe(true);
+ expect(result.errors).toHaveLength(0);
+ });
+
+ it('should fail validation for invalid domain configuration', () => {
+ const result = AccessConfigValidator.validateFast({
+ domain: 'INVALID_DOMAIN' as DomainName,
+ ritualId: 999,
+ viemClient: fakeViemPublicClient(),
+ viemSignerAccount: fakeViemAccount(),
+ });
+
+ expect(result.isValid).toBe(false);
+ expect(result.errors.length).toBeGreaterThan(0);
+ });
+
+ it('should fail validation when domain is missing', () => {
+ const result = AccessConfigValidator.validateFast({
+ ritualId: 6,
+ viemClient: fakeViemPublicClient(),
+ viemSignerAccount: fakeViemAccount(),
+ } as AccessClientConfig);
+
+ expect(result.isValid).toBe(false);
+ expect(result.errors).toContain('The property `domain` is required');
+ });
+ });
+});
+
+// Test helpers for accessing AccessClient's private static members
+const getAccessClientStatics = () =>
+ AccessClient as unknown as {
+ initializationPromise: Promise | undefined;
+ };
+
+const resetAccessClientStatics = () => {
+ delete (AccessClient as unknown as { initializationPromise?: Promise })
+ .initializationPromise;
+};
+
+describe('AccessClient', () => {
+ beforeAll(async () => {
+ // Ensure AccessClient is initialized before running tests
+ await AccessClient.initialize();
+ });
+
+ let validViemConfig: AccessClientViemConfig;
+ let validEthersConfig: AccessClientEthersConfig;
+
+ beforeEach(() => {
+ validViemConfig = {
+ domain: 'tapir',
+ ritualId: 6,
+ viemClient: fakeViemPublicClient(),
+ viemSignerAccount: fakeViemAccount(),
+ };
+
+ const ethersProvider = fakeProvider();
+ validEthersConfig = {
+ domain: 'tapir',
+ ritualId: 6,
+ ethersProvider,
+ ethersSigner: ethersProvider.getSigner(),
+ };
+ });
+
+ describe('Client Construction', () => {
+ it('should successfully create client with valid viem configuration', async () => {
+ const client = new AccessClient(validViemConfig);
+ await client.validateConfig();
+ expect(client).toBeInstanceOf(AccessClient);
+ });
+
+ it('should successfully create client with valid ethers configuration', async () => {
+ const client = new AccessClient(validEthersConfig);
+ await client.validateConfig();
+ expect(client).toBeInstanceOf(AccessClient);
+ });
+
+ it('should throw error for invalid domain name', () => {
+ expect(
+ () =>
+ new AccessClient({
+ ...validViemConfig,
+ domain: 'INVALID' as DomainName,
+ }),
+ ).toThrow('Invalid domain name');
+ });
+
+ it('should throw error for invalid ritual ID', () => {
+ expect(
+ () =>
+ new AccessClient({
+ ...validViemConfig,
+ ritualId: -1,
+ }),
+ ).toThrow('Invalid ritual ID');
+ });
+
+ it.each([
+ {
+ configModifications: { domain: undefined },
+ baseConfig: 'viem',
+ expectedError: 'The property `domain` is required',
+ description: 'missing domain from viem config',
+ },
+ {
+ configModifications: { ritualId: undefined },
+ baseConfig: 'viem',
+ expectedError: 'The property `ritualId` is required',
+ description: 'missing ritual ID from viem config',
+ },
+ {
+ configModifications: { viemClient: undefined },
+ baseConfig: 'viem',
+ expectedError: 'viemClient is required for viem configuration',
+ description: 'missing viemClient from viem config',
+ },
+ {
+ configModifications: { viemSignerAccount: undefined },
+ baseConfig: 'viem',
+ expectedError: 'viemSignerAccount is required for viem configuration',
+ description: 'missing viemSignerAccount from viem config',
+ },
+ {
+ configModifications: { domain: undefined },
+ baseConfig: 'ethers',
+ expectedError: 'The property `domain` is required',
+ description: 'missing domain from ethers config',
+ },
+ {
+ configModifications: { ritualId: undefined },
+ baseConfig: 'ethers',
+ expectedError: 'The property `ritualId` is required',
+ description: 'missing ritual ID from ethers config',
+ },
+ {
+ configModifications: { ethersProvider: undefined },
+ baseConfig: 'ethers',
+ expectedError: 'ethersProvider is required for ethers configuration',
+ description: 'missing ethersProvider from ethers config',
+ },
+ {
+ configModifications: { ethersSigner: undefined },
+ baseConfig: 'ethers',
+ expectedError: 'ethersSigner is required for ethers configuration',
+ description: 'missing ethersSigner from ethers config',
+ },
+ ])(
+ 'should throw error for $description',
+ ({ configModifications, baseConfig, expectedError }) => {
+ const baseConfigObject =
+ baseConfig === 'viem' ? validViemConfig : validEthersConfig;
+ const invalidConfig = { ...baseConfigObject, ...configModifications };
+
+ expect(
+ () => new AccessClient(invalidConfig as AccessClientConfig),
+ ).toThrow(expectedError);
+ },
+ );
+
+ it('should throw error for mixed/invalid configuration types', () => {
+ expect(
+ () =>
+ new AccessClient({
+ domain: 'tapir',
+ ritualId: 6,
+ viemClient: fakeViemPublicClient(),
+ ethersProvider: fakeProvider(),
+ } as unknown as AccessClientConfig),
+ ).toThrow(
+ 'Invalid configuration: Configuration must include either viem objects (viemClient + viemSignerAccount) or ethers objects (ethersProvider + ethersSigner)',
+ );
+ });
+ });
+
+ describe('Configuration Access', () => {
+ it.each([
+ {
+ configType: 'viem',
+ config: () => validViemConfig,
+ expectedProperties: ['viemClient', 'viemSignerAccount'],
+ description: 'viem client configuration',
+ },
+ {
+ configType: 'ethers',
+ config: () => validEthersConfig,
+ expectedProperties: ['ethersProvider', 'ethersSigner'],
+ description: 'ethers client configuration',
+ },
+ ])(
+ 'should return readonly configuration object for $description',
+ ({ config, expectedProperties }) => {
+ const client = new AccessClient(config());
+ const clientConfig = client.getConfig();
+
+ // Verify common properties
+ expect(clientConfig.domain).toBe('tapir');
+ expect(clientConfig.ritualId).toBe(6);
+
+ // Verify config-specific properties
+ expectedProperties.forEach((prop) => {
+ expect(prop in clientConfig).toBe(true);
+ });
+
+ // Should be frozen/readonly
+ expect(() => {
+ (clientConfig as Record).domain = 'lynx';
+ }).toThrow();
+ },
+ );
+ });
+
+ describe('Initialization Lifecycle', () => {
+ it('should trigger automatic AccessClient initialization on client construction', async () => {
+ // Reset static initialization state to verify automatic initialization
+ // occurs when AccessClient constructor is called
+ resetAccessClientStatics();
+
+ new AccessClient(validViemConfig);
+
+ // Initialization should be triggered by constructor
+ expect(getAccessClientStatics().initializationPromise).toBeDefined();
+ });
+
+ it('should share initialization across multiple AccessClient instances', async () => {
+ new AccessClient(validViemConfig);
+ new AccessClient({
+ ...validViemConfig,
+ ritualId: 27, // Different ritual ID
+ });
+
+ // Both clients should share the same initialization promise
+ const initPromise1 = getAccessClientStatics().initializationPromise;
+ const initPromise2 = getAccessClientStatics().initializationPromise;
+
+ expect(initPromise1).toBe(initPromise2);
+ expect(initPromise1).toBeDefined();
+ });
+
+ it('should provide static initialize method with proper promise handling', async () => {
+ // Verify AccessClient.initialize() method exists and returns a promise
+ const initPromise = AccessClient.initialize();
+ expect(initPromise).toBeInstanceOf(Promise);
+
+ // Wait for initialization to complete
+ await initPromise;
+
+ // Verify that repeated calls return the same promise (singleton pattern)
+ const initPromise2 = AccessClient.initialize();
+ expect(initPromise2).toBeInstanceOf(Promise);
+ });
+ });
+
+ describe('Full Configuration Validation', () => {
+ it.each([
+ {
+ configType: 'viem',
+ config: () => validViemConfig,
+ description: 'correct viem configuration',
+ },
+ {
+ configType: 'ethers',
+ config: () => validEthersConfig,
+ description: 'correct ethers configuration',
+ },
+ ])('should pass full validation for $description', async ({ config }) => {
+ const result = AccessConfigValidator.validate(config());
+ expect(result).resolves.not.toThrow();
+ });
+
+ it('should detect and report missing blockchain dependencies', async () => {
+ const result = await AccessConfigValidator.validate({
+ domain: 'tapir',
+ ritualId: 6,
+ // Missing blockchain objects
+ } as unknown as AccessClientConfig);
+
+ expect(result.isValid).toBe(false);
+ expect(result.errors).toContain(
+ 'Configuration must include either viem objects (viemClient + viemSignerAccount) or ethers objects (ethersProvider + ethersSigner)',
+ );
+ });
+
+ it('should detect and report invalid domain in full validation', async () => {
+ const result = await AccessConfigValidator.validate({
+ ...validViemConfig,
+ domain: 'INVALID' as DomainName,
+ });
+
+ expect(result.isValid).toBe(false);
+ expect(
+ result.errors.some((error) => error.includes('Invalid domain name')),
+ ).toBe(true);
+ });
+
+ it('should detect and report invalid ritual ID during construction', async () => {
+ expect(
+ () =>
+ new AccessClient({
+ domain: 'tapir',
+ ritualId: -5,
+ viemClient: fakeViemPublicClient(),
+ viemSignerAccount: fakeViemAccount(),
+ }),
+ ).toThrow('Invalid ritual ID');
+ });
+ });
+
+ describe('Domain Support', () => {
+ it('should provide domain name via getConfig method', () => {
+ const client = new AccessClient(validViemConfig);
+ const config = client.getConfig();
+
+ expect(config.domain).toBe('tapir');
+ });
+ });
+});
diff --git a/packages/taco/test/taco.test.ts b/packages/taco/test/taco.test.ts
index e035f211f..89260b847 100644
--- a/packages/taco/test/taco.test.ts
+++ b/packages/taco/test/taco.test.ts
@@ -11,11 +11,15 @@ import {
fakePorterUri,
fakeProvider,
fakeTDecFlow,
+ fakeViemAccount,
+ fakeViemPublicClient,
mockGetRitualIdFromPublicKey,
mockTacoDecrypt,
TEST_CHAIN_ID,
TEST_SIWE_PARAMS,
} from '@nucypher/test-utils';
+import { ethers } from 'ethers';
+import type { LocalAccount, PublicClient } from 'viem';
import { beforeAll, describe, expect, it } from 'vitest';
import * as taco from '../src';
@@ -30,110 +34,170 @@ import {
mockMakeSessionKey,
} from './test-utils';
-// Shared test variables
-const message = 'this is a secret';
+// Test fixtures
+const TEST_MESSAGE = 'this is a secret';
+const TEST_NFT_CONTRACT = '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77';
+const TEST_NFT_TOKEN_ID = 3591;
+
const ownsNFT = new conditions.predefined.erc721.ERC721Ownership({
- contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
- parameters: [3591],
+ contractAddress: TEST_NFT_CONTRACT,
+ parameters: [TEST_NFT_TOKEN_ID],
chain: TEST_CHAIN_ID,
});
-describe('taco', () => {
+describe('TACo SDK', () => {
beforeAll(async () => {
await initialize();
});
- it('encrypts and decrypts', async () => {
- const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4);
- const mockedDkgRitual = fakeDkgRitual(mockedDkg);
- const provider = fakeProvider(aliceSecretKeyBytes);
- const signer = provider.getSigner();
- const getFinalizedRitualSpy = mockGetActiveRitual(mockedDkgRitual);
-
- const messageKit = await taco.encrypt(
- provider,
- domains.DEVNET,
- message,
- ownsNFT,
- mockedDkg.ritualId,
- signer,
- );
- expect(getFinalizedRitualSpy).toHaveBeenCalled();
-
- const { decryptionShares } = fakeTDecFlow({
- ...mockedDkg,
- message: toBytes(message),
- dkgPublicKey: mockedDkg.dkg.publicKey(),
- thresholdMessageKit: messageKit,
- });
- const { participantSecrets, participants } = await mockDkgParticipants(
- mockedDkg.ritualId,
- );
- const requesterSessionKey = SessionStaticSecret.random();
- const decryptSpy = mockTacoDecrypt(
- mockedDkg.ritualId,
- decryptionShares,
- participantSecrets,
- requesterSessionKey.publicKey(),
- );
- const getParticipantsSpy = mockGetParticipants(participants);
- const sessionKeySpy = mockMakeSessionKey(requesterSessionKey);
- const getRitualIdFromPublicKey = mockGetRitualIdFromPublicKey(
- mockedDkg.ritualId,
- );
- const getRitualSpy = mockGetActiveRitual(mockedDkgRitual);
-
- const authProvider = new tacoAuth.EIP4361AuthProvider(
- provider,
- signer,
- TEST_SIWE_PARAMS,
- );
- const conditionContext = ConditionContext.fromMessageKit(messageKit);
- conditionContext.addAuthProvider(USER_ADDRESS_PARAM_DEFAULT, authProvider);
- const decryptedMessage = await taco.decrypt(
- provider,
- domains.DEVNET,
- messageKit,
- conditionContext,
- [fakePorterUri],
- );
- expect(decryptedMessage).toEqual(toBytes(message));
- expect(getParticipantsSpy).toHaveBeenCalled();
- expect(sessionKeySpy).toHaveBeenCalled();
- expect(getRitualIdFromPublicKey).toHaveBeenCalled();
- expect(getRitualSpy).toHaveBeenCalled();
- expect(decryptSpy).toHaveBeenCalled();
- }, 9000); // test timeout 9s (TODO: not sure why this test takes so long on CI)
-
- it('exposes requested parameters', async () => {
- const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4);
- const mockedDkgRitual = fakeDkgRitual(mockedDkg);
- const provider = fakeProvider(aliceSecretKeyBytes);
- const signer = provider.getSigner();
- const getFinalizedRitualSpy = mockGetActiveRitual(mockedDkgRitual);
-
- const customParamKey = ':nftId';
- const ownsNFTWithCustomParams =
- new conditions.predefined.erc721.ERC721Ownership({
- contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77',
- parameters: [customParamKey],
- chain: TEST_CHAIN_ID,
+ describe.each<
+ | ['ethers', () => ethers.providers.Provider, () => ethers.Signer]
+ | ['viem', () => PublicClient, () => LocalAccount]
+ >([
+ [
+ 'ethers',
+ () => fakeProvider(aliceSecretKeyBytes),
+ () => fakeProvider(aliceSecretKeyBytes).getSigner(),
+ ],
+ [
+ 'viem',
+ () => fakeViemPublicClient(),
+ () => fakeViemAccount(aliceSecretKeyBytes),
+ ],
+ ])('Provider: %s', (providerType, createProvider, createSigner) => {
+ it('should encrypt and decrypt a message with conditions', async () => {
+ // Setup
+ const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4);
+ const mockedDkgRitual = fakeDkgRitual(mockedDkg);
+ const provider = createProvider();
+ const signer = createSigner();
+ const getFinalizedRitualSpy = mockGetActiveRitual(mockedDkgRitual);
+
+ // Encrypt
+ const messageKit = await taco.encrypt(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ provider as any,
+ domains.DEVNET,
+ TEST_MESSAGE,
+ ownsNFT,
+ mockedDkg.ritualId,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ signer as any,
+ );
+ expect(getFinalizedRitualSpy).toHaveBeenCalled();
+
+ // Setup decryption mocks
+ const { decryptionShares } = fakeTDecFlow({
+ ...mockedDkg,
+ message: toBytes(TEST_MESSAGE),
+ dkgPublicKey: mockedDkg.dkg.publicKey(),
+ thresholdMessageKit: messageKit,
});
+ const { participantSecrets, participants } = await mockDkgParticipants(
+ mockedDkg.ritualId,
+ );
+ const requesterSessionKey = SessionStaticSecret.random();
+ const decryptSpy = mockTacoDecrypt(
+ mockedDkg.ritualId,
+ decryptionShares,
+ participantSecrets,
+ requesterSessionKey.publicKey(),
+ );
+ const getParticipantsSpy = mockGetParticipants(participants);
+ const sessionKeySpy = mockMakeSessionKey(requesterSessionKey);
+ const getRitualIdFromPublicKey = mockGetRitualIdFromPublicKey(
+ mockedDkg.ritualId,
+ );
+ const getRitualSpy = mockGetActiveRitual(mockedDkgRitual);
+
+ // Setup authentication
+ const authProvider = new tacoAuth.EIP4361AuthProvider(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ provider as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ signer as any,
+ TEST_SIWE_PARAMS,
+ );
+ const conditionContext = ConditionContext.fromMessageKit(messageKit);
+ conditionContext.addAuthProvider(
+ USER_ADDRESS_PARAM_DEFAULT,
+ authProvider,
+ );
+
+ // Decrypt
+ const decryptedMessage = await taco.decrypt(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ provider as any,
+ domains.DEVNET,
+ messageKit,
+ conditionContext,
+ [fakePorterUri],
+ );
+
+ // Verify
+ expect(decryptedMessage).toEqual(toBytes(TEST_MESSAGE));
+ expect(getParticipantsSpy).toHaveBeenCalled();
+ expect(sessionKeySpy).toHaveBeenCalled();
+ expect(getRitualIdFromPublicKey).toHaveBeenCalled();
+ expect(getRitualSpy).toHaveBeenCalled();
+ expect(decryptSpy).toHaveBeenCalled();
+ }, 15000); // Extended timeout for CI
+
+ it('should handle custom condition parameters', async () => {
+ // Setup
+ const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4);
+ const mockedDkgRitual = fakeDkgRitual(mockedDkg);
+ const provider = createProvider();
+ const signer = createSigner();
+ const getFinalizedRitualSpy = mockGetActiveRitual(mockedDkgRitual);
- const messageKit = await taco.encrypt(
- provider,
- domains.DEVNET,
- message,
- ownsNFTWithCustomParams,
- mockedDkg.ritualId,
- signer,
- );
- expect(getFinalizedRitualSpy).toHaveBeenCalled();
-
- const conditionContext = ConditionContext.fromMessageKit(messageKit);
- const requestedParameters = conditionContext.requestedContextParameters;
- expect(requestedParameters).toEqual(
- new Set([customParamKey, USER_ADDRESS_PARAM_DEFAULT]),
- );
+ // Create condition with custom parameter
+ const customParamKey = ':nftId';
+ const ownsNFTWithCustomParams =
+ new conditions.predefined.erc721.ERC721Ownership({
+ contractAddress: TEST_NFT_CONTRACT,
+ parameters: [customParamKey],
+ chain: TEST_CHAIN_ID,
+ });
+
+ // Encrypt with custom condition
+ const messageKit = await taco.encrypt(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ provider as any,
+ domains.DEVNET,
+ TEST_MESSAGE,
+ ownsNFTWithCustomParams,
+ mockedDkg.ritualId,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ signer as any,
+ );
+ expect(getFinalizedRitualSpy).toHaveBeenCalled();
+
+ // Verify parameters are exposed
+ const conditionContext = ConditionContext.fromMessageKit(messageKit);
+ const requestedParameters = conditionContext.requestedContextParameters;
+ expect(requestedParameters).toEqual(
+ new Set([customParamKey, USER_ADDRESS_PARAM_DEFAULT]),
+ );
+ });
+
+ it('should encrypt with public key directly', async () => {
+ // Setup
+ const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4);
+ const signer = createSigner();
+
+ // Encrypt with public key
+ const messageKit = await taco.encryptWithPublicKey(
+ TEST_MESSAGE,
+ ownsNFT,
+ mockedDkg.dkg.publicKey(),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ signer as any,
+ );
+
+ // Verify
+ expect(messageKit).toBeDefined();
+ expect(messageKit).toBeInstanceOf(Object);
+ });
});
});
diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json
index 1295e2594..e4ac8c5a5 100644
--- a/packages/test-utils/package.json
+++ b/packages/test-utils/package.json
@@ -10,6 +10,7 @@
"author": "Piotr Roslaniec ",
"exports": {
".": {
+ "types": "./dist/cjs/index.d.ts",
"import": "./dist/es/index.js",
"require": "./dist/cjs/index.js"
}
@@ -21,9 +22,9 @@
"dist"
],
"scripts": {
- "build": "pnpm build:module && pnpm build:cjs",
+ "build": "pnpm build:es && pnpm build:cjs",
"build:cjs": "tsc --build ./tsconfig.cjs.json --verbose",
- "build:module": "tsc --build ./tsconfig.es.json --verbose",
+ "build:es": "tsc --build ./tsconfig.es.json --verbose",
"exports:lint": "ts-unused-exports tsconfig.json --ignoreFiles src/index.ts",
"lint": "eslint --ext .ts src",
"lint:fix": "pnpm lint --fix"
@@ -34,6 +35,7 @@
"@nucypher/taco-auth": "workspace:*",
"axios": "^1.11.0",
"ethers": "^5.8.0",
+ "viem": "^2.0.0",
"vitest": "^3.0.9"
},
"engines": {
diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts
index dc328f23c..24e4e4ed1 100644
--- a/packages/test-utils/src/utils.ts
+++ b/packages/test-utils/src/utils.ts
@@ -45,6 +45,9 @@ import {
SingleSignOnEIP4361AuthProvider,
} from '@nucypher/taco-auth';
import { ethers, providers, Wallet } from 'ethers';
+import { createPublicClient, custom, PublicClient } from 'viem';
+import { LocalAccount, privateKeyToAccount } from 'viem/accounts';
+import { polygonAmoy } from 'viem/chains';
import { expect, MockInstance, vi } from 'vitest';
import { TEST_CONTRACT_ADDR, TEST_SIWE_PARAMS } from './variables';
@@ -73,7 +76,7 @@ const makeFakeProvider = (
getBlockNumber: () => Promise.resolve(blockNumber),
getBlock: () => Promise.resolve(block),
_isProvider: true,
- getNetwork: () => Promise.resolve({ name: 'mockNetwork', chainId: 1234 }),
+ getNetwork: () => Promise.resolve({ name: 'mockNetwork', chainId: 80_002 }),
};
};
@@ -99,6 +102,33 @@ export const fakeProvider = (
} as unknown as ethers.providers.Web3Provider;
};
+// Viem test utilities
+export const fakeViemPublicClient = (): PublicClient => {
+ // Create public client for reading data
+ const publicClient = createPublicClient({
+ chain: polygonAmoy,
+ transport: custom({
+ request: vi.fn().mockImplementation(async ({ method }) => {
+ // Network detection calls
+ if (method === 'eth_chainId') {
+ return `0x${polygonAmoy.id.toString(16)}`;
+ }
+ // Default response for other calls
+ return null;
+ }),
+ }),
+ });
+ return publicClient;
+};
+
+export const fakeViemAccount = (
+ secretKeyBytes = ethers.utils.randomBytes(32),
+): LocalAccount => {
+ // Convert bytes to hex string for viem
+ const privateKey = `0x${Buffer.from(secretKeyBytes).toString('hex')}`;
+ return privateKeyToAccount(privateKey as `0x${string}`);
+};
+
export const fakeAuthProviders = async (
signer?: ethers.providers.JsonRpcSigner,
) => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 558fd750b..518cc3e1a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -39,7 +39,7 @@ importers:
version: 6.21.0(eslint@8.57.1)(typescript@5.8.2)
'@vitest/coverage-v8':
specifier: ^3.2.4
- version: 3.2.4(vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0))
+ version: 3.2.4(vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0))
bundlemon:
specifier: ^3.1.0
version: 3.1.0(typescript@5.8.2)
@@ -88,6 +88,9 @@ importers:
ts-unused-exports:
specifier: ^10.1.0
version: 10.1.0(typescript@5.8.2)
+ tsx:
+ specifier: ^4.20.5
+ version: 4.20.6
typedoc:
specifier: ^0.25.13
version: 0.25.13(typescript@5.8.2)
@@ -105,7 +108,7 @@ importers:
version: 5.8.2
vitest:
specifier: ^3.0.9
- version: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0)
+ version: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
demos/taco-demo:
dependencies:
@@ -129,7 +132,7 @@ importers:
version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
file-loader:
specifier: ^6.2.0
- version: 6.2.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 6.2.0(webpack@5.101.3)
react:
specifier: ^18.3.1
version: 18.3.1
@@ -145,7 +148,7 @@ importers:
devDependencies:
'@pmmmwh/react-refresh-webpack-plugin':
specifier: ^0.6.0
- version: 0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack-cli@6.0.1)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2)(webpack@5.101.3)
'@types/react':
specifier: ^18.3.20
version: 18.3.20
@@ -157,16 +160,16 @@ importers:
version: 18.3.5(@types/react@18.3.20)
copy-webpack-plugin:
specifier: ^13.0.0
- version: 13.0.1(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 13.0.1(webpack@5.101.3)
crypto-browserify:
specifier: ^3.12.1
version: 3.12.1
esbuild-loader:
specifier: ^2.21.0
- version: 2.21.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 2.21.0(webpack@5.101.3)
html-webpack-plugin:
specifier: ^5.6.3
- version: 5.6.3(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 5.6.3(webpack@5.101.3)
process:
specifier: ^0.11.10
version: 0.11.10
@@ -214,7 +217,7 @@ importers:
version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
file-loader:
specifier: ^6.2.0
- version: 6.2.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 6.2.0(webpack@5.101.3)
react:
specifier: ^18.3.1
version: 18.3.1
@@ -230,7 +233,7 @@ importers:
devDependencies:
'@pmmmwh/react-refresh-webpack-plugin':
specifier: ^0.6.0
- version: 0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack-cli@6.0.1)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2)(webpack@5.101.3)
'@types/react':
specifier: ^18.3.20
version: 18.3.20
@@ -242,13 +245,13 @@ importers:
version: 18.3.5(@types/react@18.3.20)
copy-webpack-plugin:
specifier: ^13.0.0
- version: 13.0.1(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 13.0.1(webpack@5.101.3)
esbuild-loader:
specifier: ^2.21.0
- version: 2.21.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 2.21.0(webpack@5.101.3)
html-webpack-plugin:
specifier: ^5.6.3
- version: 5.6.3(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 5.6.3(webpack@5.101.3)
react-refresh:
specifier: ^0.17.0
version: 0.17.0
@@ -352,10 +355,10 @@ importers:
devDependencies:
copy-webpack-plugin:
specifier: ^13.0.0
- version: 13.0.1(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 13.0.1(webpack@5.101.3)
esbuild-loader:
specifier: ^2.21.0
- version: 2.21.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 2.21.0(webpack@5.101.3)
ethers:
specifier: ^5.8.0
version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
@@ -460,6 +463,43 @@ importers:
specifier: ^5.0.1
version: 5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(type-fest@0.21.3)(typescript@5.8.2)(utf-8-validate@5.0.10)
+ examples/taco/react-viem:
+ dependencies:
+ '@nucypher/shared':
+ specifier: workspace:*
+ version: link:../../../packages/shared
+ '@nucypher/taco':
+ specifier: workspace:*
+ version: link:../../../packages/taco
+ '@nucypher/taco-auth':
+ specifier: workspace:*
+ version: link:../../../packages/taco-auth
+ ethers:
+ specifier: ^5.7.2
+ version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ viem:
+ specifier: ^2.0.0
+ version: 2.37.8(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+ devDependencies:
+ '@types/node':
+ specifier: ^20.17.28
+ version: 20.19.17
+ '@types/react':
+ specifier: ^18.3.20
+ version: 18.3.20
+ '@types/react-dom':
+ specifier: ^18.3.5
+ version: 18.3.5(@types/react@18.3.20)
+ react-scripts:
+ specifier: ^5.0.1
+ version: 5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(type-fest@0.21.3)(typescript@5.8.2)(utf-8-validate@5.0.10)
+
examples/taco/webpack-5:
dependencies:
'@nucypher/taco':
@@ -474,10 +514,10 @@ importers:
devDependencies:
copy-webpack-plugin:
specifier: ^13.0.0
- version: 13.0.1(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 13.0.1(webpack@5.101.3)
esbuild-loader:
specifier: ^2.21.0
- version: 2.21.0(webpack@5.101.3(webpack-cli@6.0.1))
+ version: 2.21.0(webpack@5.101.3)
webpack:
specifier: ^5.99.9
version: 5.101.3(webpack-cli@6.0.1)
@@ -558,6 +598,11 @@ importers:
typechain:
specifier: ^8.3.2
version: 8.3.2(typescript@5.8.2)
+ viem:
+ specifier: ^2.0.0
+ version: 2.37.8(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+
+ packages/shared/dist/es: {}
packages/taco:
dependencies:
@@ -598,6 +643,9 @@ importers:
modified-zod2md:
specifier: 0.1.5-modified.4
version: 0.1.5-modified.4(zod@3.24.2)
+ viem:
+ specifier: ^2.0.0
+ version: 2.37.8(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
packages/taco-auth:
dependencies:
@@ -609,7 +657,7 @@ importers:
version: link:../shared
siwe:
specifier: ^3.0.0
- version: 3.0.0(ethers@5.8.0)
+ version: 3.0.0(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
zod:
specifier: ^3.24.2
version: 3.24.2
@@ -618,6 +666,10 @@ importers:
specifier: workspace:*
version: link:../test-utils
+ packages/taco-auth/dist/es: {}
+
+ packages/taco/dist/es: {}
+
packages/test-utils:
dependencies:
'@nucypher/nucypher-core':
@@ -635,12 +687,18 @@ importers:
ethers:
specifier: ^5.8.0
version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ viem:
+ specifier: ^2.0.0
+ version: 2.37.8(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
vitest:
specifier: ^3.0.9
- version: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0)
+ version: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
packages:
+ '@adraffy/ens-normalize@1.11.0':
+ resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==}
+
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -2525,15 +2583,23 @@ packages:
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
- '@noble/curves@1.8.1':
- resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==}
+ '@noble/ciphers@1.3.0':
+ resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/curves@1.9.1':
+ resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/curves@1.9.2':
+ resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==}
engines: {node: ^14.21.3 || >=16}
'@noble/ed25519@1.7.3':
resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==}
- '@noble/hashes@1.7.1':
- resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==}
+ '@noble/hashes@1.8.0':
+ resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
'@nodelib/fs.scandir@2.1.5':
@@ -2766,14 +2832,14 @@ packages:
'@rushstack/eslint-patch@1.11.0':
resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
- '@scure/base@1.2.4':
- resolution: {integrity: sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==}
+ '@scure/base@1.2.6':
+ resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==}
- '@scure/bip32@1.6.2':
- resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==}
+ '@scure/bip32@1.7.0':
+ resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==}
- '@scure/bip39@1.5.4':
- resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==}
+ '@scure/bip39@1.6.0':
+ resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==}
'@sinclair/typebox@0.24.51':
resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==}
@@ -3033,6 +3099,9 @@ packages:
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
+ '@types/node@20.19.17':
+ resolution: {integrity: sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==}
+
'@types/node@24.3.0':
resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==}
@@ -3461,6 +3530,17 @@ packages:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
deprecated: Use your platform's native atob() and btoa() methods instead
+ abitype@1.1.0:
+ resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ zod: ^3.22.0 || ^4.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ zod:
+ optional: true
+
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -6250,6 +6330,11 @@ packages:
peerDependencies:
ws: '*'
+ isows@1.0.7:
+ resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==}
+ peerDependencies:
+ ws: '*'
+
istanbul-lib-coverage@3.2.2:
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
engines: {node: '>=8'}
@@ -7202,6 +7287,14 @@ packages:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
+ ox@0.9.6:
+ resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
p-cancelable@2.1.1:
resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
engines: {node: '>=8'}
@@ -9012,6 +9105,11 @@ packages:
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
+ tsx@4.20.6:
+ resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
tweetnacl@1.0.3:
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
@@ -9141,6 +9239,9 @@ packages:
underscore@1.12.1:
resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==}
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
undici-types@7.10.0:
resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
@@ -9237,6 +9338,14 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
+ viem@2.37.8:
+ resolution: {integrity: sha512-mL+5yvCQbRIR6QvngDQMfEiZTfNWfd+/QL5yFaOoYbpH3b1Q2ddwF7YG2eI2AcYSh9LE1gtUkbzZLFUAVyj4oQ==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
vite-node@3.0.9:
resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -9628,6 +9737,18 @@ packages:
utf-8-validate:
optional: true
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
wsl-utils@0.1.0:
resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
engines: {node: '>=18'}
@@ -9686,6 +9807,8 @@ packages:
snapshots:
+ '@adraffy/ens-normalize@1.11.0': {}
+
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
@@ -9720,10 +9843,10 @@ snapshots:
'@aptos-labs/aptos-cli': 1.0.2
'@aptos-labs/aptos-client': 1.1.0(axios@1.8.4)(got@11.8.6)
'@aptos-labs/script-composer-pack': 0.0.9
- '@noble/curves': 1.8.1
- '@noble/hashes': 1.7.1
- '@scure/bip32': 1.6.2
- '@scure/bip39': 1.5.4
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
eventemitter3: 5.0.1
form-data: 4.0.2
js-base64: 3.7.7
@@ -11608,6 +11731,43 @@ snapshots:
jest-util: 28.1.3
slash: 3.0.0
+ '@jest/core@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)':
+ dependencies:
+ '@jest/console': 27.5.1
+ '@jest/reporters': 27.5.1
+ '@jest/test-result': 27.5.1
+ '@jest/transform': 27.5.1
+ '@jest/types': 27.5.1
+ '@types/node': 24.3.0
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ emittery: 0.8.1
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 27.5.1
+ jest-config: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ jest-haste-map: 27.5.1
+ jest-message-util: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-resolve-dependencies: 27.5.1
+ jest-runner: 27.5.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ jest-runtime: 27.5.1
+ jest-snapshot: 27.5.1
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ jest-watcher: 27.5.1
+ micromatch: 4.0.8
+ rimraf: 3.0.2
+ slash: 3.0.0
+ strip-ansi: 6.0.1
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
'@jest/core@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10)':
dependencies:
'@jest/console': 27.5.1
@@ -11991,13 +12151,19 @@ snapshots:
dependencies:
eslint-scope: 5.1.1
- '@noble/curves@1.8.1':
+ '@noble/ciphers@1.3.0': {}
+
+ '@noble/curves@1.9.1':
dependencies:
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
+
+ '@noble/curves@1.9.2':
+ dependencies:
+ '@noble/hashes': 1.8.0
'@noble/ed25519@1.7.3': {}
- '@noble/hashes@1.7.1': {}
+ '@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
@@ -12037,7 +12203,7 @@ snapshots:
dependencies:
'@ethersproject/abstract-signer': 5.8.0
'@nucypher/shared': 0.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
- siwe: 3.0.0(ethers@5.8.0)
+ siwe: 3.0.0(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
zod: 3.24.2
transitivePeerDependencies:
- bufferutil
@@ -12072,12 +12238,12 @@ snapshots:
react-refresh: 0.11.0
schema-utils: 4.3.0
source-map: 0.7.4
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
optionalDependencies:
type-fest: 0.21.3
webpack-dev-server: 4.15.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack@5.101.3)
- '@pmmmwh/react-refresh-webpack-plugin@0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack-cli@6.0.1)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))':
+ '@pmmmwh/react-refresh-webpack-plugin@0.6.1(react-refresh@0.17.0)(type-fest@0.21.3)(webpack-dev-server@5.2.2)(webpack@5.101.3)':
dependencies:
anser: 2.3.2
core-js-pure: 3.41.0
@@ -12197,18 +12363,18 @@ snapshots:
'@rushstack/eslint-patch@1.11.0': {}
- '@scure/base@1.2.4': {}
+ '@scure/base@1.2.6': {}
- '@scure/bip32@1.6.2':
+ '@scure/bip32@1.7.0':
dependencies:
- '@noble/curves': 1.8.1
- '@noble/hashes': 1.7.1
- '@scure/base': 1.2.4
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/base': 1.2.6
- '@scure/bip39@1.5.4':
+ '@scure/bip39@1.6.0':
dependencies:
- '@noble/hashes': 1.7.1
- '@scure/base': 1.2.4
+ '@noble/hashes': 1.8.0
+ '@scure/base': 1.2.6
'@sinclair/typebox@0.24.51': {}
@@ -12234,8 +12400,8 @@ snapshots:
'@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
dependencies:
'@babel/runtime': 7.27.0
- '@noble/curves': 1.8.1
- '@noble/hashes': 1.7.1
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
'@solana/buffer-layout': 4.0.1
agentkeepalive: 4.6.0
bigint-buffer: 1.1.5
@@ -12255,7 +12421,7 @@ snapshots:
'@spruceid/siwe-parser@3.0.0':
dependencies:
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
apg-js: 4.4.0
'@stablelib/binary@1.0.1':
@@ -12527,6 +12693,10 @@ snapshots:
'@types/node@12.20.55': {}
+ '@types/node@20.19.17':
+ dependencies:
+ undici-types: 6.21.0
+
'@types/node@24.3.0':
dependencies:
undici-types: 7.10.0
@@ -12910,7 +13080,7 @@ snapshots:
- node-fetch
- supports-color
- '@vitest/coverage-v8@3.2.4(vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0))':
+ '@vitest/coverage-v8@3.2.4(vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
@@ -12925,7 +13095,7 @@ snapshots:
std-env: 3.9.0
test-exclude: 7.0.1
tinyrainbow: 2.0.0
- vitest: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0)
+ vitest: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
transitivePeerDependencies:
- supports-color
@@ -12936,13 +13106,13 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
- '@vitest/mocker@3.0.9(vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0))':
+ '@vitest/mocker@3.0.9(vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0))':
dependencies:
'@vitest/spy': 3.0.9
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0)
+ vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
'@vitest/pretty-format@3.0.9':
dependencies:
@@ -13045,17 +13215,17 @@ snapshots:
'@webassemblyjs/ast': 1.14.1
'@xtuc/long': 4.2.2
- '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))':
+ '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.101.3)':
dependencies:
webpack: 5.101.3(webpack-cli@6.0.1)
webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3)
- '@webpack-cli/info@3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))':
+ '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.101.3)':
dependencies:
webpack: 5.101.3(webpack-cli@6.0.1)
webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3)
- '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack-cli@6.0.1)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))':
+ '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.101.3)':
dependencies:
webpack: 5.101.3(webpack-cli@6.0.1)
webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3)
@@ -13073,6 +13243,11 @@ snapshots:
abab@2.0.6: {}
+ abitype@1.1.0(typescript@5.8.2)(zod@3.24.2):
+ optionalDependencies:
+ typescript: 5.8.2
+ zod: 3.24.2
+
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@@ -13450,7 +13625,7 @@ snapshots:
loader-utils: 2.0.4
make-dir: 3.1.0
schema-utils: 2.7.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
babel-plugin-istanbul@6.1.1:
dependencies:
@@ -14199,7 +14374,7 @@ snapshots:
dependencies:
toggle-selection: 1.0.6
- copy-webpack-plugin@13.0.1(webpack@5.101.3(webpack-cli@6.0.1)):
+ copy-webpack-plugin@13.0.1(webpack@5.101.3):
dependencies:
glob-parent: 6.0.2
normalize-path: 3.0.0
@@ -14347,7 +14522,7 @@ snapshots:
postcss-value-parser: 4.2.0
semver: 7.7.1
optionalDependencies:
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
css-minimizer-webpack-plugin@3.4.1(webpack@5.101.3):
dependencies:
@@ -14357,7 +14532,7 @@ snapshots:
schema-utils: 4.3.0
serialize-javascript: 6.0.2
source-map: 0.6.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
css-prefers-color-scheme@6.0.3(postcss@8.5.3):
dependencies:
@@ -14928,7 +15103,7 @@ snapshots:
dependencies:
es6-promise: 4.2.8
- esbuild-loader@2.21.0(webpack@5.101.3(webpack-cli@6.0.1)):
+ esbuild-loader@2.21.0(webpack@5.101.3):
dependencies:
esbuild: 0.16.17
joycon: 3.1.1
@@ -15052,7 +15227,7 @@ snapshots:
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0)
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0)
eslint-plugin-react: 7.37.4(eslint@8.57.0)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0)
@@ -15071,7 +15246,7 @@ snapshots:
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0)
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.0)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0)
eslint-plugin-react: 7.37.4(eslint@8.57.0)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0)
@@ -15086,6 +15261,33 @@ snapshots:
dependencies:
eslint: 8.57.1
+ eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2):
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/eslint-parser': 7.27.0(@babel/core@7.26.10)(eslint@8.57.1)
+ '@rushstack/eslint-patch': 1.11.0
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.8.2)
+ babel-preset-react-app: 10.1.0
+ confusing-browser-globals: 1.0.11
+ eslint: 8.57.1
+ eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.1)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)
+ eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2)
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
+ eslint-plugin-react: 7.37.4(eslint@8.57.1)
+ eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
+ eslint-plugin-testing-library: 5.11.1(eslint@8.57.1)(typescript@5.8.2)
+ optionalDependencies:
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - '@babel/plugin-syntax-flow'
+ - '@babel/plugin-transform-react-jsx'
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - jest
+ - supports-color
+
eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2):
dependencies:
'@babel/core': 7.26.10
@@ -15137,7 +15339,7 @@ snapshots:
tinyglobby: 0.2.12
unrs-resolver: 1.3.2
optionalDependencies:
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.0)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
@@ -15151,24 +15353,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0):
- dependencies:
- debug: 3.2.7
- optionalDependencies:
- '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2)
- eslint: 8.57.0
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0)
- transitivePeerDependencies:
- - supports-color
-
- eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.2)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
@@ -15225,7 +15417,7 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0):
+ eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -15236,7 +15428,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -15248,13 +15440,13 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2)
+ '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.0):
+ eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -15263,9 +15455,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
- eslint: 8.57.0
+ eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -15283,34 +15475,16 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1):
+ eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2):
dependencies:
- '@rtsao/scc': 1.1.0
- array-includes: 3.1.8
- array.prototype.findlastindex: 1.2.6
- array.prototype.flat: 1.3.3
- array.prototype.flatmap: 1.3.3
- debug: 3.2.7
- doctrine: 2.1.0
+ '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.8.2)
eslint: 8.57.1
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1)
- hasown: 2.0.2
- is-core-module: 2.16.1
- is-glob: 4.0.3
- minimatch: 3.1.2
- object.fromentries: 2.0.8
- object.groupby: 1.0.3
- object.values: 1.2.1
- semver: 6.3.1
- string.prototype.trimend: 1.0.9
- tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.8.2)
+ '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2)
+ jest: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- supports-color
+ - typescript
eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2):
dependencies:
@@ -15459,7 +15633,7 @@ snapshots:
micromatch: 4.0.8
normalize-path: 3.0.0
schema-utils: 4.3.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
eslint@8.57.0:
dependencies:
@@ -15763,17 +15937,11 @@ snapshots:
dependencies:
flat-cache: 3.2.0
- file-loader@6.2.0(webpack@5.101.3(webpack-cli@6.0.1)):
- dependencies:
- loader-utils: 2.0.4
- schema-utils: 3.3.0
- webpack: 5.101.3(webpack-cli@6.0.1)
-
file-loader@6.2.0(webpack@5.101.3):
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
file-uri-to-path@1.0.0: {}
@@ -15886,7 +16054,7 @@ snapshots:
semver: 7.7.1
tapable: 1.1.3
typescript: 5.8.2
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
optionalDependencies:
eslint: 8.57.1
@@ -16273,16 +16441,6 @@ snapshots:
relateurl: 0.2.7
terser: 5.39.0
- html-webpack-plugin@5.6.3(webpack@5.101.3(webpack-cli@6.0.1)):
- dependencies:
- '@types/html-minifier-terser': 6.1.0
- html-minifier-terser: 6.1.0
- lodash: 4.17.21
- pretty-error: 4.0.0
- tapable: 2.2.1
- optionalDependencies:
- webpack: 5.101.3(webpack-cli@6.0.1)
-
html-webpack-plugin@5.6.3(webpack@5.101.3):
dependencies:
'@types/html-minifier-terser': 6.1.0
@@ -16291,7 +16449,7 @@ snapshots:
pretty-error: 4.0.0
tapable: 2.2.1
optionalDependencies:
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
htmlparser2@6.1.0:
dependencies:
@@ -16699,6 +16857,10 @@ snapshots:
dependencies:
ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ isows@1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)):
+ dependencies:
+ ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
istanbul-lib-coverage@3.2.2: {}
istanbul-lib-instrument@5.2.1:
@@ -16812,6 +16974,27 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ jest-cli@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10):
+ dependencies:
+ '@jest/core': 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ '@jest/test-result': 27.5.1
+ '@jest/types': 27.5.1
+ chalk: 4.1.2
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ import-local: 3.2.0
+ jest-config: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ prompts: 2.4.2
+ yargs: 16.2.0
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
jest-cli@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10):
dependencies:
'@jest/core': 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10)
@@ -16833,6 +17016,40 @@ snapshots:
- ts-node
- utf-8-validate
+ jest-config@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10):
+ dependencies:
+ '@babel/core': 7.26.10
+ '@jest/test-sequencer': 27.5.1
+ '@jest/types': 27.5.1
+ babel-jest: 27.5.1(@babel/core@7.26.10)
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ deepmerge: 4.3.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-circus: 27.5.1
+ jest-environment-jsdom: 27.5.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ jest-environment-node: 27.5.1
+ jest-get-type: 27.5.1
+ jest-jasmine2: 27.5.1
+ jest-regex-util: 27.5.1
+ jest-resolve: 27.5.1
+ jest-runner: 27.5.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ jest-util: 27.5.1
+ jest-validate: 27.5.1
+ micromatch: 4.0.8
+ parse-json: 5.2.0
+ pretty-format: 27.5.1
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ ts-node: 10.9.2(@types/node@20.19.17)(typescript@5.8.2)
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
jest-config@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10):
dependencies:
'@babel/core': 7.26.10
@@ -17136,6 +17353,17 @@ snapshots:
leven: 3.1.0
pretty-format: 27.5.1
+ jest-watch-typeahead@1.1.0(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)):
+ dependencies:
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ jest: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ jest-regex-util: 28.0.2
+ jest-watcher: 28.1.3
+ slash: 4.0.0
+ string-length: 5.0.1
+ strip-ansi: 7.1.0
+
jest-watch-typeahead@1.1.0(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10)):
dependencies:
ansi-escapes: 4.3.2
@@ -17186,6 +17414,18 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
+ jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10):
+ dependencies:
+ '@jest/core': 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ import-local: 3.2.0
+ jest-cli: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - ts-node
+ - utf-8-validate
+
jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10):
dependencies:
'@jest/core': 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(utf-8-validate@5.0.10)
@@ -17589,7 +17829,7 @@ snapshots:
dependencies:
schema-utils: 4.3.0
tapable: 2.2.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
minimalistic-assert@1.0.1: {}
@@ -17920,6 +18160,21 @@ snapshots:
object-keys: 1.1.1
safe-push-apply: 1.0.0
+ ox@0.9.6(typescript@5.8.2)(zod@3.24.2):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.1.0(typescript@5.8.2)(zod@3.24.2)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - zod
+
p-cancelable@2.1.1: {}
p-filter@2.1.0:
@@ -18240,6 +18495,14 @@ snapshots:
postcss: 8.5.3
postcss-value-parser: 4.2.0
+ postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2)):
+ dependencies:
+ lilconfig: 3.1.3
+ yaml: 2.7.0
+ optionalDependencies:
+ postcss: 8.5.3
+ ts-node: 10.9.2(@types/node@20.19.17)(typescript@5.8.2)
+
postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2)):
dependencies:
lilconfig: 3.1.3
@@ -18254,7 +18517,7 @@ snapshots:
klona: 2.0.6
postcss: 8.5.3
semver: 7.7.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
postcss-logical@5.0.4(postcss@8.5.3):
dependencies:
@@ -18696,7 +18959,7 @@ snapshots:
shell-quote: 1.8.2
strip-ansi: 6.0.1
text-table: 0.2.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
optionalDependencies:
typescript: 5.8.2
transitivePeerDependencies:
@@ -18722,6 +18985,93 @@ snapshots:
react-refresh@0.17.0: {}
+ react-scripts@5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(type-fest@0.21.3)(typescript@5.8.2)(utf-8-validate@5.0.10):
+ dependencies:
+ '@babel/core': 7.26.10
+ '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@4.15.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack@5.101.3))(webpack@5.101.3)
+ '@svgr/webpack': 5.5.0
+ babel-jest: 27.5.1(@babel/core@7.26.10)
+ babel-loader: 8.4.1(@babel/core@7.26.10)(webpack@5.101.3)
+ babel-plugin-named-asset-import: 0.3.8(@babel/core@7.26.10)
+ babel-preset-react-app: 10.1.0
+ bfj: 7.1.0
+ browserslist: 4.24.4
+ camelcase: 6.3.0
+ case-sensitive-paths-webpack-plugin: 2.4.0
+ css-loader: 6.11.0(webpack@5.101.3)
+ css-minimizer-webpack-plugin: 3.4.1(webpack@5.101.3)
+ dotenv: 10.0.0
+ dotenv-expand: 5.1.0
+ eslint: 8.57.1
+ eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10))(typescript@5.8.2)
+ eslint-webpack-plugin: 3.2.0(eslint@8.57.1)(webpack@5.101.3)
+ file-loader: 6.2.0(webpack@5.101.3)
+ fs-extra: 10.1.0
+ html-webpack-plugin: 5.6.3(webpack@5.101.3)
+ identity-obj-proxy: 3.0.0
+ jest: 27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10)
+ jest-resolve: 27.5.1
+ jest-watch-typeahead: 1.1.0(jest@27.5.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))(utf-8-validate@5.0.10))
+ mini-css-extract-plugin: 2.9.2(webpack@5.101.3)
+ postcss: 8.5.3
+ postcss-flexbugs-fixes: 5.0.2(postcss@8.5.3)
+ postcss-loader: 6.2.1(postcss@8.5.3)(webpack@5.101.3)
+ postcss-normalize: 10.0.1(browserslist@4.24.4)(postcss@8.5.3)
+ postcss-preset-env: 7.8.3(postcss@8.5.3)
+ prompts: 2.4.2
+ react: 18.3.1
+ react-app-polyfill: 3.0.0
+ react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.8.2)(webpack@5.101.3)
+ react-refresh: 0.11.0
+ resolve: 1.22.10
+ resolve-url-loader: 4.0.0
+ sass-loader: 12.6.0(webpack@5.101.3)
+ semver: 7.7.1
+ source-map-loader: 3.0.2(webpack@5.101.3)
+ style-loader: 3.3.4(webpack@5.101.3)
+ tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))
+ terser-webpack-plugin: 5.3.14(webpack@5.101.3)
+ webpack: 5.101.3(webpack-cli@6.0.1)
+ webpack-dev-server: 4.15.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack@5.101.3)
+ webpack-manifest-plugin: 4.1.1(webpack@5.101.3)
+ workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.101.3)
+ optionalDependencies:
+ fsevents: 2.3.3
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - '@babel/plugin-syntax-flow'
+ - '@babel/plugin-transform-react-jsx'
+ - '@parcel/css'
+ - '@rspack/core'
+ - '@swc/core'
+ - '@types/babel__core'
+ - '@types/webpack'
+ - bufferutil
+ - canvas
+ - clean-css
+ - csso
+ - debug
+ - esbuild
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - fibers
+ - node-notifier
+ - node-sass
+ - rework
+ - rework-visit
+ - sass
+ - sass-embedded
+ - sockjs-client
+ - supports-color
+ - ts-node
+ - type-fest
+ - uglify-js
+ - utf-8-validate
+ - vue-template-compiler
+ - webpack-cli
+ - webpack-hot-middleware
+ - webpack-plugin-serve
+
react-scripts@5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))(type-fest@0.21.3)(typescript@5.8.2)(utf-8-validate@5.0.10):
dependencies:
'@babel/core': 7.26.10
@@ -18768,7 +19118,7 @@ snapshots:
style-loader: 3.3.4(webpack@5.101.3)
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2))
terser-webpack-plugin: 5.3.14(webpack@5.101.3)
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
webpack-dev-server: 4.15.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack@5.101.3)
webpack-manifest-plugin: 4.1.1(webpack@5.101.3)
workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.101.3)
@@ -19114,7 +19464,7 @@ snapshots:
dependencies:
klona: 2.0.6
neo-async: 2.6.2
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
sax@1.2.4: {}
@@ -19356,7 +19706,7 @@ snapshots:
sisteransi@1.0.5: {}
- siwe@3.0.0(ethers@5.8.0):
+ siwe@3.0.0(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)):
dependencies:
'@spruceid/siwe-parser': 3.0.0
'@stablelib/random': 1.0.2
@@ -19394,7 +19744,7 @@ snapshots:
abab: 2.0.6
iconv-lite: 0.6.3
source-map-js: 1.2.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
source-map-support@0.5.21:
dependencies:
@@ -19645,7 +19995,7 @@ snapshots:
style-loader@3.3.4(webpack@5.101.3):
dependencies:
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
styled-jsx@5.1.6(react@18.3.1):
dependencies:
@@ -19726,6 +20076,33 @@ snapshots:
typical: 5.2.0
wordwrapjs: 4.0.1
+ tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2)):
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.3
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.7
+ lilconfig: 3.1.3
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.3
+ postcss-import: 15.1.0(postcss@8.5.3)
+ postcss-js: 4.0.1(postcss@8.5.3)
+ postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2))
+ postcss-nested: 6.2.0(postcss@8.5.3)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.10
+ sucrase: 3.35.0
+ transitivePeerDependencies:
+ - ts-node
+
tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2)):
dependencies:
'@alloc/quick-lru': 5.2.0
@@ -19773,15 +20150,6 @@ snapshots:
ansi-escapes: 4.3.2
supports-hyperlinks: 2.3.0
- terser-webpack-plugin@5.3.14(webpack@5.101.3(webpack-cli@6.0.1)):
- dependencies:
- '@jridgewell/trace-mapping': 0.3.25
- jest-worker: 27.5.1
- schema-utils: 4.3.0
- serialize-javascript: 6.0.2
- terser: 5.39.0
- webpack: 5.101.3(webpack-cli@6.0.1)
-
terser-webpack-plugin@5.3.14(webpack@5.101.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
@@ -19789,7 +20157,7 @@ snapshots:
schema-utils: 4.3.0
serialize-javascript: 6.0.2
terser: 5.39.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
terser@5.39.0:
dependencies:
@@ -19932,6 +20300,25 @@ snapshots:
ts-interface-checker@0.1.13: {}
+ ts-node@10.9.2(@types/node@20.19.17)(typescript@5.8.2):
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.11
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ '@types/node': 20.19.17
+ acorn: 8.14.1
+ acorn-walk: 8.3.4
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.8.2
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+ optional: true
+
ts-node@10.9.2(@types/node@24.3.0)(typescript@5.8.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
@@ -19972,6 +20359,13 @@ snapshots:
tslib: 1.14.1
typescript: 5.8.2
+ tsx@4.20.6:
+ dependencies:
+ esbuild: 0.25.1
+ get-tsconfig: 4.10.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
tweetnacl@1.0.3: {}
type-check@0.3.2:
@@ -20098,6 +20492,8 @@ snapshots:
underscore@1.12.1: {}
+ undici-types@6.21.0: {}
+
undici-types@7.10.0: {}
unicode-canonical-property-names-ecmascript@2.0.1: {}
@@ -20195,13 +20591,30 @@ snapshots:
vary@1.1.2: {}
- vite-node@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0):
+ viem@2.37.8(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2):
+ dependencies:
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.1.0(typescript@5.8.2)(zod@3.24.2)
+ isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.9.6(typescript@5.8.2)(zod@3.24.2)
+ ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
+ vite-node@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0):
dependencies:
cac: 6.7.14
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 2.0.3
- vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0)
+ vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -20216,7 +20629,7 @@ snapshots:
- tsx
- yaml
- vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0):
+ vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0):
dependencies:
esbuild: 0.25.1
postcss: 8.5.3
@@ -20226,12 +20639,13 @@ snapshots:
fsevents: 2.3.3
jiti: 2.4.2
terser: 5.39.0
+ tsx: 4.20.6
yaml: 2.7.0
- vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0)(terser@5.39.0)(yaml@2.7.0):
+ vitest@3.0.9(@types/node@24.3.0)(jiti@2.4.2)(jsdom@16.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0):
dependencies:
'@vitest/expect': 3.0.9
- '@vitest/mocker': 3.0.9(vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0))
+ '@vitest/mocker': 3.0.9(vite@6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0))
'@vitest/pretty-format': 3.0.9
'@vitest/runner': 3.0.9
'@vitest/snapshot': 3.0.9
@@ -20247,8 +20661,8 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0)
- vite-node: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(yaml@2.7.0)
+ vite: 6.2.3(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
+ vite-node: 3.0.9(@types/node@24.3.0)(jiti@2.4.2)(terser@5.39.0)(tsx@4.20.6)(yaml@2.7.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.3.0
@@ -20311,9 +20725,9 @@ snapshots:
webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3):
dependencies:
'@discoveryjs/json-ext': 0.6.3
- '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))
- '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))
- '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.101.3))(webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)(webpack-cli@6.0.1)(webpack@5.101.3))(webpack@5.101.3(webpack-cli@6.0.1))
+ '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.101.3)
+ '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.101.3)
+ '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.101.3)
colorette: 2.0.20
commander: 12.1.0
cross-spawn: 7.0.6
@@ -20334,9 +20748,9 @@ snapshots:
mime-types: 2.1.35
range-parser: 1.2.1
schema-utils: 4.3.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
- webpack-dev-middleware@7.4.2(webpack@5.101.3(webpack-cli@6.0.1)):
+ webpack-dev-middleware@7.4.2(webpack@5.101.3):
dependencies:
colorette: 2.0.20
memfs: 4.38.2
@@ -20380,7 +20794,7 @@ snapshots:
webpack-dev-middleware: 5.3.4(webpack@5.101.3)
ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
optionalDependencies:
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
transitivePeerDependencies:
- bufferutil
- debug
@@ -20415,7 +20829,7 @@ snapshots:
serve-index: 1.9.1
sockjs: 0.3.24
spdy: 4.0.2
- webpack-dev-middleware: 7.4.2(webpack@5.101.3(webpack-cli@6.0.1))
+ webpack-dev-middleware: 7.4.2(webpack@5.101.3)
ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
optionalDependencies:
webpack: 5.101.3(webpack-cli@6.0.1)
@@ -20429,7 +20843,7 @@ snapshots:
webpack-manifest-plugin@4.1.1(webpack@5.101.3):
dependencies:
tapable: 2.2.1
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
webpack-sources: 2.3.1
webpack-merge@6.0.1:
@@ -20450,38 +20864,6 @@ snapshots:
webpack-sources@3.3.3: {}
- webpack@5.101.3:
- dependencies:
- '@types/eslint-scope': 3.7.7
- '@types/estree': 1.0.8
- '@types/json-schema': 7.0.15
- '@webassemblyjs/ast': 1.14.1
- '@webassemblyjs/wasm-edit': 1.14.1
- '@webassemblyjs/wasm-parser': 1.14.1
- acorn: 8.15.0
- acorn-import-phases: 1.0.4(acorn@8.15.0)
- browserslist: 4.24.4
- chrome-trace-event: 1.0.4
- enhanced-resolve: 5.18.1
- es-module-lexer: 1.6.0
- eslint-scope: 5.1.1
- events: 3.3.0
- glob-to-regexp: 0.4.1
- graceful-fs: 4.2.11
- json-parse-even-better-errors: 2.3.1
- loader-runner: 4.3.0
- mime-types: 2.1.35
- neo-async: 2.6.2
- schema-utils: 4.3.2
- tapable: 2.2.1
- terser-webpack-plugin: 5.3.14(webpack@5.101.3)
- watchpack: 2.4.2
- webpack-sources: 3.3.3
- transitivePeerDependencies:
- - '@swc/core'
- - esbuild
- - uglify-js
-
webpack@5.101.3(webpack-cli@6.0.1):
dependencies:
'@types/eslint-scope': 3.7.7
@@ -20506,7 +20888,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 4.3.2
tapable: 2.2.1
- terser-webpack-plugin: 5.3.14(webpack@5.101.3(webpack-cli@6.0.1))
+ terser-webpack-plugin: 5.3.14(webpack@5.101.3)
watchpack: 2.4.2
webpack-sources: 3.3.3
optionalDependencies:
@@ -20733,7 +21115,7 @@ snapshots:
fast-json-stable-stringify: 2.1.0
pretty-bytes: 5.6.0
upath: 1.2.0
- webpack: 5.101.3
+ webpack: 5.101.3(webpack-cli@6.0.1)
webpack-sources: 1.4.3
workbox-build: 6.6.0(@types/babel__core@7.20.5)
transitivePeerDependencies:
@@ -20787,6 +21169,11 @@ snapshots:
bufferutil: 4.0.9
utf-8-validate: 5.0.10
+ ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ optionalDependencies:
+ bufferutil: 4.0.9
+ utf-8-validate: 5.0.10
+
wsl-utils@0.1.0:
dependencies:
is-wsl: 3.1.0
diff --git a/scripts/ensure-es-compatible-imports.ts b/scripts/ensure-es-compatible-imports.ts
new file mode 100644
index 000000000..df23e4064
--- /dev/null
+++ b/scripts/ensure-es-compatible-imports.ts
@@ -0,0 +1,108 @@
+#!/usr/bin/env tsx
+/**
+ * This is needed to support ECMA Script modules.
+ * It fixes TypeScript source imports to use .js extensions for proper ES module support
+ * Can be called from any package directory with: pnpm tsx ../../scripts/ensure-es-compatible-imports.ts
+ */
+
+import * as fs from 'fs';
+import * as path from 'path';
+
+function addJsExtensionToImports(dir: string): void {
+ const files = fs.readdirSync(dir);
+
+ for (const file of files) {
+ const fullPath = path.join(dir, file);
+ const stat = fs.statSync(fullPath);
+
+ if (stat.isDirectory()) {
+ // Recursively process subdirectories
+ addJsExtensionToImports(fullPath);
+ } else if (file.endsWith('.ts')) {
+ // Fix imports in .ts files
+ let content = fs.readFileSync(fullPath, 'utf8');
+ let modified = false;
+
+ // Fix all relative import/export statements to include .js extension
+ // Match import/export statements that use `from "..."`, including:
+ // - import ... from './path'
+ // - export { ... } from './path'
+ // - export * as ns from './path'
+ // - export * from './path'
+ const importExportRegex =
+ /((?:import|export)(?:\s+type)?\s*(?:\{\s*[^}]*\}\s*|\*\s*as\s+\w+|\*|\w+)?\s*from\s+['"])([^'"]*?)(['"])/g;
+
+ content = content.replace(
+ importExportRegex,
+ (match, prefix, importPath, quote) => {
+ // The relative modules are: './', '../', '.', or '..'
+ const isRelative =
+ importPath.startsWith('./') ||
+ importPath.startsWith('../') ||
+ importPath === '.' ||
+ importPath === '..';
+ if (!isRelative) {
+ // Skip external modules
+ return match;
+ }
+
+ // Skip if already has extension
+ if (importPath.endsWith('.js') || importPath.endsWith('.mjs')) {
+ return match;
+ }
+
+ // Check if it's a directory with index.ts
+ const resolvedPath = path.resolve(path.dirname(fullPath), importPath);
+ const indexPath = path.join(resolvedPath, 'index.ts');
+
+ if (
+ fs.existsSync(resolvedPath) &&
+ fs.statSync(resolvedPath).isDirectory()
+ ) {
+ if (fs.existsSync(indexPath)) {
+ modified = true;
+ return `${prefix}${importPath}/index.js${quote}`;
+ }
+ } else {
+ // Check if it's a file without extension
+ const filePath = `${resolvedPath}.ts`;
+ if (fs.existsSync(filePath)) {
+ modified = true;
+ return `${prefix}${importPath}.js${quote}`;
+ }
+ }
+
+ return match;
+ },
+ );
+
+ if (modified) {
+ fs.writeFileSync(fullPath, content);
+ console.log(
+ `Fixed source imports in: ${path.relative(process.cwd(), fullPath)}`,
+ );
+ }
+ }
+ }
+}
+
+// Get the calling package directory (where the script is run from)
+const packageDir = process.cwd();
+const packageName = path.basename(packageDir);
+const packageJsonPath = path.join(packageDir, 'package.json');
+const srcDir = path.join(packageDir, 'src');
+if (!fs.existsSync(packageJsonPath)) {
+ console.log(
+ `❌ No package.json found in the current directory (${packageDir}). Please run this script from a package directory.`,
+ );
+ process.exit(1);
+}
+
+if (fs.existsSync(srcDir)) {
+ console.log(`🔧 Fixing TypeScript source imports for ${packageName}...`);
+ addJsExtensionToImports(srcDir);
+ console.log(`✅ Source import fix complete for ${packageName}!`);
+} else {
+ console.log(`❌ Source directory not found for ${packageName}.`);
+ process.exit(1);
+}
diff --git a/scripts/ensure-es-package-type.ts b/scripts/ensure-es-package-type.ts
new file mode 100644
index 000000000..861ed23be
--- /dev/null
+++ b/scripts/ensure-es-package-type.ts
@@ -0,0 +1,52 @@
+#!/usr/bin/env tsx
+/**
+ * Set dist/es/package.json type to "module" while preserving
+ * any existing content. This enables proper ES module resolution for dual
+ * CommonJS/ES module packages.
+ *
+ * Usage from within a package directory:
+ * pnpm tsx ../../scripts/ensure-es-package-type.ts
+ */
+
+import * as fs from 'fs';
+import * as path from 'path';
+
+interface ESPackageJson {
+ type: 'module';
+ [key: string]: unknown;
+}
+
+type Action = 'created' | 'updated' | 'verified' | 'recreated';
+
+// Get the calling package directory (where the script is run from)
+const packageDir = process.cwd();
+const packageName = path.basename(packageDir);
+const esDir = path.join(packageDir, 'dist/es');
+const packageJsonPath = path.join(esDir, 'package.json');
+
+if (!fs.existsSync(esDir)) {
+ console.log(`❌ ES directory not found for ${packageName}`);
+ process.exit(1);
+}
+
+let esPackageJson: ESPackageJson = { type: 'module' };
+let action: Action = 'created';
+
+// Preserve existing content if package.json exists
+if (fs.existsSync(packageJsonPath)) {
+ try {
+ const existingContent = fs.readFileSync(packageJsonPath, 'utf8');
+ esPackageJson = JSON.parse(existingContent);
+ action = esPackageJson.type === 'module' ? 'verified' : 'updated';
+ } catch (error) {
+ // Invalid JSON - will be recreated
+ esPackageJson = { type: 'module' };
+ action = 'recreated';
+ }
+}
+
+// Set ES Module type
+esPackageJson.type = 'module';
+
+fs.writeFileSync(packageJsonPath, JSON.stringify(esPackageJson, null, 2));
+console.log(`✅ ES module package.json ${action} for ${packageName}`);