diff --git a/.eslintignore b/.eslintignore index dd449725e..17ab736c5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,3 @@ *.md +**/dist/** +**/build/** diff --git a/demos/taco-demo/package.json b/demos/taco-demo/package.json index 615b81ae9..d775733ba 100644 --- a/demos/taco-demo/package.json +++ b/demos/taco-demo/package.json @@ -9,7 +9,9 @@ "build": "pnpm clean && webpack --mode production --progress", "clean": "rimraf build", "check": "pnpm type-check && pnpm build", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "lint": "eslint --ext .ts src", + "lint:fix": "pnpm lint --fix" }, "dependencies": { "@nucypher/taco": "^0.1.0-rc.6", @@ -21,7 +23,8 @@ "react": "^18.2.0", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.2.0", - "react-spinners": "^0.13.6" + "react-spinners": "^0.13.6", + "viem": "^1.19.9" }, "devDependencies": { "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -34,7 +37,7 @@ "react-refresh": "^0.14.0", "rimraf": "^5.0.5", "stream-browserify": "^3.0.0", - "typescript": "^4.8.3", + "typescript": "^5.2.2", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.11.1" diff --git a/demos/taco-demo/src/App.tsx b/demos/taco-demo/src/App.tsx index 0329df38b..087a19255 100644 --- a/demos/taco-demo/src/App.tsx +++ b/demos/taco-demo/src/App.tsx @@ -8,14 +8,16 @@ import { ThresholdMessageKit, } from '@nucypher/taco'; import { Mumbai, useEthers } from '@usedapp/core'; -import { ethers } from 'ethers'; import React, { useEffect, useState } from 'react'; +import 'viem/window'; + +import { createPublicClient, createWalletClient, custom } from 'viem'; import { ConditionBuilder } from './ConditionBuilder'; +import { DEFAULT_DOMAIN, DEFAULT_RITUAL_ID } from './config'; import { Decrypt } from './Decrypt'; import { Encrypt } from './Encrypt'; import { Spinner } from './Spinner'; -import { DEFAULT_DOMAIN, DEFAULT_RITUAL_ID } from './config'; export default function App() { const { activateBrowserWallet, deactivate, account, switchNetwork } = @@ -36,6 +38,10 @@ export default function App() { }, []); const encryptMessage = async (message: string) => { + if (!window.ethereum) { + console.error('You need to connect to your wallet first'); + return; + } if (!condition) { return; } @@ -43,14 +49,19 @@ export default function App() { await switchNetwork(Mumbai.chainId); - const provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const encryptedMessage = await encrypt( - provider, + publicClient, domain, message, condition, ritualId, - provider.getSigner(), + walletClient, ); setEncryptedMessage(encryptedMessage); @@ -58,6 +69,10 @@ export default function App() { }; const decryptMessage = async (encryptedMessage: ThresholdMessageKit) => { + if (!window.ethereum) { + console.error('You need to connect to your wallet first'); + return; + } if (!condition) { return; } @@ -65,13 +80,18 @@ export default function App() { setDecryptedMessage(''); setDecryptionErrors([]); - const provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const decryptedMessage = await decrypt( - provider, + publicClient, domain, encryptedMessage, getPorterUri(domain), - provider.getSigner(), + walletClient, ); setDecryptedMessage(new TextDecoder().decode(decryptedMessage)); diff --git a/demos/taco-demo/src/react-app-env.d.ts b/demos/taco-demo/src/react-app-env.d.ts deleted file mode 100644 index 4e39a38ad..000000000 --- a/demos/taco-demo/src/react-app-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// - -interface Window { - ethereum: any; -} diff --git a/demos/taco-demo/webpack.config.js b/demos/taco-demo/webpack.config.js index 11c98381e..9bbd7185b 100644 --- a/demos/taco-demo/webpack.config.js +++ b/demos/taco-demo/webpack.config.js @@ -33,8 +33,8 @@ module.exports = { DEFAULT_RITUAL_ID: JSON.stringify(process.env.DEFAULT_RITUAL_ID), DEFAULT_DOMAIN: JSON.stringify(process.env.DEFAULT_DOMAIN), }, - } - }) + }, + }), ].filter(Boolean), module: { rules: [ diff --git a/demos/taco-nft-demo/package.json b/demos/taco-nft-demo/package.json index 1b7c07e08..2ed6ae059 100644 --- a/demos/taco-nft-demo/package.json +++ b/demos/taco-nft-demo/package.json @@ -9,7 +9,9 @@ "build": "pnpm clean && webpack --mode production --progress", "clean": "rimraf build", "check": "pnpm type-check && pnpm build", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "lint": "eslint --ext .ts src", + "lint:fix": "pnpm lint --fix" }, "dependencies": { "@nucypher/taco": "^0.1.0-rc.6", @@ -21,7 +23,8 @@ "react": "^18.2.0", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.2.0", - "react-spinners": "^0.13.6" + "react-spinners": "^0.13.6", + "viem": "^1.19.9" }, "devDependencies": { "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -34,7 +37,7 @@ "react-refresh": "^0.14.0", "rimraf": "^5.0.5", "stream-browserify": "^3.0.0", - "typescript": "^4.8.3", + "typescript": "^5.2.2", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.11.1" diff --git a/demos/taco-nft-demo/src/App.tsx b/demos/taco-nft-demo/src/App.tsx index c4928f8e5..1ecf960c8 100644 --- a/demos/taco-nft-demo/src/App.tsx +++ b/demos/taco-nft-demo/src/App.tsx @@ -8,8 +8,9 @@ import { ThresholdMessageKit, } from '@nucypher/taco'; import { Mumbai, useEthers } from '@usedapp/core'; -import { ethers } from 'ethers'; import React, { useEffect, useState } from 'react'; +import 'viem/window'; +import { createPublicClient, createWalletClient, custom } from 'viem'; import { Decrypt } from './Decrypt'; import { Encrypt } from './Encrypt'; @@ -35,6 +36,10 @@ export default function App() { }, []); const encryptMessage = async (message: string) => { + if (!window.ethereum) { + console.error('You need to connect to your wallet first'); + return; + } if (!condition) { return; } @@ -42,14 +47,19 @@ export default function App() { await switchNetwork(Mumbai.chainId); - const provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const encryptedMessage = await encrypt( - provider, + publicClient, domain, message, condition, ritualId, - provider.getSigner(), + walletClient, ); setEncryptedMessage(encryptedMessage); @@ -57,6 +67,10 @@ export default function App() { }; const decryptMessage = async (encryptedMessage: ThresholdMessageKit) => { + if (!window.ethereum) { + console.error('You need to connect to your wallet first'); + return; + } if (!condition) { return; } @@ -64,13 +78,18 @@ export default function App() { setDecryptedMessage(''); setDecryptionErrors([]); - const provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const decryptedMessage = await decrypt( - provider, + publicClient, domain, encryptedMessage, getPorterUri(domain), - provider.getSigner(), + walletClient, ); setDecryptedMessage(new TextDecoder().decode(decryptedMessage)); diff --git a/demos/taco-nft-demo/src/react-app-env.d.ts b/demos/taco-nft-demo/src/react-app-env.d.ts deleted file mode 100644 index 4e39a38ad..000000000 --- a/demos/taco-nft-demo/src/react-app-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// - -interface Window { - ethereum: any; -} diff --git a/demos/taco-nft-demo/webpack.config.js b/demos/taco-nft-demo/webpack.config.js index 11c98381e..9bbd7185b 100644 --- a/demos/taco-nft-demo/webpack.config.js +++ b/demos/taco-nft-demo/webpack.config.js @@ -33,8 +33,8 @@ module.exports = { DEFAULT_RITUAL_ID: JSON.stringify(process.env.DEFAULT_RITUAL_ID), DEFAULT_DOMAIN: JSON.stringify(process.env.DEFAULT_DOMAIN), }, - } - }) + }, + }), ].filter(Boolean), module: { rules: [ diff --git a/examples/pre/nextjs/package.json b/examples/pre/nextjs/package.json index a9779d02b..38345be18 100644 --- a/examples/pre/nextjs/package.json +++ b/examples/pre/nextjs/package.json @@ -11,6 +11,12 @@ }, "dependencies": { "@nucypher/pre": "workspace:*", + "next": "14.0.3", + "react": "18.2.0", + "react-dom": "18.2.0", + "viem": "^1.19.9" + }, + "devDependencies": { "@types/node": "20.10.0", "@types/react": "18.2.45", "@types/react-dom": "18.2.14", diff --git a/examples/pre/nextjs/src/app/page.tsx b/examples/pre/nextjs/src/app/page.tsx index 772dfe070..8e7fd4666 100644 --- a/examples/pre/nextjs/src/app/page.tsx +++ b/examples/pre/nextjs/src/app/page.tsx @@ -9,18 +9,13 @@ import { SecretKey, toHexString, } from '@nucypher/pre'; -import { ethers } from 'ethers'; -import { hexlify } from "ethers/lib/utils"; import { useEffect, useState } from 'react'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -declare const window: any; +import { createWalletClient, custom, toHex, WalletClient } from 'viem'; +import 'viem/window'; function App() { const [isInit, setIsInit] = useState(false); - const [provider, setProvider] = useState< - ethers.providers.Web3Provider | undefined - >(); + const [walletClient, setWalletClient] = useState(); const [alice, setAlice] = useState(); const [bob, setBob] = useState(); const [policy, setPolicy] = useState(); @@ -30,32 +25,33 @@ function App() { setIsInit(true); }; - const loadWeb3Provider = async () => { + const loadWalletClient = async () => { if (!window.ethereum) { console.error('You need to connect to your wallet first'); + return; } - const provider = new ethers.providers.Web3Provider(window.ethereum, 'any'); - - const { chainId } = await provider.getNetwork(); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + const chainId = await walletClient.getChainId(); const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet await window.ethereum.request({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } - await provider.send('eth_requestAccounts', []); - setProvider(provider); + setWalletClient(walletClient); }; useEffect(() => { initNucypher(); - loadWeb3Provider(); + loadWalletClient(); }, []); - if (!isInit || !provider) { + if (!isInit || !walletClient) { return
Loading...
; } @@ -82,7 +78,7 @@ function App() { const getRandomLabel = () => `label-${new Date().getTime()}`; const runExample = async () => { - if (!provider) { + if (!walletClient) { console.error('You need to connect to your wallet first'); return; } @@ -106,8 +102,7 @@ function App() { endDate, }; const policy = await alice.grant( - provider, - provider.getSigner(), + walletClient, domains.TESTNET, getPorterUri(domains.TESTNET), policyParams, diff --git a/examples/pre/nodejs/package.json b/examples/pre/nodejs/package.json index 35a479f94..950380e62 100644 --- a/examples/pre/nodejs/package.json +++ b/examples/pre/nodejs/package.json @@ -11,7 +11,9 @@ }, "dependencies": { "@nucypher/pre": "workspace:*", - "dotenv": "^16.3.1", - "ethers": "^5.7.2" + "viem": "^1.19.9" + }, + "devDependencies": { + "dotenv": "^16.3.1" } } diff --git a/examples/pre/nodejs/src/index.ts b/examples/pre/nodejs/src/index.ts index c684fdf10..7f131a0e3 100644 --- a/examples/pre/nodejs/src/index.ts +++ b/examples/pre/nodejs/src/index.ts @@ -8,7 +8,8 @@ import { toBytes, } from '@nucypher/pre'; import * as dotenv from 'dotenv'; -import { ethers } from 'ethers'; +import { createWalletClient, Hex, http } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; dotenv.config(); @@ -49,15 +50,18 @@ const getRandomLabel = () => `label-${new Date().getTime()}`; const runExample = async () => { await initialize(); - const provider = new ethers.providers.JsonRpcProvider(rpcProviderUrl); + const account = privateKeyToAccount(privateKey); + const walletClient = createWalletClient({ + transport: http(rpcProviderUrl), + account, + }); // Make sure the provider is connected to Mumbai testnet - const network = await provider.getNetwork(); - if (network.chainId !== 80001) { + const chainId = await walletClient.getChainId(); + if (chainId !== 80001) { console.error('Please connect to Mumbai testnet'); } - const signer = new ethers.Wallet(privateKey); const policyParams = { bob: makeRemoteBob(), label: getRandomLabel(), @@ -70,8 +74,7 @@ const runExample = async () => { console.log('Creating policy...'); const policy = await alice.grant( - provider, - signer, + walletClient, domains.TESTNET, getPorterUri(domains.TESTNET), policyParams, diff --git a/examples/pre/react/package.json b/examples/pre/react/package.json index f4d540117..b21b51404 100644 --- a/examples/pre/react/package.json +++ b/examples/pre/react/package.json @@ -26,7 +26,7 @@ "@nucypher/pre": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0", - "ethers": "^5.7.2" + "viem": "^1.19.9" }, "devDependencies": { "@types/node": "^20.10.0", diff --git a/examples/pre/react/src/App.tsx b/examples/pre/react/src/App.tsx index 3bd6c2bc5..47174c8d4 100644 --- a/examples/pre/react/src/App.tsx +++ b/examples/pre/react/src/App.tsx @@ -8,15 +8,13 @@ import { SecretKey, toHexString, } from '@nucypher/pre'; -import { ethers } from 'ethers'; -import { hexlify } from "ethers/lib/utils"; import { useEffect, useState } from 'react'; +import { createWalletClient, custom, toHex, WalletClient } from 'viem'; +import 'viem/window'; function App() { const [isInit, setIsInit] = useState(false); - const [provider, setProvider] = useState< - ethers.providers.Web3Provider | undefined - >(); + const [walletClient, setWalletClient] = useState(); const [alice, setAlice] = useState(); const [bob, setBob] = useState(); const [policy, setPolicy] = useState(); @@ -29,21 +27,23 @@ function App() { const loadWeb3Provider = async () => { if (!window.ethereum) { console.error('You need to connect to your wallet first'); + return; } - const provider = new ethers.providers.Web3Provider(window.ethereum, 'any'); - const { chainId } = await provider.getNetwork(); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + const chainId = await walletClient.getChainId(); const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet await window.ethereum.request({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } - await provider.send('eth_requestAccounts', []); - setProvider(provider); + setWalletClient(walletClient); }; useEffect(() => { @@ -51,7 +51,7 @@ function App() { loadWeb3Provider(); }, []); - if (!isInit || !provider) { + if (!isInit || !walletClient) { return
Loading...
; } @@ -78,7 +78,7 @@ function App() { const getRandomLabel = () => `label-${new Date().getTime()}`; const runExample = async () => { - if (!provider) { + if (!walletClient) { console.error('You need to connect to your wallet first'); return; } @@ -102,8 +102,7 @@ function App() { endDate, }; const policy = await alice.grant( - provider, - provider.getSigner(), + walletClient, domains.TESTNET, getPorterUri(domains.TESTNET), policyParams, diff --git a/examples/pre/react/src/react-app-env.d.ts b/examples/pre/react/src/react-app-env.d.ts deleted file mode 100644 index 8f8826530..000000000 --- a/examples/pre/react/src/react-app-env.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// -import { ExternalProvider } from '@ethersproject/providers'; - -declare global { - interface Window { - ethereum?: ExternalProvider; - } -} diff --git a/examples/pre/webpack-5/package.json b/examples/pre/webpack-5/package.json index 5b863ae07..e37ac44a0 100644 --- a/examples/pre/webpack-5/package.json +++ b/examples/pre/webpack-5/package.json @@ -12,14 +12,14 @@ "type-check": "tsc" }, "dependencies": { - "@nucypher/pre": "workspace:*" + "@nucypher/pre": "workspace:*", + "viem": "^1.19.9" }, "devDependencies": { "copy-webpack-plugin": "^11.0.0", "esbuild-loader": "^2.11.0", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.7.4", - "ethers": "^5.7.2" + "webpack-dev-server": "^4.7.4" } } diff --git a/examples/pre/webpack-5/src/index.ts b/examples/pre/webpack-5/src/index.ts index eb555dc06..fac8ada2f 100644 --- a/examples/pre/webpack-5/src/index.ts +++ b/examples/pre/webpack-5/src/index.ts @@ -6,14 +6,8 @@ import { initialize, SecretKey, } from '@nucypher/pre'; -import { ethers } from 'ethers'; -import { hexlify } from 'ethers/lib/utils'; - -declare global { - interface Window { - ethereum?: ethers.providers.ExternalProvider; - } -} +import { createWalletClient, custom, toHex } from 'viem'; +import 'viem/window'; const txtEncoder = new TextEncoder(); @@ -44,22 +38,23 @@ const getRandomLabel = () => `label-${new Date().getTime()}`; const runExample = async () => { if (!window.ethereum) { console.error('You need to connect to your wallet first'); + return; } await initialize(); alert('Sign a transaction to create a policy.'); - const provider = new ethers.providers.Web3Provider(window.ethereum!, 'any'); - await provider.send('eth_requestAccounts', []); - - const { chainId } = await provider.getNetwork(); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + const chainId = await walletClient.getChainId(); const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet - await window.ethereum!.request!({ + await window.ethereum.request!({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } @@ -79,8 +74,7 @@ const runExample = async () => { const alice = makeAlice(); const policy = await alice.grant( - provider, - provider.getSigner(), + walletClient, domains.TESTNET, getPorterUri(domains.TESTNET), policyParams, diff --git a/examples/taco/nextjs/package.json b/examples/taco/nextjs/package.json index 7c1862f74..d1abc04f0 100644 --- a/examples/taco/nextjs/package.json +++ b/examples/taco/nextjs/package.json @@ -12,15 +12,21 @@ "dependencies": { "@nucypher/shared": "workspace:*", "@nucypher/taco": "workspace:*", - "@types/node": "20.10.0", + "@types/node": "20.6.3", "@types/react": "18.2.45", "@types/react-dom": "18.2.14", - "eslint": "8.54.0", + "eslint": "8.53.0", "eslint-config-next": "14.0.3", - "ethers": "^5.7.2", "next": "14.0.4", "react": "18.2.0", "react-dom": "18.2.0", - "typescript": "5.2.2" + "viem": "^1.19.9" + }, + "devDependencies": { + "@types/node": "20.10.0", + "@types/react": "18.2.37", + "@types/react-dom": "18.2.14", + "eslint": "8.54.0", + "eslint-config-next": "14.0.3" } } diff --git a/examples/taco/nextjs/src/app/layout.tsx b/examples/taco/nextjs/src/app/layout.tsx index a048075ad..f00aeecb5 100644 --- a/examples/taco/nextjs/src/app/layout.tsx +++ b/examples/taco/nextjs/src/app/layout.tsx @@ -1,6 +1,6 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; -import '../../styles/global.css' +import '../../styles/global.css'; const inter = Inter({ subsets: ['latin'] }); diff --git a/examples/taco/nextjs/src/app/page.tsx b/examples/taco/nextjs/src/app/page.tsx index a9530033a..f16fd5c7b 100644 --- a/examples/taco/nextjs/src/app/page.tsx +++ b/examples/taco/nextjs/src/app/page.tsx @@ -1,30 +1,29 @@ 'use client'; -import {fromHexString} from "@nucypher/shared"; -import {conditions, domains, fromBytes, toHexString} from '@nucypher/taco'; -import {ethers} from 'ethers'; -import {hexlify} from 'ethers/lib/utils'; -import {useEffect, useState} from 'react'; +import { fromHexString } from '@nucypher/shared'; +import { conditions, domains, fromBytes, toHexString } from '@nucypher/taco'; +import { useEffect, useState } from 'react'; +import 'viem/window'; +import { + createPublicClient, + createWalletClient, + custom, + PublicClient, + toHex, + WalletClient, +} from 'viem'; import useTaco from '../hooks/useTaco'; - - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -declare const window: any; - const ritualId = 5; // Replace with your own ritual ID const domain = domains.TESTNET; function App() { - const [provider, setProvider] = useState< - ethers.providers.Web3Provider | undefined - >(); - const [message, setMessage] = useState('this is a secret') - const [encrypting, setEncrypting] = useState(false) - const [encryptedText, setEncryptedText] = useState( - '', - ); - const [decrypting, setDecrypting] = useState(false) + const [walletClient, setWalletClient] = useState(); + const [publicClient, setPublicClient] = 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( '', ); @@ -32,21 +31,28 @@ function App() { const loadWeb3Provider = async () => { if (!window.ethereum) { console.error('You need to connect to your wallet first'); + return; } - const provider = new ethers.providers.Web3Provider(window.ethereum, 'any'); - const { chainId } = await provider.getNetwork(); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + + const chainId = await walletClient.getChainId(); const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet await window.ethereum.request({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } - await provider.send('eth_requestAccounts', []); - setProvider(provider); + setWalletClient(walletClient); + setPublicClient(publicClient); }; useEffect(() => { @@ -55,19 +61,21 @@ function App() { const { isInit, encryptDataToBytes, decryptDataFromBytes } = useTaco({ domain, - provider, ritualId, + walletClient, + publicClient, }); - if (!isInit || !provider) { + if (!isInit || !walletClient) { return
Loading...
; } const encryptMessage = async () => { - if(!provider) return; - setEncrypting(true) + if (!walletClient) { + return; + } + setEncrypting(true); try { - const signer = provider.getSigner(); const hasPositiveBalance = new conditions.RpcCondition({ chain: 80001, method: 'eth_getBalance', @@ -82,41 +90,60 @@ function App() { const encryptedBytes = await encryptDataToBytes( message, hasPositiveBalance, - signer, + publicClient, + walletClient, ); if (encryptedBytes) { - setEncryptedText(toHexString(encryptedBytes)) + setEncryptedText(toHexString(encryptedBytes)); } } catch (e) { - console.log(e) + console.log(e); } - setEncrypting(false) - } + setEncrypting(false); + }; const decryptMessage = async () => { - if(!encryptedText || !provider) return + if (!encryptedText || !walletClient || !publicClient) { + return; + } try { - setDecrypting(true) - const signer = provider.getSigner(); - + setDecrypting(true); console.log('Decrypting message...'); const decryptedMessage = await decryptDataFromBytes( fromHexString(encryptedText), - signer, + publicClient, + walletClient, ); if (decryptedMessage) { setDecryptedMessage(fromBytes(decryptedMessage)); } } catch (e) { - console.log(e) + console.log(e); } - setDecrypting(false) + setDecrypting(false); }; return (
-

Secret message: setMessage(e.target.value)} onClick={encryptMessage}/> {encrypting && 'Encrypting...'}

-

Encrypted message: setEncryptedText(e.target.value)} /> {decrypting && 'Decrypting...'}

+

+ Secret message:{' '} + setMessage(e.target.value)} + onClick={encryptMessage} + /> + + {encrypting && 'Encrypting...'} +

+

+ Encrypted message:{' '} + setEncryptedText(e.target.value)} + /> + + {decrypting && 'Decrypting...'} +

{decryptedMessage &&

Decrypted message: {decryptedMessage}

}
); diff --git a/examples/taco/nextjs/src/hooks/useTaco.ts b/examples/taco/nextjs/src/hooks/useTaco.ts index be78a7bb5..653a15005 100644 --- a/examples/taco/nextjs/src/hooks/useTaco.ts +++ b/examples/taco/nextjs/src/hooks/useTaco.ts @@ -7,17 +7,17 @@ import { initialize, ThresholdMessageKit, } from '@nucypher/taco'; -import { ethers } from 'ethers'; import { useCallback, useEffect, useState } from 'react'; +import { PublicClient, WalletClient } from 'viem'; export default function useTaco({ ritualId, domain, - provider, }: { ritualId: number; domain: Domain; - provider: ethers.providers.Provider | undefined; + publicClient?: PublicClient; + walletClient?: WalletClient; }) { const [isInit, setIsInit] = useState(false); @@ -26,38 +26,47 @@ export default function useTaco({ }, []); const decryptDataFromBytes = useCallback( - async (encryptedBytes: Uint8Array, signer?: ethers.Signer) => { - if (!isInit || !provider) return; + async ( + encryptedBytes: Uint8Array, + publicClient?: PublicClient, + walletClient?: WalletClient, + ) => { + if (!isInit || !publicClient || !walletClient) { + return; + } const messageKit = ThresholdMessageKit.fromBytes(encryptedBytes); return decrypt( - provider, + publicClient, domain, messageKit, getPorterUri(domain), - signer, + walletClient, ); }, - [isInit, provider, domain], + [isInit, domain], ); const encryptDataToBytes = useCallback( async ( message: string, condition: conditions.Condition, - encryptorSigner: ethers.Signer, + publicClient?: PublicClient, + walletClient?: WalletClient, ) => { - if (!isInit || !provider) return; + if (!isInit || !publicClient || !walletClient) { + return; + } const messageKit = await encrypt( - provider, + publicClient, domain, message, condition, ritualId, - encryptorSigner, + walletClient, ); return messageKit.toBytes(); }, - [isInit, provider, domain, ritualId], + [isInit, domain, ritualId], ); return { diff --git a/examples/taco/nodejs/README.md b/examples/taco/nodejs/README.md index a0a7578b8..9537598d3 100644 --- a/examples/taco/nodejs/README.md +++ b/examples/taco/nodejs/README.md @@ -10,7 +10,12 @@ This script needs 3 environment variables, that you can set in the `.env` file: * `ENCRYPTOR_PRIVATE_KEY` and `CONSUMER_PRIVATE_KEY`: Hex-encoded private keys for the Encryptor and the Consumer, respectively. -Default values for these variables are provided in `.env.example`, so you can run: +- `RPC_PROVIDER_URL`: For TACo testnet you should use a Polygon Mumbai endpoint. +- `ENCRYPTOR_PRIVATE_KEY` and `CONSUMER_PRIVATE_KEY`: Hex-encoded private keys + for the Encryptor and the Consumer, respectively. + +Default values for these variables are provided in `.env.example`, so you can +run: ```bash cp .env.example .env diff --git a/examples/taco/nodejs/package.json b/examples/taco/nodejs/package.json index ecf9cdb3b..6dc6085e4 100644 --- a/examples/taco/nodejs/package.json +++ b/examples/taco/nodejs/package.json @@ -11,7 +11,9 @@ }, "dependencies": { "@nucypher/taco": "workspace:*", - "dotenv": "^16.3.1", - "ethers": "^5.7.2" + "viem": "^1.19.9" + }, + "devDependencies": { + "dotenv": "^16.3.1" } } diff --git a/examples/taco/nodejs/src/index.ts b/examples/taco/nodejs/src/index.ts index e9595823d..812a7ee42 100644 --- a/examples/taco/nodejs/src/index.ts +++ b/examples/taco/nodejs/src/index.ts @@ -13,7 +13,9 @@ import { toHexString, } from '@nucypher/taco'; import * as dotenv from 'dotenv'; -import { ethers } from 'ethers'; +import { Hex, createPublicClient, createWalletClient, http } from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { polygonMumbai } from 'viem/chains'; dotenv.config(); @@ -34,20 +36,40 @@ if (!consumerPrivateKey) { const domain = domains.TESTNET; const ritualId = 5; // Replace with your own ritual ID -const provider = new ethers.providers.JsonRpcProvider(rpcProviderUrl); + +const publicClient = createPublicClient({ + transport: http(rpcProviderUrl), + chain: polygonMumbai, +}); + +const encryptorAccount = privateKeyToAccount(encryptorPrivateKey); +const encryptorWalletClient = createWalletClient({ + transport: http(rpcProviderUrl), + account: encryptorAccount, + chain: polygonMumbai, +}); + +const consumerAccount = privateKeyToAccount(consumerPrivateKey); +const consumerWalletClient = createWalletClient({ + transport: http(rpcProviderUrl), + account: consumerAccount, + chain: polygonMumbai, +}); const encryptToBytes = async (messageString: string) => { - const encryptorSigner = new ethers.Wallet(encryptorPrivateKey); - console.log( - "Encryptor signer's address:", - await encryptorSigner.getAddress(), - ); + // Make sure the provider is connected to Mumbai testnet + const chainId = await encryptorWalletClient.getChainId(); + if (chainId !== polygonMumbai.id) { + console.error('Please connect to Mumbai testnet'); + } + + console.log("Encryptor signer's address:", encryptorAccount.address); const message = toBytes(messageString); console.log(format('Encrypting message ("%s") ...', messageString)); const hasPositiveBalance = new conditions.RpcCondition({ - chain: 80001, + chain: polygonMumbai.id, method: 'eth_getBalance', parameters: [':userAddress', 'latest'], returnValueTest: { @@ -61,39 +83,35 @@ const encryptToBytes = async (messageString: string) => { ); const messageKit = await encrypt( - provider, + publicClient, domain, message, hasPositiveBalance, ritualId, - encryptorSigner, + encryptorWalletClient, ); return messageKit.toBytes(); }; const decryptFromBytes = async (encryptedBytes: Uint8Array) => { - const consumerSigner = new ethers.Wallet(consumerPrivateKey); - console.log( - "\nConsumer signer's address:", - await consumerSigner.getAddress(), - ); + console.log("\nConsumer signer's address:", consumerAccount.address); const messageKit = ThresholdMessageKit.fromBytes(encryptedBytes); console.log('Decrypting message ...'); return decrypt( - provider, + publicClient, domain, messageKit, getPorterUri(domain), - consumerSigner, + consumerWalletClient, ); }; const runExample = async () => { // Make sure the provider is connected to Mumbai testnet - const network = await provider.getNetwork(); - if (network.chainId !== 80001) { + const chainId = await publicClient.getChainId(); + if (chainId !== polygonMumbai.id) { console.error('Please connect to Mumbai testnet'); } await initialize(); diff --git a/examples/taco/react/package.json b/examples/taco/react/package.json index 4085765a4..23d07c6b2 100644 --- a/examples/taco/react/package.json +++ b/examples/taco/react/package.json @@ -26,15 +26,13 @@ "@nucypher/shared": "workspace:*", "@nucypher/taco": "workspace:*", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "viem": "^1.19.9" }, "devDependencies": { "@types/node": "^20.10.0", "@types/react": "^18.2.45", "@types/react-dom": "^18.2.14", "react-scripts": "^5.0.1" - }, - "peerDependencies": { - "ethers": "^5.7.2" } } diff --git a/examples/taco/react/src/App.tsx b/examples/taco/react/src/App.tsx index 9820d7d65..2b83e6751 100644 --- a/examples/taco/react/src/App.tsx +++ b/examples/taco/react/src/App.tsx @@ -1,8 +1,14 @@ -import {fromHexString} from "@nucypher/shared"; -import {conditions, domains, fromBytes, toHexString} from '@nucypher/taco'; -import {ethers} from 'ethers'; -import {hexlify} from 'ethers/lib/utils'; -import {useEffect, useState} from 'react'; +import { fromHexString } from '@nucypher/shared'; +import { conditions, domains, fromBytes, toHexString } from '@nucypher/taco'; +import { useEffect, useState } from 'react'; +import { + createPublicClient, + createWalletClient, + custom, + PublicClient, + toHex, + WalletClient, +} from 'viem'; import useTaco from './hooks/useTaco'; @@ -13,58 +19,65 @@ const ritualId = 5; // Replace with your own ritual ID const domain = domains.TESTNET; function App() { - const [provider, setProvider] = useState< - ethers.providers.Web3Provider | undefined - >(); - const [message, setMessage] = useState('this is a secret') - const [encrypting, setEncrypting] = useState(false) - const [encryptedText, setEncryptedText] = useState( - '', - ); - const [decrypting, setDecrypting] = useState(false) + const [walletClient, setWalletClient] = useState(); + const [publicClient, setPublicClient] = 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 () => { + const loadWeb3Clients = async () => { if (!window.ethereum) { console.error('You need to connect to your wallet first'); + return; } - const provider = new ethers.providers.Web3Provider(window.ethereum, 'any'); - const { chainId } = await provider.getNetwork(); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + const chainId = await walletClient.getChainId(); + const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet await window.ethereum.request({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } - await provider.send('eth_requestAccounts', []); - setProvider(provider); + await walletClient.getAddresses(); + setPublicClient(publicClient); + setWalletClient(walletClient); }; useEffect(() => { - loadWeb3Provider(); + loadWeb3Clients(); }, []); const { isInit, encryptDataToBytes, decryptDataFromBytes } = useTaco({ domain, - provider, ritualId, + walletClient, + publicClient, }); - if (!isInit || !provider) { + if (!isInit || !walletClient) { return
Loading...
; } const encryptMessage = async () => { - if(!provider) return; - setEncrypting(true) + if (!walletClient) { + return; + } + setEncrypting(true); try { - const signer = provider.getSigner(); const hasPositiveBalance = new conditions.RpcCondition({ chain: 80001, method: 'eth_getBalance', @@ -79,41 +92,60 @@ function App() { const encryptedBytes = await encryptDataToBytes( message, hasPositiveBalance, - signer, + publicClient, + walletClient, ); if (encryptedBytes) { - setEncryptedText(toHexString(encryptedBytes)) + setEncryptedText(toHexString(encryptedBytes)); } } catch (e) { - console.log(e) + console.log(e); } - setEncrypting(false) - } + setEncrypting(false); + }; const decryptMessage = async () => { - if(!encryptedText || !provider) return + if (!encryptedText || !walletClient) { + return; + } try { - setDecrypting(true) - const signer = provider.getSigner(); - + setDecrypting(true); console.log('Decrypting message...'); const decryptedMessage = await decryptDataFromBytes( fromHexString(encryptedText), - signer, + publicClient, + walletClient, ); if (decryptedMessage) { setDecryptedMessage(fromBytes(decryptedMessage)); } } catch (e) { - console.log(e) + console.log(e); } - setDecrypting(false) + setDecrypting(false); }; return (
-

Secret message: setMessage(e.target.value)} onClick={encryptMessage}/> {encrypting && 'Encrypting...'}

-

Encrypted message: setEncryptedText(e.target.value)} /> {decrypting && 'Decrypting...'}

+

+ Secret message:{' '} + setMessage(e.target.value)} + onClick={encryptMessage} + /> + + {encrypting && 'Encrypting...'} +

+

+ Encrypted message:{' '} + setEncryptedText(e.target.value)} + /> + + {decrypting && 'Decrypting...'} +

{decryptedMessage &&

Decrypted message: {decryptedMessage}

}
); diff --git a/examples/taco/react/src/hooks/useTaco.ts b/examples/taco/react/src/hooks/useTaco.ts index ed7732eb6..653a15005 100644 --- a/examples/taco/react/src/hooks/useTaco.ts +++ b/examples/taco/react/src/hooks/useTaco.ts @@ -1,4 +1,5 @@ import { + conditions, decrypt, Domain, encrypt, @@ -6,18 +7,17 @@ import { initialize, ThresholdMessageKit, } from '@nucypher/taco'; -import { Condition } from '@nucypher/taco/src/conditions'; -import { ethers } from 'ethers'; import { useCallback, useEffect, useState } from 'react'; +import { PublicClient, WalletClient } from 'viem'; export default function useTaco({ ritualId, domain, - provider, }: { ritualId: number; domain: Domain; - provider: ethers.providers.Provider | undefined; + publicClient?: PublicClient; + walletClient?: WalletClient; }) { const [isInit, setIsInit] = useState(false); @@ -26,38 +26,47 @@ export default function useTaco({ }, []); const decryptDataFromBytes = useCallback( - async (encryptedBytes: Uint8Array, signer?: ethers.Signer) => { - if (!isInit || !provider) return; + async ( + encryptedBytes: Uint8Array, + publicClient?: PublicClient, + walletClient?: WalletClient, + ) => { + if (!isInit || !publicClient || !walletClient) { + return; + } const messageKit = ThresholdMessageKit.fromBytes(encryptedBytes); return decrypt( - provider, + publicClient, domain, messageKit, getPorterUri(domain), - signer, + walletClient, ); }, - [isInit, provider, domain], + [isInit, domain], ); const encryptDataToBytes = useCallback( async ( message: string, - condition: Condition, - encryptorSigner: ethers.Signer, + condition: conditions.Condition, + publicClient?: PublicClient, + walletClient?: WalletClient, ) => { - if (!isInit || !provider) return; + if (!isInit || !publicClient || !walletClient) { + return; + } const messageKit = await encrypt( - provider, + publicClient, domain, message, condition, ritualId, - encryptorSigner, + walletClient, ); return messageKit.toBytes(); }, - [isInit, provider, domain, ritualId], + [isInit, domain, ritualId], ); return { diff --git a/examples/taco/react/src/index.tsx b/examples/taco/react/src/index.tsx index 253d3f9b3..3f83eeaea 100644 --- a/examples/taco/react/src/index.tsx +++ b/examples/taco/react/src/index.tsx @@ -1,8 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css' import App from './App'; +import './index.css'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, diff --git a/examples/taco/react/src/react-app-env.d.ts b/examples/taco/react/src/react-app-env.d.ts deleted file mode 100644 index 8f8826530..000000000 --- a/examples/taco/react/src/react-app-env.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// -import { ExternalProvider } from '@ethersproject/providers'; - -declare global { - interface Window { - ethereum?: ExternalProvider; - } -} diff --git a/examples/taco/webpack-5/package.json b/examples/taco/webpack-5/package.json index 4266b882a..3a67f4853 100644 --- a/examples/taco/webpack-5/package.json +++ b/examples/taco/webpack-5/package.json @@ -12,7 +12,9 @@ "type-check": "tsc" }, "dependencies": { - "@nucypher/taco": "workspace:*" + "@nucypher/taco": "workspace:*", + "viem": "^1.19.9", + "ethers": "^5.7.2" }, "devDependencies": { "copy-webpack-plugin": "^11.0.0", @@ -20,8 +22,5 @@ "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.7.4" - }, - "peerDependencies": { - "ethers": "^5.7.2" } } diff --git a/examples/taco/webpack-5/src/index.html b/examples/taco/webpack-5/src/index.html index ad1f3fd35..6aaef3537 100644 --- a/examples/taco/webpack-5/src/index.html +++ b/examples/taco/webpack-5/src/index.html @@ -8,7 +8,8 @@ + javascript in your browser.

Check console for results

diff --git a/examples/taco/webpack-5/src/index.ts b/examples/taco/webpack-5/src/index.ts index ebf81dd5c..e169bf2f5 100644 --- a/examples/taco/webpack-5/src/index.ts +++ b/examples/taco/webpack-5/src/index.ts @@ -8,8 +8,7 @@ import { initialize, toBytes, } from '@nucypher/taco'; -import { ethers } from 'ethers'; -import { hexlify } from 'ethers/lib/utils'; +import { createPublicClient, createWalletClient, custom, toHex } from 'viem'; // eslint-disable-next-line @typescript-eslint/no-explicit-any declare const window: any; @@ -20,20 +19,24 @@ const runExample = async () => { const ritualId = 5; // Replace with your own ritual ID const domain = domains.TESTNET; - const provider = new ethers.providers.Web3Provider(window.ethereum!, 'any'); - await provider.send('eth_requestAccounts', []); - const signer = provider.getSigner(); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); + const chainId = await walletClient.getChainId(); + const [address] = await walletClient.getAddresses(); - const { chainId } = await provider.getNetwork(); const mumbaiChainId = 80001; if (chainId !== mumbaiChainId) { // Switch to Polygon Mumbai testnet await window.ethereum!.request!({ method: 'wallet_switchEthereumChain', - params: [{ chainId: hexlify(mumbaiChainId) }], + params: [{ chainId: toHex(mumbaiChainId) }], }); } - console.log("Signer's address:", await signer.getAddress()); + console.log("Signer's address:", address); console.log('Encrypting message...'); const message = toBytes('this is a secret'); @@ -51,21 +54,21 @@ const runExample = async () => { 'Condition requires signer', ); const messageKit = await encrypt( - provider, + publicClient, domain, message, hasPositiveBalance, ritualId, - signer, + walletClient, ); console.log('Decrypting message...'); const decryptedBytes = await decrypt( - provider, + publicClient, domain, messageKit, getPorterUri(domain), - signer, + walletClient, ); const decryptedMessage = fromBytes(decryptedBytes); console.log('Decrypted message:', decryptedMessage); diff --git a/packages/pre/package.json b/packages/pre/package.json index 0053ffdb0..020402998 100644 --- a/packages/pre/package.json +++ b/packages/pre/package.json @@ -40,7 +40,8 @@ "dependencies": { "@nucypher/nucypher-core": "*", "@nucypher/shared": "workspace:*", - "ethers": "^5.7.2" + "ethers": "^5.7.2", + "viem": "^1.19.9" }, "devDependencies": { "@nucypher/test-utils": "workspace:*" diff --git a/packages/pre/src/characters/alice.ts b/packages/pre/src/characters/alice.ts index 45082af1d..1278b4943 100644 --- a/packages/pre/src/characters/alice.ts +++ b/packages/pre/src/characters/alice.ts @@ -4,8 +4,14 @@ import { Signer, VerifiedKeyFrag, } from '@nucypher/nucypher-core'; -import { ChecksumAddress, Domain, PorterClient } from '@nucypher/shared'; -import { ethers } from 'ethers'; +import { + ChecksumAddress, + Domain, + PorterClient, + toPublicClient, +} from '@nucypher/shared'; +import { PublicClient, WalletClient } from 'viem'; +import { getBlock, getBlockNumber } from 'viem/actions'; import { Keyring } from '../keyring'; import { @@ -41,8 +47,7 @@ export class Alice { } public async grant( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, porterUri: string, policyParameters: BlockchainPolicyParameters, @@ -55,12 +60,12 @@ export class Alice { excludeUrsulas, includeUrsulas, ); - const policy = await this.createPolicy(provider, policyParameters); - return await policy.enact(provider, signer, domain, ursulas); + const policy = await this.createPolicy(walletClient, policyParameters); + return await policy.enact(walletClient, domain, ursulas); } public async generatePreEnactedPolicy( - provider: ethers.providers.Provider, + walletClient: WalletClient, porterUri: string, policyParameters: BlockchainPolicyParameters, includeUrsulas?: readonly ChecksumAddress[], @@ -72,7 +77,7 @@ export class Alice { excludeUrsulas, includeUrsulas, ); - const policy = await this.createPolicy(provider, policyParameters); + const policy = await this.createPolicy(walletClient, policyParameters); return await policy.generatePreEnactedPolicy(ursulas); } @@ -95,11 +100,12 @@ export class Alice { } private async createPolicy( - provider: ethers.providers.Provider, + walletClient: WalletClient, rawParameters: BlockchainPolicyParameters, ): Promise { + const publicClient = toPublicClient(walletClient); const { bob, label, threshold, shares, startDate, endDate } = - await this.validatePolicyParameters(provider, rawParameters); + await this.validatePolicyParameters(publicClient, rawParameters); const { delegatingKey, verifiedKFrags } = this.generateKFrags( bob, label, @@ -120,7 +126,7 @@ export class Alice { } private async validatePolicyParameters( - provider: ethers.providers.Provider, + publicClient: PublicClient, rawParams: BlockchainPolicyParameters, ): Promise { const startDate = rawParams.startDate ?? new Date(); @@ -142,9 +148,9 @@ export class Alice { ); } - const blockNumber = await provider.getBlockNumber(); - const block = await provider.getBlock(blockNumber); - const blockTime = new Date(block.timestamp * 1000); + const blockNumber = await getBlockNumber(publicClient); + const block = await getBlock(publicClient, { blockNumber }); + const blockTime = new Date(Number(block.timestamp) * 1000); if (endDate < blockTime) { throw new Error( `End date must be in the future, ${endDate} is earlier than block time ${blockTime}).`, diff --git a/packages/pre/src/policy.ts b/packages/pre/src/policy.ts index 0f593b1e4..301164ca4 100644 --- a/packages/pre/src/policy.ts +++ b/packages/pre/src/policy.ts @@ -13,10 +13,11 @@ import { toBytes, toCanonicalAddress, toEpoch, + toPublicClient, Ursula, zip, } from '@nucypher/shared'; -import { ethers } from 'ethers'; +import { WalletClient } from 'viem'; import { Alice, RemoteBob } from './characters'; @@ -47,11 +48,10 @@ export class PreEnactedPolicy implements IPreEnactedPolicy { ) {} public async enact( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ): Promise { - const txHash = await this.publish(provider, signer, domain); + const txHash = await this.publish(walletClient, domain); return { ...this, txHash, @@ -59,23 +59,25 @@ export class PreEnactedPolicy implements IPreEnactedPolicy { } private async publish( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ): Promise { + const publicClient = toPublicClient(walletClient); const startTimestamp = toEpoch(this.startTimestamp); const endTimestamp = toEpoch(this.endTimestamp); - const ownerAddress = await signer.getAddress(); + const ownerAddress = await walletClient.account?.address; + if (!ownerAddress) { + throw new Error('No account set'); + } const value = await PreSubscriptionManagerAgent.getPolicyCost( - provider, + publicClient, domain, this.size, startTimestamp, endTimestamp, ); const tx = await PreSubscriptionManagerAgent.createPolicy( - provider, - signer, + walletClient, domain, value, this.id.toBytes(), @@ -119,13 +121,12 @@ export class BlockchainPolicy { } public async enact( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ursulas: readonly Ursula[], ): Promise { const preEnacted = await this.generatePreEnactedPolicy(ursulas); - return await preEnacted.enact(provider, signer, domain); + return await preEnacted.enact(walletClient, domain); } public async generatePreEnactedPolicy( diff --git a/packages/pre/test/acceptance/alice-grants.test.ts b/packages/pre/test/acceptance/alice-grants.test.ts index d5f02c487..9cb51db1a 100644 --- a/packages/pre/test/acceptance/alice-grants.test.ts +++ b/packages/pre/test/acceptance/alice-grants.test.ts @@ -9,9 +9,8 @@ import { ChecksumAddress, domains, initialize, Ursula } from '@nucypher/shared'; import { bytesEqual, fakePorterUri, - fakeProvider, - fakeSigner, fakeUrsulas, + fakeWalletClient, fromBytes, mockGetUrsulas, mockRetrieveCFragsRequest, @@ -71,8 +70,7 @@ describe('story: alice shares message with bob through policy', () => { endDate, }; policy = await alice.grant( - fakeProvider(), - fakeSigner(), + fakeWalletClient, domains.DEV, fakePorterUri, policyParams, diff --git a/packages/pre/test/acceptance/delay-enact.test.ts b/packages/pre/test/acceptance/delay-enact.test.ts index efdb9f946..ad9403d00 100644 --- a/packages/pre/test/acceptance/delay-enact.test.ts +++ b/packages/pre/test/acceptance/delay-enact.test.ts @@ -1,9 +1,8 @@ import { bytesEqual, fakePorterUri, - fakeProvider, - fakeSigner, fakeUrsulas, + fakeWalletClient, mockGetUrsulas, } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -30,7 +29,6 @@ describe('story: alice creates a policy but someone else enacts it', () => { }); it('alice generates a new policy', async () => { - const provider = fakeProvider(); const getUrsulasSpy = mockGetUrsulas(fakeUrsulas(shares)); const generateKFragsSpy = mockGenerateKFrags(); const publishToBlockchainSpy = mockPublishToBlockchain(); @@ -47,7 +45,7 @@ describe('story: alice creates a policy but someone else enacts it', () => { }; const preEnactedPolicy = await alice.generatePreEnactedPolicy( - provider, + fakeWalletClient, fakePorterUri, policyParams, ); @@ -60,8 +58,7 @@ describe('story: alice creates a policy but someone else enacts it', () => { expect(preEnactedPolicy.label).toBe(label); const enacted = await preEnactedPolicy.enact( - provider, - fakeSigner(), + fakeWalletClient, domains.DEV, ); expect(enacted.txHash).toBeDefined(); diff --git a/packages/shared/package.json b/packages/shared/package.json index 533f75dcf..dec8bb17f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -47,7 +47,8 @@ "axios": "^1.6.2", "deep-equal": "^2.2.3", "ethers": "^5.7.2", - "qs": "^6.10.1" + "qs": "^6.10.1", + "viem": "^1.19.9" }, "devDependencies": { "@typechain/ethers-v5": "^11.1.2", diff --git a/packages/shared/src/contracts/agents/coordinator.ts b/packages/shared/src/contracts/agents/coordinator.ts index 71721cc6e..f9435f984 100644 --- a/packages/shared/src/contracts/agents/coordinator.ts +++ b/packages/shared/src/contracts/agents/coordinator.ts @@ -4,11 +4,13 @@ import { SessionStaticKey, ThresholdMessageKit, } from '@nucypher/nucypher-core'; -import { BigNumberish, ethers } from 'ethers'; +import { BigNumberish } from 'ethers'; +import { PublicClient, WalletClient } from 'viem'; import { Domain } from '../../porter'; import { ChecksumAddress } from '../../types'; import { fromHexString } from '../../utils'; +import { publicClientToProvider, walletClientToSigner } from '../../viem'; import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const'; import { Coordinator__factory } from '../ethers-typechain'; import { BLS12381, Coordinator } from '../ethers-typechain/Coordinator'; @@ -46,11 +48,11 @@ export enum DkgRitualState { export class DkgCoordinatorAgent { public static async getParticipants( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); const participants = await coordinator.getParticipants(ritualId); return participants.map((participant) => { @@ -65,15 +67,14 @@ export class DkgCoordinatorAgent { } public static async initializeRitual( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, providers: ChecksumAddress[], authority: string, duration: BigNumberish, accessController: string, ): Promise { - const coordinator = await this.connectReadWrite(provider, domain, signer); + const coordinator = await this.connectReadWrite(walletClient, domain); const tx = await coordinator.initiateRitual( providers, authority, @@ -89,30 +90,30 @@ export class DkgCoordinatorAgent { } public static async getRitual( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); return await coordinator.rituals(ritualId); } public static async getRitualState( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); return await coordinator.getRitualState(ritualId); } public static async onRitualEndEvent( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, callback: (successful: boolean) => void, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); // We leave `initiator` undefined because we don't care who the initiator is // We leave `successful` undefined because we don't care if the ritual was successful const eventFilter = coordinator.filters.EndRitual(ritualId, undefined); @@ -122,11 +123,11 @@ export class DkgCoordinatorAgent { } public static async getRitualIdFromPublicKey( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, dkgPublicKey: DkgPublicKey, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); const dkgPublicKeyBytes = dkgPublicKey.toBytes(); const pointStruct: BLS12381.G1PointStruct = { word0: dkgPublicKeyBytes.slice(0, 32), @@ -136,12 +137,12 @@ export class DkgCoordinatorAgent { } public static async isEncryptionAuthorized( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, thresholdMessageKit: ThresholdMessageKit, ): Promise { - const coordinator = await this.connectReadOnly(provider, domain); + const coordinator = await this.connectReadOnly(publicClient, domain); return await coordinator.isEncryptionAuthorized( ritualId, thresholdMessageKit.acp.authorization, @@ -150,27 +151,26 @@ export class DkgCoordinatorAgent { } private static async connectReadOnly( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, - ) { - return await this.connect(provider, domain); + ): Promise { + const chainId = await publicClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'Coordinator'); + return Coordinator__factory.connect( + contractAddress, + publicClientToProvider(publicClient), + ); } private static async connectReadWrite( - provider: ethers.providers.Provider, - domain: Domain, - signer: ethers.Signer, - ) { - return await this.connect(provider, domain, signer); - } - - private static async connect( - provider: ethers.providers.Provider, + walletClient: WalletClient, domain: Domain, - signer?: ethers.Signer, ): Promise { - const network = await provider.getNetwork(); - const contractAddress = getContract(domain, network.chainId, 'Coordinator'); - return Coordinator__factory.connect(contractAddress, signer ?? provider); + const chainId = await walletClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'Coordinator'); + return Coordinator__factory.connect( + contractAddress, + walletClientToSigner(walletClient), + ); } } diff --git a/packages/shared/src/contracts/agents/global-allow-list.ts b/packages/shared/src/contracts/agents/global-allow-list.ts index fc09d5946..c1216593b 100644 --- a/packages/shared/src/contracts/agents/global-allow-list.ts +++ b/packages/shared/src/contracts/agents/global-allow-list.ts @@ -1,57 +1,45 @@ import { getContract } from '@nucypher/nucypher-contracts'; -import { ethers } from 'ethers'; +import { PublicClient, WalletClient } from 'viem'; import { Domain } from '../../porter'; import { ChecksumAddress } from '../../types'; +import { publicClientToProvider, walletClientToSigner } from '../../viem'; import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const'; -import { GlobalAllowList, GlobalAllowList__factory } from '../ethers-typechain'; +import { GlobalAllowList__factory } from '../ethers-typechain'; export class GlobalAllowListAgent { public static async registerEncrypters( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ritualId: number, encrypters: ChecksumAddress[], ): Promise { - const globalAllowList = await this.connectReadWrite( - provider, - domain, - signer, - ); + const globalAllowList = await this.connectReadWrite(walletClient, domain); const tx = await globalAllowList.authorize(ritualId, encrypters); await tx.wait(DEFAULT_WAIT_N_CONFIRMATIONS); } private static async connectReadOnly( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ) { - return await this.connect(provider, domain); + const chainId = await publicClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'GlobalAllowList'); + return GlobalAllowList__factory.connect( + contractAddress, + publicClientToProvider(publicClient), + ); } private static async connectReadWrite( - provider: ethers.providers.Provider, + walletClient: WalletClient, domain: Domain, - signer: ethers.Signer, ) { - return await this.connect(provider, domain, signer); - } - - private static async connect( - provider: ethers.providers.Provider, - domain: Domain, - signer?: ethers.Signer, - ): Promise { - const network = await provider.getNetwork(); - const contractAddress = getContract( - domain, - network.chainId, - 'GlobalAllowList', - ); + const chainId = await walletClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'GlobalAllowList'); return GlobalAllowList__factory.connect( contractAddress, - signer ?? provider, + walletClientToSigner(walletClient), ); } } diff --git a/packages/shared/src/contracts/agents/subscription-manager.ts b/packages/shared/src/contracts/agents/subscription-manager.ts index 20f787300..7a101791e 100644 --- a/packages/shared/src/contracts/agents/subscription-manager.ts +++ b/packages/shared/src/contracts/agents/subscription-manager.ts @@ -1,23 +1,16 @@ import { getContract } from '@nucypher/nucypher-contracts'; -import { - BigNumber, - ContractTransaction, - ethers, - utils as ethersUtils, -} from 'ethers'; +import { BigNumber, ContractTransaction, utils as ethersUtils } from 'ethers'; +import { PublicClient, WalletClient } from 'viem'; import { Domain } from '../../porter'; import { ChecksumAddress } from '../../types'; +import { publicClientToProvider, walletClientToSigner } from '../../viem'; import { DEFAULT_WAIT_N_CONFIRMATIONS } from '../const'; -import { - SubscriptionManager, - SubscriptionManager__factory, -} from '../ethers-typechain'; +import { SubscriptionManager__factory } from '../ethers-typechain'; export class PreSubscriptionManagerAgent { public static async createPolicy( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, valueInWei: BigNumber, policyId: Uint8Array, @@ -27,9 +20,8 @@ export class PreSubscriptionManagerAgent { ownerAddress: ChecksumAddress, ): Promise { const subscriptionManager = await this.connectReadWrite( - provider, + walletClient, domain, - signer, ); const overrides = { value: valueInWei.toString(), @@ -55,13 +47,16 @@ export class PreSubscriptionManagerAgent { } public static async getPolicyCost( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, size: number, startTimestamp: number, endTimestamp: number, ): Promise { - const subscriptionManager = await this.connectReadOnly(provider, domain); + const subscriptionManager = await this.connectReadOnly( + publicClient, + domain, + ); return await subscriptionManager.getPolicyCost( size, startTimestamp, @@ -70,34 +65,26 @@ export class PreSubscriptionManagerAgent { } private static async connectReadOnly( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ) { - return await this.connect(provider, domain); + const chainId = await publicClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'SubscriptionManager'); + return SubscriptionManager__factory.connect( + contractAddress, + publicClientToProvider(publicClient), + ); } private static async connectReadWrite( - provider: ethers.providers.Provider, + walletClient: WalletClient, domain: Domain, - signer: ethers.Signer, ) { - return await this.connect(provider, domain, signer); - } - - private static async connect( - provider: ethers.providers.Provider, - domain: Domain, - signer?: ethers.Signer, - ): Promise { - const network = await provider.getNetwork(); - const contractAddress = getContract( - domain, - network.chainId, - 'SubscriptionManager', - ); + const chainId = await walletClient.getChainId(); + const contractAddress = getContract(domain, chainId, 'SubscriptionManager'); return SubscriptionManager__factory.connect( contractAddress, - signer ?? provider, + walletClientToSigner(walletClient), ); } } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 63f08aa5a..a67761818 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -2,6 +2,7 @@ export * from './contracts'; export * from './porter'; export type * from './types'; export * from './utils'; +export * from './viem'; export * from './web3'; // Re-exports diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index f9f67bdd5..6e45477de 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -1,6 +1,5 @@ import deepEqual from 'deep-equal'; -// TODO: Replace byte and hex manipulation with ethers.js export const toBytes = (str: string): Uint8Array => new TextEncoder().encode(str); diff --git a/packages/shared/src/viem.ts b/packages/shared/src/viem.ts new file mode 100644 index 000000000..c99f75f26 --- /dev/null +++ b/packages/shared/src/viem.ts @@ -0,0 +1,69 @@ +import { providers } from 'ethers'; +import { + createPublicClient, + http, + HttpTransport, + PublicClient, + WalletClient, + webSocket, +} from 'viem'; + +const transportForTransportType = (type: string) => { + switch (type) { + case 'http': + return http(); + case 'webSocket': + return webSocket(); + default: + throw new Error(`Unknown transport type: ${type}`); + } +}; + +export const toPublicClient = (client: WalletClient): PublicClient => + createPublicClient({ + chain: client.chain, + transport: transportForTransportType(client.transport.type), + }); + +// We need to convert from our PublicClient and WalletClient to ethers.js because we +// rely on typechain to generate our typescript bindings for our contracts, and there +// is no support for viem in typechain, so we use our legacy ethers.js bindings instead. + +// Adapted from: https://wagmi.sh/react/ethers-adapters + +export const publicClientToProvider = (publicClient: PublicClient) => { + const { chain, transport } = publicClient; + if (!chain) { + throw new Error('chain is undefined'); + } + const network = { + chainId: chain.id, + name: chain.name, + }; + if (transport.type === 'fallback') { + return new providers.FallbackProvider( + (transport.transports as ReturnType[]).map( + ({ value }) => new providers.JsonRpcProvider(value?.url, network), + ), + ); + } + return new providers.JsonRpcProvider(transport.url, network); +}; + +export const walletClientToSigner = (walletClient: WalletClient) => { + const { account, chain, transport } = walletClient; + if (!account) { + throw new Error('account is undefined'); + } + if (!chain) { + throw new Error('chain is undefined'); + } + const network = { + chainId: chain.id, + name: chain.name, + ensAddress: chain.contracts!.ensRegistry!.address, + }; + const provider = new providers.Web3Provider(transport, network); + + return provider.getSigner(account.address); +}; diff --git a/packages/taco/examples/encrypt-decrypt.ts b/packages/taco/examples/encrypt-decrypt.ts index 9f1154cd5..a60ffd42a 100644 --- a/packages/taco/examples/encrypt-decrypt.ts +++ b/packages/taco/examples/encrypt-decrypt.ts @@ -1,5 +1,6 @@ -import { ethers } from 'ethers'; +import { createPublicClient, createWalletClient, custom } from 'viem'; +import 'viem/window'; import { conditions, decrypt, @@ -16,40 +17,56 @@ const ritualId = 1; const run = async () => { // The data encryptor runs this part const doEncrypt = async (message: string) => { + if (!window.ethereum) { + throw new Error('No Ethereum provider detected'); + } + // We have to initialize TACo library first await initialize(); - // @ts-ignore - const web3Provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const ownsNFT = new conditions.predefined.ERC721Ownership({ contractAddress: '0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77', parameters: [3591], chain: 5, }); const messageKit = await encrypt( - web3Provider, + publicClient, domains.TESTNET, message, ownsNFT, ritualId, - web3Provider.getSigner(), + walletClient, ); return messageKit; }; // The data recipient runs this part const doDecrypt = async (messageKit: ThresholdMessageKit) => { + if (!window.ethereum) { + throw new Error('No Ethereum provider detected'); + } + // We have to initialize TACo library first await initialize(); - // @ts-ignore - const web3Provider = new ethers.providers.Web3Provider(window.ethereum); + const publicClient = createPublicClient({ + transport: custom(window.ethereum), + }); + const walletClient = createWalletClient({ + transport: custom(window.ethereum), + }); const decryptedMessage = await decrypt( - web3Provider, + publicClient, domains.TESTNET, messageKit, getPorterUri(domains.TESTNET), - web3Provider.getSigner(), + walletClient, ); return decryptedMessage; }; diff --git a/packages/taco/package.json b/packages/taco/package.json index e3b71fb74..e013d1c2b 100644 --- a/packages/taco/package.json +++ b/packages/taco/package.json @@ -44,15 +44,14 @@ "@nucypher/shared": "^0.1.0-rc.4", "ethers": "^5.7.2", "semver": "^7.5.2", + "viem": "^1.19.9", "zod": "^3.22.4" }, "devDependencies": { + "@nucypher/shared": "workspace:*", "@nucypher/test-utils": "workspace:*", "@types/semver": "^7.5.6" }, - "peerDependencies": { - "@nucypher/shared": "workspace:*" - }, "engines": { "node": ">=18", "pnpm": ">=8.0.0" diff --git a/packages/taco/src/conditions/condition-expr.ts b/packages/taco/src/conditions/condition-expr.ts index da2fa3c95..85b4fb3bb 100644 --- a/packages/taco/src/conditions/condition-expr.ts +++ b/packages/taco/src/conditions/condition-expr.ts @@ -1,7 +1,7 @@ import { Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { toJSON } from '@nucypher/shared'; -import { ethers } from 'ethers'; import { SemVer } from 'semver'; +import { PublicClient, WalletClient } from 'viem'; import { Condition } from './condition'; import { ConditionContext, CustomContextParam } from './context'; @@ -64,15 +64,15 @@ export class ConditionExpression { } public buildContext( - provider: ethers.providers.Provider, + publicClient: PublicClient, customParameters: Record = {}, - signer?: ethers.Signer, + walletClient?: WalletClient, ): ConditionContext { return new ConditionContext( - provider, + publicClient, this.condition, customParameters, - signer, + walletClient, ); } diff --git a/packages/taco/src/conditions/context/context.ts b/packages/taco/src/conditions/context/context.ts index 7b528f70e..6dd62369a 100644 --- a/packages/taco/src/conditions/context/context.ts +++ b/packages/taco/src/conditions/context/context.ts @@ -1,6 +1,6 @@ import { Context, Conditions as WASMConditions } from '@nucypher/nucypher-core'; import { fromJSON, toJSON } from '@nucypher/shared'; -import { ethers } from 'ethers'; +import { PublicClient, WalletClient } from 'viem'; import { CompoundConditionType } from '../compound-condition'; import { Condition, ConditionProps } from '../condition'; @@ -32,15 +32,14 @@ export class ConditionContext { private readonly walletAuthProvider?: WalletAuthenticationProvider; constructor( - private readonly provider: ethers.providers.Provider, + private readonly publicClient: PublicClient, private readonly condition: Condition, public readonly customParameters: Record = {}, - private readonly signer?: ethers.Signer, + public readonly walletClient?: WalletClient, ) { - if (this.signer) { + if (this.walletClient) { this.walletAuthProvider = new WalletAuthenticationProvider( - this.provider, - this.signer, + this.walletClient, ); } this.validate(); @@ -56,7 +55,7 @@ export class ConditionContext { } }); - if (this.condition.requiresSigner() && !this.signer) { + if (this.condition.requiresSigner() && !this.walletClient) { throw new Error(ERR_SIGNER_REQUIRED); } } @@ -162,10 +161,10 @@ export class ConditionContext { params: Record, ): ConditionContext { return new ConditionContext( - this.provider, + this.publicClient, this.condition, params, - this.signer, + this.walletClient, ); } @@ -175,18 +174,16 @@ export class ConditionContext { } public static fromConditions( - provider: ethers.providers.Provider, + publicClient: PublicClient, conditions: WASMConditions, - signer?: ethers.Signer, + walletClient?: WalletClient, customParameters?: Record, ): ConditionContext { - const innerCondition = - ConditionExpression.fromWASMConditions(conditions).condition; return new ConditionContext( - provider, - innerCondition, + publicClient, + ConditionExpression.fromWASMConditions(conditions).condition, customParameters, - signer, + walletClient, ); } } diff --git a/packages/taco/src/conditions/context/providers.ts b/packages/taco/src/conditions/context/providers.ts index 4b6c8f43a..7dd5b60b3 100644 --- a/packages/taco/src/conditions/context/providers.ts +++ b/packages/taco/src/conditions/context/providers.ts @@ -1,8 +1,31 @@ -import type { TypedDataSigner } from '@ethersproject/abstract-signer'; -import { ethers } from 'ethers'; -import { utils as ethersUtils } from 'ethers/lib/ethers'; +import { bytesToHex, WalletClient } from 'viem'; +import { getBlock, getBlockNumber, signTypedData } from 'viem/actions'; -import { Eip712TypedData, FormattedTypedData } from '../../web3'; +const ERR_NO_ACCOUNT = 'No account found'; +const ERR_NO_CHAIN_ID = 'No chain ID found'; + +const makeSalt = () => { + const randomBytes = crypto.getRandomValues(new Uint8Array(32)); + return bytesToHex(randomBytes); +}; + +interface Eip712TypedData { + types: { + Wallet: { name: string; type: string }[]; + }; + domain: { + salt: string; + chainId: number; + name: string; + version: string; + }; + message: { + blockHash: string; + address: string; + blockNumber: number; + signatureText: string; + }; +} export interface TypedSignature { signature: string; @@ -19,13 +42,10 @@ interface ChainData { export class WalletAuthenticationProvider { private walletSignature?: Record; - constructor( - private readonly provider: ethers.providers.Provider, - private readonly signer: ethers.Signer, - ) {} + constructor(private readonly client: WalletClient) {} public async getOrCreateWalletSignature(): Promise { - const address = await this.signer.getAddress(); + const { address } = await this.getAccount(); const storageKey = `wallet-signature-${address}`; // If we have a signature in localStorage, return it @@ -63,69 +83,60 @@ export class WalletAuthenticationProvider { private async createWalletSignature(): Promise { // Ensure freshness of the signature const { blockNumber, blockHash, chainId } = await this.getChainData(); - const address = await this.signer.getAddress(); + const account = await this.getAccount(); + const { address } = account; const signatureText = `I'm the owner of address ${address} as of block number ${blockNumber}`; - const salt = ethersUtils.hexlify(ethersUtils.randomBytes(32)); - - const typedData: Eip712TypedData = { - types: { - Wallet: [ - { name: 'address', type: 'address' }, - { name: 'signatureText', type: 'string' }, - { name: 'blockNumber', type: 'uint256' }, - { name: 'blockHash', type: 'bytes32' }, - ], - }, - domain: { - name: 'taco', - version: '1', - chainId, - salt, - }, - message: { - address, - signatureText, - blockNumber, - blockHash, - }, + const salt = makeSalt(); + + const types = { + Wallet: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'salt', type: 'bytes32' }, + ], + }; + const domain = { + name: 'cbd', + version: '1', + chainId, + salt, }; - // https://github.com/ethers-io/ethers.js/issues/1431#issuecomment-813950552 - const signature = await ( - this.signer as unknown as TypedDataSigner - )._signTypedData(typedData.domain, typedData.types, typedData.message); - - const formattedTypedData: FormattedTypedData = { - ...typedData, - primaryType: 'Wallet', - types: { - ...typedData.types, - EIP712Domain: [ - { - name: 'name', - type: 'string', - }, - { - name: 'version', - type: 'string', - }, - { - name: 'chainId', - type: 'uint256', - }, - { - name: 'salt', - type: 'bytes32', - }, - ], - }, + const message = { + address, + signatureText, + blockNumber, + blockHash, }; - return { signature, typedData: formattedTypedData, address }; + const typedData = { + // Normally, we would pass an account address here, but viem needs the account object to figure out whether + // to sign locally or to call JSON-RPC (see {'type': 'local'} field in `account`). + account, + types, + primaryType: 'EIP712Domain', + domain, + message, + } as const; + const signature = await signTypedData(this.client, typedData); + + return { signature, typedData, address }; + } + + private async getAccount() { + const account = this.client.account; + if (!account) { + throw new Error(ERR_NO_ACCOUNT); + } + return account; } private async getChainData(): Promise { - const blockNumber = await this.provider.getBlockNumber(); - const blockHash = (await this.provider.getBlock(blockNumber)).hash; - const chainId = (await this.provider.getNetwork()).chainId; + const blockNumber = Number(await getBlockNumber(this.client)); + const blockHash = (await getBlock(this.client)).hash; + const chainId = this.client.chain?.id; + if (!chainId) { + throw new Error(ERR_NO_CHAIN_ID); + } return { blockNumber, blockHash, chainId }; } } diff --git a/packages/taco/src/dkg.ts b/packages/taco/src/dkg.ts index 8e6ce50ec..293d85bd7 100644 --- a/packages/taco/src/dkg.ts +++ b/packages/taco/src/dkg.ts @@ -5,8 +5,10 @@ import { DkgRitualState, Domain, fromHexString, + toPublicClient, } from '@nucypher/shared'; -import { BigNumberish, ethers } from 'ethers'; +import { BigNumberish } from 'ethers'; +import { PublicClient, WalletClient } from 'viem'; export interface DkgRitualJSON { id: number; @@ -67,8 +69,7 @@ const ERR_RITUAL_NOT_FINALIZED = (ritualId: number, ritual: DkgRitual) => export class DkgClient { public static async initializeRitual( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ursulas: ChecksumAddress[], authority: string, @@ -76,9 +77,9 @@ export class DkgClient { accessController: string, waitUntilEnd = false, ): Promise { + const publicClient = toPublicClient(walletClient); const ritualId = await DkgCoordinatorAgent.initializeRitual( - provider, - signer, + walletClient, domain, ursulas.sort(), // Contract call requires sorted addresses authority, @@ -88,13 +89,13 @@ export class DkgClient { if (waitUntilEnd) { const isSuccessful = await DkgClient.waitUntilRitualEnd( - provider, + publicClient, domain, ritualId, ); if (!isSuccessful) { const ritualState = await DkgCoordinatorAgent.getRitualState( - provider, + publicClient, domain, ritualId, ); @@ -108,7 +109,7 @@ export class DkgClient { } private static waitUntilRitualEnd = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise => { @@ -121,7 +122,7 @@ export class DkgClient { } }; DkgCoordinatorAgent.onRitualEndEvent( - provider, + publicClient, domain, ritualId, callback, @@ -130,17 +131,17 @@ export class DkgClient { }; public static async getRitual( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise { const ritualState = await DkgCoordinatorAgent.getRitualState( - provider, + publicClient, domain, ritualId, ); const ritual = await DkgCoordinatorAgent.getRitual( - provider, + publicClient, domain, ritualId, ); @@ -158,11 +159,11 @@ export class DkgClient { } public static async getActiveRitual( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, ritualId: number, ): Promise { - const ritual = await DkgClient.getRitual(provider, domain, ritualId); + const ritual = await DkgClient.getRitual(publicClient, domain, ritualId); if (ritual.state !== DkgRitualState.ACTIVE) { throw new Error(ERR_RITUAL_NOT_FINALIZED(ritualId, ritual)); } @@ -172,14 +173,14 @@ export class DkgClient { // TODO: Without Validator public key in Coordinator, we cannot verify the // transcript. We need to add it to the Coordinator (nucypher-contracts #77). // public async verifyRitual(ritualId: number): Promise { - // const ritual = await DkgCoordinatorAgent.getRitual(this.provider, ritualId); + // const ritual = await DkgCoordinatorAgent.getRitual(this.publicClient, ritualId); // const participants = await DkgCoordinatorAgent.getParticipants( - // this.provider, + // this.publicClient, // ritualId // ); // // const validatorMessages = participants.map((p) => { - // const validatorAddress = EthereumAddress.fromString(p.provider); + // const validatorAddress = EthereumAddress.fromString(p.publicClient); // const publicKey = FerveoPublicKey.fromBytes(fromHexString(p.???)); // const validator = new Validator(validatorAddress, publicKey); // const transcript = Transcript.fromBytes(fromHexString(p.transcript)); diff --git a/packages/taco/src/taco.ts b/packages/taco/src/taco.ts index a9d9054b2..705ced1df 100644 --- a/packages/taco/src/taco.ts +++ b/packages/taco/src/taco.ts @@ -13,8 +13,9 @@ import { GlobalAllowListAgent, toBytes, } from '@nucypher/shared'; -import { ethers } from 'ethers'; import { keccak256 } from 'ethers/lib/utils'; +import { PublicClient, WalletClient } from 'viem'; +import { signMessage } from 'viem/actions'; import { Condition, @@ -28,7 +29,7 @@ import { retrieveAndDecrypt } from './tdec'; * Encrypts a message under given conditions using a public key from an active DKG ritual. * * @export - * @param {ethers.providers.Provider} provider - Instance of ethers provider which is used to interact with + * @param {PublicClient} publicClient - Instance of viem's PublicClient 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`. @@ -37,7 +38,7 @@ import { retrieveAndDecrypt } from './tdec'; * 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. + * @param {WalletClient} walletClient - 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. @@ -45,12 +46,12 @@ import { retrieveAndDecrypt } from './tdec'; * @throws {Error} If the active DKG Ritual cannot be retrieved an error is thrown. */ export const encrypt = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, message: Uint8Array | string, condition: Condition, ritualId: number, - authSigner: ethers.Signer, + walletClient: WalletClient, // TODO: remove? ): Promise => { // TODO(#264): Enable ritual initialization // if (ritualId === undefined) { @@ -64,13 +65,17 @@ export const encrypt = async ( // // 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); + const dkgRitual = await DkgClient.getActiveRitual( + publicClient, + domain, + ritualId, + ); return await encryptWithPublicKey( message, condition, dkgRitual.dkgPublicKey, - authSigner, + walletClient, ); }; @@ -82,7 +87,7 @@ export const encrypt = async ( * @param {Condition} condition - Condition under which the message will be encrypted. Those conditions must be * satisfied in order to decrypt the message. * @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 {WalletClient} walletClient - 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. @@ -93,7 +98,7 @@ export const encryptWithPublicKey = async ( message: Uint8Array | string, condition: Condition, dkgPublicKey: DkgPublicKey, - authSigner: ethers.Signer, + walletClient: WalletClient, // TODO: remove? ): Promise => { if (typeof message === 'string') { message = toBytes(message); @@ -108,7 +113,14 @@ export const encryptWithPublicKey = async ( ); const headerHash = keccak256(ciphertext.header.toBytes()); - const authorization = await authSigner.signMessage(fromHexString(headerHash)); + const account = walletClient.account; + if (!account) { + throw new Error('WalletClient must be initialized with an account'); + } + const authorization = await signMessage(walletClient, { + account, + message: headerHash, + }); const acp = new AccessControlPolicy( authenticatedData, fromHexString(authorization), @@ -121,14 +133,14 @@ export const encryptWithPublicKey = async ( * Decrypts an encrypted message. * * @export - * @param {ethers.providers.Provider} provider - Instance of ethers provider which is used to interact with + * @param {PublicClient} publicClient - Instance of viem's PublicClient 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 {string} [porterUri] - The URI for the Porter service. If not provided, a value will be obtained * from the Domain - * @param {ethers.Signer} [signer] - An optional signer for the decryption + * @param {WalletClient} [walletClient] - An optional signer for the decryption * @param {Record} [customParameters] - Optional custom parameters that may be required * depending on the condition used * @@ -138,11 +150,11 @@ export const encryptWithPublicKey = async ( * an error is thrown. */ export const decrypt = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, messageKit: ThresholdMessageKit, porterUri?: string, - signer?: ethers.Signer, + walletClient?: WalletClient, customParameters?: Record, ): Promise => { if (!porterUri) { @@ -150,19 +162,23 @@ export const decrypt = async ( } const ritualId = await DkgCoordinatorAgent.getRitualIdFromPublicKey( - provider, + publicClient, domain, messageKit.acp.publicKey, ); - const ritual = await DkgClient.getActiveRitual(provider, domain, ritualId); + const ritual = await DkgClient.getActiveRitual( + publicClient, + domain, + ritualId, + ); return retrieveAndDecrypt( - provider, + publicClient, domain, porterUri, messageKit, ritualId, ritual.threshold, - signer, + walletClient, customParameters, ); }; @@ -171,7 +187,7 @@ export const decrypt = async ( * Checks if the encryption from the provided messageKit is authorized for the specified ritual. * * @export - * @param {ethers.providers.Provider} provider - Instance of ethers provider which is used to interact with + * @param {PublicClient} publicClient - Instance of viem's PublicClient which is used to interact with * your selected network. * @param {Domain} domain - The domain which was used to encrypt the network. Must match the `ritualId`. * @param {ThresholdMessageKit} messageKit - The encrypted message kit to be checked. @@ -181,28 +197,26 @@ export const decrypt = async ( * True if authorized, false otherwise */ export const isAuthorized = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, messageKit: ThresholdMessageKit, ritualId: number, ) => DkgCoordinatorAgent.isEncryptionAuthorized( - provider, + publicClient, domain, ritualId, messageKit, ); export const registerEncrypters = async ( - provider: ethers.providers.Provider, - signer: ethers.Signer, + walletClient: WalletClient, domain: Domain, ritualId: number, encrypters: ChecksumAddress[], ): Promise => { await GlobalAllowListAgent.registerEncrypters( - provider, - signer, + walletClient, domain, ritualId, encrypters, diff --git a/packages/taco/src/tdec.ts b/packages/taco/src/tdec.ts index 642506c67..b41c5f4e6 100644 --- a/packages/taco/src/tdec.ts +++ b/packages/taco/src/tdec.ts @@ -20,8 +20,9 @@ import { PorterClient, toBytes, } from '@nucypher/shared'; -import { ethers } from 'ethers'; -import { arrayify, keccak256 } from 'ethers/lib/utils'; +import { keccak256 } from 'ethers/lib/utils'; +import { PublicClient, WalletClient } from 'viem'; +import { signMessage } from 'viem/actions'; import { ConditionContext, @@ -37,12 +38,13 @@ const ERR_RITUAL_ID_MISMATCH = ( expectedRitualId: number, ritualIds: number[], ) => `Ritual id mismatch. Expected ${expectedRitualId}, got ${ritualIds}`; +const ERR_NO_ACCOUNT_FOUND = 'No account found in WalletClient'; export const encryptMessage = async ( plaintext: Uint8Array | string, encryptingKey: DkgPublicKey, conditions: ConditionExpression, - authSigner: ethers.Signer, + walletClient: WalletClient, ): Promise => { const [ciphertext, authenticatedData] = encryptForDkg( plaintext instanceof Uint8Array ? plaintext : toBytes(plaintext), @@ -51,7 +53,14 @@ export const encryptMessage = async ( ); const headerHash = keccak256(ciphertext.header.toBytes()); - const authorization = await authSigner.signMessage(arrayify(headerHash)); + const account = walletClient.account; + if (!account) { + throw new Error(ERR_NO_ACCOUNT_FOUND); + } + const authorization = await signMessage(walletClient, { + message: headerHash, + account, + }); const acp = new AccessControlPolicy( authenticatedData, toBytes(authorization), @@ -62,23 +71,23 @@ export const encryptMessage = async ( // Retrieve and decrypt ciphertext using provider and condition expression export const retrieveAndDecrypt = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, porterUri: string, thresholdMessageKit: ThresholdMessageKit, ritualId: number, threshold: number, - signer?: ethers.Signer, + walletClient?: WalletClient, customParameters?: Record, ): Promise => { const decryptionShares = await retrieve( - provider, + publicClient, domain, porterUri, thresholdMessageKit, ritualId, threshold, - signer, + walletClient, customParameters, ); const sharedSecret = combineDecryptionSharesSimple(decryptionShares); @@ -87,24 +96,24 @@ export const retrieveAndDecrypt = async ( // Retrieve decryption shares const retrieve = async ( - provider: ethers.providers.Provider, + publicClient: PublicClient, domain: Domain, porterUri: string, thresholdMessageKit: ThresholdMessageKit, ritualId: number, threshold: number, - signer?: ethers.Signer, + walletClient?: WalletClient, customParameters?: Record, ): Promise => { const dkgParticipants = await DkgCoordinatorAgent.getParticipants( - provider, + publicClient, domain, ritualId, ); const wasmContext = await ConditionContext.fromConditions( - provider, + publicClient, thresholdMessageKit.acp.conditions, - signer, + walletClient, customParameters, ).toWASMContext(); const { sharedSecrets, encryptedRequests } = await makeDecryptionRequests( diff --git a/packages/taco/src/web3.ts b/packages/taco/src/web3.ts deleted file mode 100644 index f8923626b..000000000 --- a/packages/taco/src/web3.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface Eip712TypedData { - types: { - Wallet: { name: string; type: string }[]; - }; - domain: { - salt: string; - chainId: number; - name: string; - version: string; - }; - message: { - blockHash: string; - address: string; - blockNumber: number; - signatureText: string; - }; -} - -export interface FormattedTypedData extends Eip712TypedData { - primaryType: 'Wallet'; - types: { - EIP712Domain: { name: string; type: string }[]; - Wallet: { name: string; type: string }[]; - }; -} diff --git a/packages/taco/test/conditions/base/contract.test.ts b/packages/taco/test/conditions/base/contract.test.ts index be469ab32..4da574e5f 100644 --- a/packages/taco/test/conditions/base/contract.test.ts +++ b/packages/taco/test/conditions/base/contract.test.ts @@ -1,5 +1,5 @@ import { initialize } from '@nucypher/nucypher-core'; -import { fakeProvider, fakeSigner } from '@nucypher/test-utils'; +import { fakePublicClient, fakeWalletClient } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { @@ -174,7 +174,7 @@ describe('supports custom function abi', () => { it('accepts custom function abi with a custom parameter', async () => { const asJson = await conditionExpr - .buildContext(fakeProvider(), {}, fakeSigner()) + .buildContext(fakePublicClient, {}, fakeWalletClient) .withCustomParams(customParams) .toJson(); diff --git a/packages/taco/test/conditions/conditions.test.ts b/packages/taco/test/conditions/conditions.test.ts index 6ad391470..9220653e1 100644 --- a/packages/taco/test/conditions/conditions.test.ts +++ b/packages/taco/test/conditions/conditions.test.ts @@ -1,5 +1,5 @@ import { ChainId } from '@nucypher/shared'; -import { fakeProvider, fakeSigner } from '@nucypher/test-utils'; +import { fakePublicClient, fakeWalletClient } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { initialize } from '../../src'; @@ -37,10 +37,12 @@ describe('conditions', () => { expect(condition.requiresSigner()).toBeTruthy(); const context = new ConditionContext( - fakeProvider(), + fakePublicClient, condition, - { ':time': 100 }, - fakeSigner(), + { + ':time': 100, + }, + fakeWalletClient, ); expect(context).toBeDefined(); diff --git a/packages/taco/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts index 7704212e8..58ec5f9aa 100644 --- a/packages/taco/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -1,6 +1,5 @@ import { initialize } from '@nucypher/nucypher-core'; -import { fakeProvider, fakeSigner } from '@nucypher/test-utils'; -import { ethers } from 'ethers'; +import { fakePublicClient, fakeWalletClient } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { toBytes, toHexString } from '../../src'; @@ -23,13 +22,8 @@ import { } from '../test-utils'; describe('context', () => { - let provider: ethers.providers.Provider; - let signer: ethers.Signer; - beforeAll(async () => { await initialize(); - provider = fakeProvider(); - signer = fakeSigner(); }); describe('serialization', () => { @@ -44,7 +38,7 @@ describe('context', () => { }); const conditionContext = new ConditionExpression( rpcCondition, - ).buildContext(provider, {}, signer); + ).buildContext(fakePublicClient, {}, fakeWalletClient); const asJson = await conditionContext.toJson(); expect(asJson).toBeDefined(); @@ -66,7 +60,11 @@ describe('context', () => { }; const contractCondition = new ContractCondition(contractConditionObj); const conditionExpr = new ConditionExpression(contractCondition); - const context = conditionExpr.buildContext(provider, {}, signer); + const context = conditionExpr.buildContext( + fakePublicClient, + {}, + fakeWalletClient, + ); describe('custom parameters', () => { it('serializes bytes as hex strings', async () => { @@ -133,8 +131,10 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); - expect(conditionExpr.buildContext(provider, {}, signer)).toBeDefined(); - expect(() => conditionExpr.buildContext(provider, {})).toThrow( + expect( + conditionExpr.buildContext(fakePublicClient, {}, fakeWalletClient), + ).toBeDefined(); + expect(() => conditionExpr.buildContext(fakePublicClient, {})).toThrow( `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition`, ); }); @@ -153,8 +153,10 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); - expect(conditionExpr.buildContext(provider, {}, signer)).toBeDefined(); - expect(() => conditionExpr.buildContext(provider, {})).toThrow( + expect( + conditionExpr.buildContext(fakePublicClient, {}, fakeWalletClient), + ).toBeDefined(); + expect(() => conditionExpr.buildContext(fakePublicClient, {})).toThrow( `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition`, ); }); @@ -166,8 +168,10 @@ describe('context', () => { JSON.stringify(condition.toObj()).includes(USER_ADDRESS_PARAM), ).toBe(false); expect(conditionExpr.contextRequiresSigner()).toBe(false); - expect(conditionExpr.buildContext(provider, {}, signer)).toBeDefined(); - expect(conditionExpr.buildContext(provider, {})).toBeDefined(); + expect( + conditionExpr.buildContext(fakePublicClient, {}, fakeWalletClient), + ).toBeDefined(); + expect(conditionExpr.buildContext(fakePublicClient, {})).toBeDefined(); }); it('rejects on a missing signer', () => { @@ -181,7 +185,9 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); - expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow( + expect(() => + conditionExpr.buildContext(fakePublicClient, {}, undefined), + ).toThrow( `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition`, ); }); @@ -197,7 +203,9 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.contextRequiresSigner()).toBe(true); - expect(() => conditionExpr.buildContext(provider, {}, undefined)).toThrow( + expect(() => + conditionExpr.buildContext(fakePublicClient, {}, undefined), + ).toThrow( `Signer required to satisfy ${USER_ADDRESS_PARAM} context variable in condition`, ); }); @@ -221,7 +229,7 @@ describe('context', () => { }); const conditionContext = new ConditionExpression( customContractCondition, - ).buildContext(provider, {}, signer); + ).buildContext(fakePublicClient, {}, fakeWalletClient); await expect(async () => conditionContext.toObj()).rejects.toThrow( `Missing custom context parameter(s): ${customParamKey}`, @@ -235,7 +243,7 @@ describe('context', () => { }); const conditionContext = new ConditionExpression( customContractCondition, - ).buildContext(provider, {}, signer); + ).buildContext(fakePublicClient, {}, fakeWalletClient); const asObj = await conditionContext.toObj(); expect(asObj).toBeDefined(); @@ -254,7 +262,7 @@ describe('context', () => { customParameters[customParamKey] = falsyParam; const conditionContext = new ConditionExpression( customContractCondition, - ).buildContext(provider, customParameters, signer); + ).buildContext(fakePublicClient, customParameters, fakeWalletClient); const asObj = await conditionContext.toObj(); expect(asObj).toBeDefined(); diff --git a/packages/taco/test/dkg-client.test.ts b/packages/taco/test/dkg-client.test.ts index e843c5461..379c271d3 100644 --- a/packages/taco/test/dkg-client.test.ts +++ b/packages/taco/test/dkg-client.test.ts @@ -1,5 +1,5 @@ import { DkgCoordinatorAgent } from '@nucypher/shared'; -import { fakeProvider } from '@nucypher/test-utils'; +import { fakePublicClient } from '@nucypher/test-utils'; import { beforeAll, describe, expect, it } from 'vitest'; import { domains, initialize } from '../src'; @@ -17,10 +17,9 @@ describe('DkgCoordinatorAgent', () => { }); it('fetches transcripts from the coordinator', async () => { - const provider = fakeProvider(); const getRitualSpy = mockGetRitual(); const ritual = await DkgCoordinatorAgent.getRitual( - provider, + fakePublicClient, domains.TEST_DOMAIN, fakeRitualId, ); @@ -29,12 +28,11 @@ describe('DkgCoordinatorAgent', () => { }); it('fetches participants from the coordinator', async () => { - const provider = fakeProvider(); const getParticipantsSpy = mockGetParticipants( (await mockDkgParticipants(fakeRitualId)).participants, ); const participants = await DkgCoordinatorAgent.getParticipants( - provider, + fakePublicClient, domains.TEST_DOMAIN, fakeRitualId, ); @@ -48,7 +46,7 @@ describe('DkgCoordinatorAgent', () => { // it('verifies the dkg ritual', async () => { // const provider = fakeWeb3Provider(SecretKey.random().toBEBytes()); // -// const dkgClient = new DkgClient(provider); +// const dkgClient = new DkgClient(fakePublicClient); // const isValid = await dkgClient.verifyRitual(fakeRitualId); // expect(isValid).toBeTruthy(); // }); diff --git a/packages/taco/test/taco.test.ts b/packages/taco/test/taco.test.ts index 398398c7a..73b1b7771 100644 --- a/packages/taco/test/taco.test.ts +++ b/packages/taco/test/taco.test.ts @@ -4,12 +4,11 @@ import { SessionStaticSecret, } from '@nucypher/nucypher-core'; import { - aliceSecretKeyBytes, fakeDkgFlow, fakePorterUri, - fakeProvider, - fakeSigner, + fakePublicClient, fakeTDecFlow, + fakeWalletClient, mockGetRitualIdFromPublicKey, mockTacoDecrypt, } from '@nucypher/test-utils'; @@ -42,17 +41,15 @@ describe('taco', () => { it('encrypts and decrypts', async () => { const mockedDkg = fakeDkgFlow(FerveoVariant.precomputed, 0, 4, 4); const mockedDkgRitual = fakeDkgRitual(mockedDkg); - const provider = fakeProvider(aliceSecretKeyBytes); - const signer = fakeSigner(aliceSecretKeyBytes); const getFinalizedRitualSpy = mockGetActiveRitual(mockedDkgRitual); const messageKit = await taco.encrypt( - provider, + fakePublicClient, domains.DEV, message, ownsNFT, mockedDkg.ritualId, - signer, + fakeWalletClient, ); expect(getFinalizedRitualSpy).toHaveBeenCalled(); @@ -80,11 +77,11 @@ describe('taco', () => { const getRitualSpy = mockGetActiveRitual(mockedDkgRitual); const decryptedMessage = await taco.decrypt( - provider, + fakePublicClient, domains.DEV, messageKit, fakePorterUri, - signer, + fakeWalletClient, ); expect(getParticipantsSpy).toHaveBeenCalled(); expect(sessionKeySpy).toHaveBeenCalled(); diff --git a/packages/taco/test/test-utils.ts b/packages/taco/test/test-utils.ts index 5a0083503..04e5bb47f 100644 --- a/packages/taco/test/test-utils.ts +++ b/packages/taco/test/test-utils.ts @@ -27,8 +27,8 @@ import { } from '@nucypher/shared'; import { fakeDkgFlow, - fakeSigner, fakeTDecFlow, + fakeWalletClient, TEST_CHAIN_ID, TEST_CONTRACT_ADDR, } from '@nucypher/test-utils'; @@ -84,7 +84,7 @@ export const fakeDkgTDecFlowE2E: ( message, dkgPublicKey, conditionExpr, - fakeSigner(), + fakeWalletClient, ); const { decryptionShares } = fakeTDecFlow({ diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index aa45101fd..de4cbdd7c 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -33,6 +33,7 @@ "@nucypher/shared": "workspace:*", "axios": "^1.6.2", "ethers": "^5.7.2", + "viem": "^1.19.9", "vitest": "^0.34.6" }, "engines": { diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index baf9d8b01..0c338b76d 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -1,2 +1,4 @@ +import './viem'; + export * from './utils'; export * from './variables'; diff --git a/packages/test-utils/src/utils.ts b/packages/test-utils/src/utils.ts index 858adf68c..6d2508d02 100644 --- a/packages/test-utils/src/utils.ts +++ b/packages/test-utils/src/utils.ts @@ -36,11 +36,14 @@ import { RetrieveCFragsResult, TacoDecryptResult, toHexString, + toPublicClient, Ursula, zip, } from '@nucypher/shared'; import axios from 'axios'; -import { ethers, providers, Wallet } from 'ethers'; +import { providers } from 'ethers'; +import { createWalletClient, http, PublicClient } from 'viem'; +import { polygonMumbai } from 'viem/chains'; import { expect, SpyInstance, vi } from 'vitest'; export const bytesEqual = (first: Uint8Array, second: Uint8Array): boolean => @@ -52,48 +55,13 @@ export const fromBytes = (bytes: Uint8Array): string => export const fakePorterUri = 'https://_this_should_crash.com/'; -const makeFakeProvider = (timestamp: number, blockNumber: number) => { - const block = { timestamp }; - return { - getBlockNumber: () => Promise.resolve(blockNumber), - getBlock: () => Promise.resolve(block), - _isProvider: true, - getNetwork: () => Promise.resolve({ name: 'mockNetwork', chainId: -1 }), - }; -}; - -export const fakeSigner = ( - secretKeyBytes = SecretKey.random().toBEBytes(), - blockNumber = 1000, - blockTimestamp = 1000, -) => { - const provider = makeFakeProvider(blockNumber, blockTimestamp); - return { - ...new Wallet(secretKeyBytes), - provider: provider, - _signTypedData: () => Promise.resolve('fake-typed-signature'), - signMessage: () => Promise.resolve('fake-signature'), - getAddress: () => - Promise.resolve('0x0000000000000000000000000000000000000000'), - } as unknown as ethers.providers.JsonRpcSigner; -}; +export const fakeWalletClient = createWalletClient({ + chain: polygonMumbai, + transport: http(), + account: '0x0000000000000000000000000000000000000000', +}); -export const fakeProvider = ( - secretKeyBytes = SecretKey.random().toBEBytes(), - blockNumber = 1000, - blockTimestamp = 1000, -): ethers.providers.Web3Provider => { - const fakeProvider = makeFakeProvider(blockTimestamp, blockNumber); - const fakeSignerWithProvider = fakeSigner( - secretKeyBytes, - blockNumber, - blockTimestamp, - ); - return { - ...fakeProvider, - getSigner: () => fakeSignerWithProvider, - } as unknown as ethers.providers.Web3Provider; -}; +export const fakePublicClient: PublicClient = toPublicClient(fakeWalletClient); const genChecksumAddress = (i: number): ChecksumAddress => `0x${'0'.repeat(40 - i.toString(16).length)}${i.toString( diff --git a/packages/test-utils/src/viem.ts b/packages/test-utils/src/viem.ts new file mode 100644 index 000000000..dbdb8c2a8 --- /dev/null +++ b/packages/test-utils/src/viem.ts @@ -0,0 +1,18 @@ +import { vi } from 'vitest'; + +vi.mock('viem/actions', () => ({ + ...vi.importActual('viem/actions'), + getBlock: vi.fn().mockResolvedValue({ + timestamp: 1000, + }), + getBlockNumber: vi.fn().mockResolvedValue(BigInt(1000)), + signTypedData: vi + .fn() + .mockResolvedValue('0x1234567890123456789012345678901234567890'), + signMessage: vi + .fn() + .mockResolvedValue('0x1234567890123456789012345678901234567890'), + getAccounts: vi + .fn() + .mockResolvedValue(['0x1234567890123456789012345678901234567890']), +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3edb9016..f698eef87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,6 +136,9 @@ importers: react-spinners: specifier: ^0.13.6 version: 0.13.8(react-dom@18.2.0)(react@18.2.0) + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.7 @@ -168,8 +171,8 @@ importers: specifier: ^3.0.0 version: 3.0.0 typescript: - specifier: ^4.8.3 - version: 4.9.5 + specifier: ^5.2.2 + version: 5.2.2 webpack: specifier: ^5.89.0 version: 5.89.0(webpack-cli@5.1.4) @@ -212,6 +215,9 @@ importers: react-spinners: specifier: ^0.13.6 version: 0.13.8(react-dom@18.2.0)(react@18.2.0) + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.7 @@ -244,8 +250,8 @@ importers: specifier: ^3.0.0 version: 3.0.0 typescript: - specifier: ^4.8.3 - version: 4.9.5 + specifier: ^5.2.2 + version: 5.2.2 webpack: specifier: ^5.89.0 version: 5.89.0(webpack-cli@5.1.4) @@ -261,6 +267,19 @@ importers: '@nucypher/pre': specifier: workspace:* version: link:../../../packages/pre + next: + specifier: 14.0.3 + version: 14.0.3(react-dom@18.2.0)(react@18.2.0) + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) + devDependencies: '@types/node': specifier: 20.10.0 version: 20.10.0 @@ -294,27 +313,28 @@ importers: '@nucypher/pre': specifier: workspace:* version: link:../../../packages/pre + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) + devDependencies: dotenv: specifier: ^16.3.1 version: 16.3.1 - ethers: - specifier: ^5.7.2 - version: 5.7.2 examples/pre/react: dependencies: '@nucypher/pre': specifier: workspace:* version: link:../../../packages/pre - ethers: - specifier: ^5.7.2 - version: 5.7.2 react: specifier: ^18.2.0 version: 18.2.0 react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@types/node': specifier: ^20.10.0 @@ -334,6 +354,9 @@ importers: '@nucypher/pre': specifier: workspace:* version: link:../../../packages/pre + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: copy-webpack-plugin: specifier: ^11.0.0 @@ -341,9 +364,6 @@ importers: esbuild-loader: specifier: ^2.11.0 version: 2.21.0(webpack@5.89.0) - ethers: - specifier: ^5.7.2 - version: 5.7.2 webpack: specifier: ^5.89.0 version: 5.89.0(webpack-cli@5.1.4) @@ -363,8 +383,8 @@ importers: specifier: workspace:* version: link:../../../packages/taco '@types/node': - specifier: 20.10.0 - version: 20.10.0 + specifier: 20.6.3 + version: 20.6.3 '@types/react': specifier: 18.2.45 version: 18.2.45 @@ -372,14 +392,11 @@ importers: specifier: 18.2.14 version: 18.2.14 eslint: - specifier: 8.54.0 - version: 8.54.0 + specifier: 8.53.0 + version: 8.53.0 eslint-config-next: specifier: 14.0.3 - version: 14.0.3(eslint@8.54.0)(typescript@5.2.2) - ethers: - specifier: ^5.7.2 - version: 5.7.2 + version: 14.0.3(eslint@8.53.0)(typescript@5.2.2) next: specifier: 14.0.4 version: 14.0.4(react-dom@18.2.0)(react@18.2.0) @@ -389,21 +406,22 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) - typescript: - specifier: 5.2.2 - version: 5.2.2 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) examples/taco/nodejs: dependencies: '@nucypher/taco': specifier: workspace:* version: link:../../../packages/taco + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) + devDependencies: dotenv: specifier: ^16.3.1 version: 16.3.1 - ethers: - specifier: ^5.7.2 - version: 5.7.2 examples/taco/react: dependencies: @@ -413,15 +431,15 @@ importers: '@nucypher/taco': specifier: workspace:* version: link:../../../packages/taco - ethers: - specifier: ^5.7.2 - version: 5.7.2 react: specifier: ^18.2.0 version: 18.2.0 react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@types/node': specifier: ^20.10.0 @@ -444,6 +462,9 @@ importers: ethers: specifier: ^5.7.2 version: 5.7.2 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: copy-webpack-plugin: specifier: ^11.0.0 @@ -472,6 +493,9 @@ importers: ethers: specifier: ^5.7.2 version: 5.7.2 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@nucypher/test-utils': specifier: workspace:* @@ -503,6 +527,9 @@ importers: qs: specifier: ^6.10.1 version: 6.11.2 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) devDependencies: '@typechain/ethers-v5': specifier: ^11.1.2 @@ -543,6 +570,9 @@ importers: semver: specifier: ^7.5.2 version: 7.5.4 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) zod: specifier: ^3.22.4 version: 3.22.4 @@ -568,6 +598,9 @@ importers: ethers: specifier: ^5.7.2 version: 5.7.2 + viem: + specifier: ^1.19.9 + version: 1.19.9(typescript@5.2.2)(zod@3.22.4) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -578,6 +611,10 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + /@adraffy/ens-normalize@1.10.0: + resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} + dev: false + /@alloc/quick-lru@5.2.0: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -2806,6 +2843,16 @@ packages: requiresBuild: true optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.53.0 + eslint-visitor-keys: 3.4.3 + dev: false + /@eslint-community/eslint-utils@4.4.0(eslint@8.54.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2835,6 +2882,11 @@ packages: transitivePeerDependencies: - supports-color + /@eslint/js@8.53.0: + resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: false + /@eslint/js@8.54.0: resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3486,7 +3538,6 @@ packages: resolution: {integrity: sha512-j4K0n+DcmQYCVnSAM+UByTVfIHnYQy2ODozfQP+4RdwtRDfobrIvKq1K4Exb2koJ79HSSa7s6B2SA8T/1YR3RA==} dependencies: glob: 7.1.7 - dev: false /@next/swc-darwin-arm64@14.0.4: resolution: {integrity: sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==} @@ -3575,6 +3626,17 @@ packages: eslint-scope: 5.1.1 dev: true + /@noble/curves@1.2.0: + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + dependencies: + '@noble/hashes': 1.3.2 + dev: false + + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3829,6 +3891,25 @@ packages: /@rushstack/eslint-patch@1.4.0: resolution: {integrity: sha512-cEjvTPU32OM9lUFegJagO0mRnIn+rbqrG89vV8/xLnLFX0DoR0r1oy5IlTga71Q7uT3Qus7qm7wgeiMT/+Irlg==} + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + dev: false + + /@scure/bip32@1.3.2: + resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} + dependencies: + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.3 + dev: false + + /@scure/bip39@1.2.1: + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + dependencies: + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.3 + dev: false + /@sinclair/typebox@0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true @@ -4199,6 +4280,10 @@ packages: dev: true optional: true + /@types/node@20.6.3: + resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} + dev: false + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -4912,6 +4997,21 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true + /abitype@0.9.8(typescript@5.2.2)(zod@3.22.4): + resolution: {integrity: sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.2.2 + zod: 3.22.4 + dev: false + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -7000,7 +7100,7 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: false + dev: true /dotgitignore@2.1.0: resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} @@ -7332,6 +7432,31 @@ packages: source-map: 0.6.1 dev: true + /eslint-config-next@14.0.3(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-IKPhpLdpSUyKofmsXUfrvBC49JMUTdeaD8ZIH4v9Vk0sC1X6URTuTJCLtA0Vwuj7V/CQh0oISuSTvNn5//Buew==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@next/eslint-plugin-next': 14.0.3 + '@rushstack/eslint-patch': 1.4.0 + '@typescript-eslint/parser': 6.7.0(eslint@8.53.0)(typescript@5.2.2) + eslint: 8.53.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0)(eslint@8.53.0) + eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.7.0)(eslint@8.53.0) + eslint-plugin-jsx-a11y: 6.7.1(eslint@8.53.0) + eslint-plugin-react: 7.33.2(eslint@8.53.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.53.0) + typescript: 5.2.2 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - supports-color + dev: false + /eslint-config-next@14.0.3(eslint@8.54.0)(typescript@5.2.2): resolution: {integrity: sha512-IKPhpLdpSUyKofmsXUfrvBC49JMUTdeaD8ZIH4v9Vk0sC1X6URTuTJCLtA0Vwuj7V/CQh0oISuSTvNn5//Buew==} peerDependencies: @@ -7355,7 +7480,7 @@ packages: transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color - dev: false + dev: true /eslint-config-prettier@9.0.0(eslint@8.54.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} @@ -7420,6 +7545,29 @@ packages: transitivePeerDependencies: - supports-color + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0)(eslint@8.53.0): + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.4 + enhanced-resolve: 5.15.0 + eslint: 8.53.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) + eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.7.0)(eslint@8.53.0) + fast-glob: 3.3.1 + get-tsconfig: 4.7.2 + is-core-module: 2.13.1 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: false + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0)(eslint@8.54.0): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -7683,6 +7831,31 @@ packages: - typescript dev: true + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.53.0): + resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + '@babel/runtime': 7.22.15 + aria-query: 5.3.0 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + ast-types-flow: 0.0.7 + axe-core: 4.8.2 + axobject-query: 3.2.1 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 8.53.0 + has: 1.0.3 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + semver: 6.3.1 + dev: false + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.54.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} @@ -7706,12 +7879,22 @@ packages: object.entries: 1.1.7 object.fromentries: 2.0.7 semver: 6.3.1 + dev: true /eslint-plugin-no-only-tests@3.1.0: resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} engines: {node: '>=5.0.0'} dev: true + /eslint-plugin-react-hooks@4.6.0(eslint@8.53.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.53.0 + dev: false + /eslint-plugin-react-hooks@4.6.0(eslint@8.54.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} @@ -7719,6 +7902,32 @@ packages: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: eslint: 8.54.0 + dev: true + + /eslint-plugin-react@7.33.2(eslint@8.53.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.53.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 + dev: false /eslint-plugin-react@7.33.2(eslint@8.54.0): resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} @@ -7743,6 +7952,7 @@ packages: resolve: 2.0.0-next.4 semver: 6.3.1 string.prototype.matchall: 4.0.10 + dev: true /eslint-plugin-simple-import-sort@10.0.0(eslint@8.54.0): resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==} @@ -7833,6 +8043,53 @@ packages: webpack: 5.89.0(webpack-cli@5.1.4) dev: true + /eslint@8.53.0: + resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/regexpp': 4.8.1 + '@eslint/eslintrc': 2.1.3 + '@eslint/js': 8.53.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /eslint@8.54.0: resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -9373,6 +9630,14 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} + /isows@1.0.3(ws@8.13.0): + resolution: {integrity: sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.13.0 + dev: false + /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} @@ -14163,12 +14428,6 @@ packages: typescript: 5.2.2 dev: true - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - /typescript@5.2.2: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} @@ -14345,6 +14604,29 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + /viem@1.19.9(typescript@5.2.2)(zod@3.22.4): + resolution: {integrity: sha512-Sf9U2x4jU0S/FALqYypcspWOGene0NZyD470oUripNhE0Ta6uOE/OgE4toTDVfRxov8qw0JFinr/wPGxYE3+HQ==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/bip32': 1.3.2 + '@scure/bip39': 1.2.1 + abitype: 0.9.8(typescript@5.2.2)(zod@3.22.4) + isows: 1.0.3(ws@8.13.0) + typescript: 5.2.2 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + dev: false + /vite-node@0.34.6(@types/node@20.10.0): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} @@ -15059,6 +15341,19 @@ packages: utf-8-validate: optional: true + /ws@8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + 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 + dev: false + /ws@8.14.2: resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} diff --git a/tsconfig.json b/tsconfig.json index bf242524e..94105c17a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,8 @@ "composite": true, "incremental": true, "exactOptionalPropertyTypes": true, - "noImplicitOverride": true + "noImplicitOverride": true, + "skipLibCheck": true, }, "include": [ "**/*.ts",