From 62da8291bf0601ebde897661ebe49de584e67b61 Mon Sep 17 00:00:00 2001 From: Andrew Min Date: Fri, 18 Oct 2024 14:22:08 -0400 Subject: [PATCH] wip --- examples/with-zerodev-aa/.env.local.example | 9 + examples/with-zerodev-aa/README.md | 131 +++++++++++++ examples/with-zerodev-aa/package.json | 25 +++ .../with-zerodev-aa/src/createNewWallet.ts | 50 +++++ examples/with-zerodev-aa/src/ethers.ts | 142 ++++++++++++++ examples/with-zerodev-aa/src/util.ts | 24 +++ examples/with-zerodev-aa/src/viem.ts | 184 ++++++++++++++++++ examples/with-zerodev-aa/tsconfig.json | 8 + pnpm-lock.yaml | 57 +++++- 9 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 examples/with-zerodev-aa/.env.local.example create mode 100644 examples/with-zerodev-aa/README.md create mode 100644 examples/with-zerodev-aa/package.json create mode 100644 examples/with-zerodev-aa/src/createNewWallet.ts create mode 100644 examples/with-zerodev-aa/src/ethers.ts create mode 100644 examples/with-zerodev-aa/src/util.ts create mode 100644 examples/with-zerodev-aa/src/viem.ts create mode 100644 examples/with-zerodev-aa/tsconfig.json diff --git a/examples/with-zerodev-aa/.env.local.example b/examples/with-zerodev-aa/.env.local.example new file mode 100644 index 000000000..47f9f3bba --- /dev/null +++ b/examples/with-zerodev-aa/.env.local.example @@ -0,0 +1,9 @@ +API_PUBLIC_KEY="" +API_PRIVATE_KEY="" +BASE_URL="https://api.turnkey.com" +ORGANIZATION_ID="" +SIGN_WITH="" # if blank, we will create a wallet for you +INFURA_KEY="" +ZERODEV_PROJECT_ID="" +ZERODEV_BUNDLER_RPC="" # see https://dashboard.zerodev.app/ +ZERODEV_PAYMASTER_RPC="" # see https://dashboard.zerodev.app/ diff --git a/examples/with-zerodev-aa/README.md b/examples/with-zerodev-aa/README.md new file mode 100644 index 000000000..e13b2659c --- /dev/null +++ b/examples/with-zerodev-aa/README.md @@ -0,0 +1,131 @@ +# Example: `with-biconomy-aa` + +This example shows how to construct and broadcast a transaction using Turnkey with [`Ethers`](https://docs.ethers.org/v6/api/providers/#Signer), [`Viem`](https://viem.sh/docs/clients/wallet.html), and [`Biconomy`](https://docs.biconomy.io/account). + +If you want to see a demo with passkeys, it's coming šŸ”œā„¢ļø! + +## Getting started + +### 1/ Cloning the example + +Make sure you have `Node.js` installed locally; we recommend using Node v18+. + +```bash +$ git clone https://github.com/tkhq/sdk +$ cd sdk/ +$ corepack enable # Install `pnpm` +$ pnpm install -r # Install dependencies +$ pnpm run build-all # Compile source code +$ cd examples/with-biconomy-aa/ +``` + +### 2a/ Setting up Turnkey + +The first step is to set up your Turnkey organization and account. By following the [Quickstart](https://docs.turnkey.com/getting-started/quickstart) guide, you should have: + +- A public/private API key pair for Turnkey +- An organization ID +- A Turnkey wallet account (address), private key address, or a private key ID + +### 2b/ Setting up Biconomy + +The next step is to navigate to Biconomy to create a paymaster. Visit the [Biconomy Dashboard](https://dashboard.biconomy.io/) to create a your paymaster and find the following: + +- Bundler URL +- Paymaster API Key + +Once you've gathered these values, add them to a new `.env.local` file. Notice that your private key should be securely managed and **_never_** be committed to git. + +```bash +$ cp .env.local.example .env.local +``` + +Now open `.env.local` and add the missing environment variables: + +- `API_PUBLIC_KEY` +- `API_PRIVATE_KEY` +- `BASE_URL` +- `ORGANIZATION_ID` +- `SIGN_WITH` -- a Turnkey wallet account address, private key address, or private key ID. If you leave this blank, we'll create a wallet for you. +- `INFURA_KEY` -- if this is not set, it will default to using the Community Infura key +- `BICONOMY_BUNDLER_URL` +- `BICONOMY_PAYMASTER_API_KEY` + +### 3/ Running the scripts + +Note: there are two included ā€” one for Viem and another for Ethers. See `package.json` for more details. + +These scripts construct transactions via Turnkey and broadcast them via Infura. If the scripts exit because your account isn't funded, you can request funds on https://sepoliafaucet.com/ or https://faucet.paradigm.xyz/. + +#### Viem + +```bash +$ pnpm start-viem +``` + +This script will do the following: + +1. instantiate a Turnkey Viem wallet client +2. instantiate a Viem public client (to be used to fetch onchain data) +3. connect the wallet client to the Biconomy paymaster +4. send ETH (via type 2 EIP-1559 transaction) + +See the following for a sample output: + +``` +Network: + sepolia (chain ID 11155111) + +Signer address: + 0xDC608F098255C89B36da905D9132A9Ee3DD266D9 + +Smart wallet address: + 0x7fDD1569812a168fe4B6637943BD36ec2c836A6A + +Balance: + 0.0499994 Ether + +Transaction count: + 1 + +Nonce: + 9 + +āœ” Amount to send (wei). Default to 0.0000001 ETH ā€¦ 100000000000 +āœ” Destination address (default to TKHQ warchest) ā€¦ 0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7 +Sent 0.0000001 Ether to 0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7: + https://sepolia.etherscan.io/tx/0x2f2d996d6b262ebf0263b432ca3e6d621ba42d60b92344f31cf3ed94d09f49c4 + +User Ops can be found here: + https://jiffyscan.xyz/bundle/0x2f2d996d6b262ebf0263b432ca3e6d621ba42d60b92344f31cf3ed94d09f49c4?network=sepolia&pageNo=0&pageSize=10 +``` + +#### Ethers + +``` +Network: + sepolia (chain ID 11155111) + +Signer address: + 0xDC608F098255C89B36da905D9132A9Ee3DD266D9 + +Smart wallet address: + 0x7fDD1569812a168fe4B6637943BD36ec2c836A6A + +Balance: + 0.0499993 Ether + +Transaction count: + 1 + +Nonce: + 10 + +āœ” Amount to send (wei). Default to 0.0000001 ETH ā€¦ 100000000000 +āœ” Destination address (default to TKHQ warchest) ā€¦ 0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7 +Sent 0.0000001 Ether to 0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7: + https://sepolia.etherscan.io/tx/0x0f0d5346ba726f7ccf80142ae295f28bf3873b0aeb7b29488b1e3dfb949d5ba6 + +User Ops can be found here: + https://jiffyscan.xyz/bundle/0x0f0d5346ba726f7ccf80142ae295f28bf3873b0aeb7b29488b1e3dfb949d5ba6?network=sepolia&pageNo=0&pageSize=10 +``` diff --git a/examples/with-zerodev-aa/package.json b/examples/with-zerodev-aa/package.json new file mode 100644 index 000000000..c2eecce76 --- /dev/null +++ b/examples/with-zerodev-aa/package.json @@ -0,0 +1,25 @@ +{ + "name": "@turnkey/example-with-biconomy-aa", + "version": "0.1.0", + "private": true, + "scripts": { + "start-ethers": "tsx src/ethers.ts", + "start-viem": "tsx src/viem.ts", + "clean": "rimraf ./dist ./.cache", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@turnkey/ethers": "workspace:*", + "@turnkey/sdk-server": "workspace:*", + "@turnkey/viem": "workspace:*", + "@zerodev/sdk": "^5.3.22", + "dotenv": "^16.0.3", + "ethers": "^6.10.0", + "permissionless": "^0.2.10", + "prompts": "^2.4.2", + "viem": "^2.21.29" + }, + "devDependencies": { + "@types/prompts": "^2.4.2" + } +} diff --git a/examples/with-zerodev-aa/src/createNewWallet.ts b/examples/with-zerodev-aa/src/createNewWallet.ts new file mode 100644 index 000000000..a2c3b4c5b --- /dev/null +++ b/examples/with-zerodev-aa/src/createNewWallet.ts @@ -0,0 +1,50 @@ +import { Turnkey as TurnkeySDKServer } from "@turnkey/sdk-server"; + +import * as crypto from "crypto"; +import { refineNonNull } from "./util"; + +export async function createNewWallet() { + console.log("creating a new wallet on Turnkey...\n"); + + const walletName = `ETH Wallet ${crypto.randomBytes(2).toString("hex")}`; + + try { + const turnkeyClient = new TurnkeySDKServer({ + apiBaseUrl: "https://api.turnkey.com", + apiPublicKey: process.env.API_PUBLIC_KEY!, + apiPrivateKey: process.env.API_PRIVATE_KEY!, + defaultOrganizationId: process.env.ORGANIZATION_ID!, + }); + + const { walletId, addresses } = await turnkeyClient + .apiClient() + .createWallet({ + walletName, + accounts: [ + { + curve: "CURVE_SECP256K1", + pathFormat: "PATH_FORMAT_BIP32", + path: "m/44'/60'/0'/0/0", + addressFormat: "ADDRESS_FORMAT_ETHEREUM", + }, + ], + }); + + const newWalletId = refineNonNull(walletId); + const address = refineNonNull(addresses[0]); + + // Success! + console.log( + [ + `New Ethereum wallet created!`, + `- Name: ${walletName}`, + `- Wallet ID: ${newWalletId}`, + `- Address: ${address}`, + ``, + "Now you can take the address, put it in `.env.local` (`SIGN_WITH=
`), then re-run the script.", + ].join("\n") + ); + } catch (error) { + throw new Error("Failed to create a new Ethereum wallet: " + error); + } +} diff --git a/examples/with-zerodev-aa/src/ethers.ts b/examples/with-zerodev-aa/src/ethers.ts new file mode 100644 index 000000000..ef20d589e --- /dev/null +++ b/examples/with-zerodev-aa/src/ethers.ts @@ -0,0 +1,142 @@ +import * as path from "path"; +import * as dotenv from "dotenv"; +import prompts, { PromptType } from "prompts"; +import { ethers } from "ethers"; +import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"; +import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; +import { entryPoint07Address } from "viem/account-abstraction" +import { toEcdsaKernelSmartAccount } from "permissionless/accounts" + + +// Load environment variables from `.env.local` +dotenv.config({ path: path.resolve(process.cwd(), ".env.local") }); + +import { TurnkeySigner } from "@turnkey/ethers"; +import { Turnkey as TurnkeyServerSDK } from "@turnkey/sdk-server"; +import { createNewWallet } from "./createNewWallet"; +import { print } from "./util"; + +async function main() { + if (!process.env.SIGN_WITH) { + // If you don't specify a `SIGN_WITH`, we'll create a new wallet for you via calling the Turnkey API. + await createNewWallet(); + return; + } + + const turnkeyClient = new TurnkeyServerSDK({ + apiBaseUrl: process.env.BASE_URL!, + apiPrivateKey: process.env.API_PRIVATE_KEY!, + apiPublicKey: process.env.API_PUBLIC_KEY!, + defaultOrganizationId: process.env.ORGANIZATION_ID!, + }); + + // Initialize a Turnkey Signer via Ethers v6 + const turnkeySigner = new TurnkeySigner({ + client: turnkeyClient.apiClient(), + organizationId: process.env.ORGANIZATION_ID!, + signWith: process.env.SIGN_WITH!, + }); + + // Bring your own provider (such as Alchemy or Infura: https://docs.ethers.org/v6/api/providers/) + const network = "sepolia"; + const provider = new ethers.JsonRpcProvider( + `https://${network}.infura.io/v3/${process.env.INFURA_KEY}` + ); + const connectedSigner = turnkeySigner.connect(provider); + + // Connect a TurnkeySigner to Zerodev + const smartAccountSigner = walletClientToSmartAccountSigner(turnkeySigner); + + const chainId = (await connectedSigner.provider?.getNetwork())?.chainId ?? 0; + const signerAddress = await connectedSigner.getAddress(); // signer + + const smartAccountAddress = await zerodevSigner.getAccountAddress(); + + const transactionCount = await connectedSigner.provider?.getTransactionCount( + smartAccountAddress + ); + const nonce = await zerodevSigner.getNonce(); + let balance = + (await connectedSigner.provider?.getBalance(smartAccountAddress)) ?? 0; + + print("Network:", `${network} (chain ID ${chainId})`); + print("Signer address:", signerAddress); + print("Smart wallet address:", smartAccountAddress); + print("Balance:", `${ethers.formatEther(balance)} Ether`); + print("Transaction count:", `${transactionCount}`); + print("Nonce:", `${nonce}`); + + while (balance === 0n) { + console.log( + [ + `\nšŸ’ø Your onchain balance is at 0! To continue this demo you'll need testnet funds! You can use:`, + `- Any online faucet (e.g. https://www.alchemy.com/faucets/)`, + `\nTo check your balance: https://${network}.etherscan.io/address/${smartAccountAddress}`, + `\n--------`, + ].join("\n") + ); + + const { continue: _ } = await prompts([ + { + type: "text" as PromptType, + name: "continue", + message: "Ready to continue? y/n", + initial: "y", + }, + ]); + + balance = (await connectedSigner.provider?.getBalance( + smartAccountAddress + ))!; + } + + const { amount, destination } = await prompts([ + { + type: "number" as PromptType, + name: "amount", + message: "Amount to send (wei). Default to 0.0000001 ETH", + initial: 100000000000, + }, + { + type: "text" as PromptType, + name: "destination", + message: "Destination address (default to TKHQ warchest)", + initial: "0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7", + }, + ]); + const transactionRequest = { + to: destination, + value: amount, + // nonce, + // nonce: transactionCount, + type: 2, + }; + + // Make a simple send tx (which calls `signTransaction` under the hood) + const userOpResponse = await smartAccount?.sendTransaction( + transactionRequest, + { + nonceOptions: { nonceKey: Number(0) }, + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + } + ); + + const { transactionHash } = await userOpResponse.waitForTxHash(); + + print( + `Sent ${ethers.formatEther(transactionRequest.value)} Ether to ${ + transactionRequest.to + }:`, + `https://${network}.etherscan.io/tx/${transactionHash}` + ); + + print( + `User Ops can be found here:`, + `https://jiffyscan.xyz/bundle/${transactionHash}?network=${network}&pageNo=0&pageSize=10` + ); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/examples/with-zerodev-aa/src/util.ts b/examples/with-zerodev-aa/src/util.ts new file mode 100644 index 000000000..1ad4478e1 --- /dev/null +++ b/examples/with-zerodev-aa/src/util.ts @@ -0,0 +1,24 @@ +export function print(header: string, body: string): void { + console.log(`${header}\n\t${body}\n`); +} + +export function assertEqual(left: T, right: T) { + if (left !== right) { + throw new Error(`${JSON.stringify(left)} !== ${JSON.stringify(right)}`); + } +} + +export function refineNonNull( + input: T | null | undefined, + errorMessage?: string +): T { + if (input == null) { + throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`); + } + + return input; +} + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/examples/with-zerodev-aa/src/viem.ts b/examples/with-zerodev-aa/src/viem.ts new file mode 100644 index 000000000..6d88e6c63 --- /dev/null +++ b/examples/with-zerodev-aa/src/viem.ts @@ -0,0 +1,184 @@ +import * as path from "path"; +import * as dotenv from "dotenv"; +import prompts, { PromptType } from "prompts"; + +import { + createWalletClient, + createPublicClient, + http, + type Account, + WalletClient, + formatEther, +} from "viem"; +import { sepolia } from "viem/chains"; + +import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"; +import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; +import { createZeroDevPaymasterClient, createKernelAccountClient } from "@zerodev/sdk" +import { entryPoint07Address } from "viem/account-abstraction"; +import { toEcdsaKernelSmartAccount } from "permissionless/accounts"; + +// Load environment variables from `.env.local` +dotenv.config({ path: path.resolve(process.cwd(), ".env.local") }); + +import { createAccount } from "@turnkey/viem"; +import { Turnkey as TurnkeyServerSDK } from "@turnkey/sdk-server"; +import { createNewWallet } from "./createNewWallet"; +import { print } from "./util"; + +async function main() { + if (!process.env.SIGN_WITH) { + // If you don't specify a `SIGN_WITH`, we'll create a new wallet for you via calling the Turnkey API. + await createNewWallet(); + return; + } + + const turnkeyClient = new TurnkeyServerSDK({ + apiBaseUrl: process.env.BASE_URL!, + apiPrivateKey: process.env.API_PRIVATE_KEY!, + apiPublicKey: process.env.API_PUBLIC_KEY!, + defaultOrganizationId: process.env.ORGANIZATION_ID!, + }); + + // Initialize a Turnkey-powered Viem Account + const turnkeyAccount = await createAccount({ + client: turnkeyClient.apiClient(), + organizationId: process.env.ORGANIZATION_ID!, + signWith: process.env.SIGN_WITH!, + }); + + const network = "sepolia"; + + // Bring your own provider (such as Alchemy or Infura: https://docs.ethers.org/v6/api/providers/) + const client = createWalletClient({ + account: turnkeyAccount as Account, + chain: sepolia, + transport: http( + `https://${network}.infura.io/v3/${process.env.INFURA_KEY!}` + ), + }); + + const publicClient = createPublicClient({ + chain: sepolia, + transport: http( + `https://${network}.infura.io/v3/${process.env.INFURA_KEY!}` + ), + }); + + const kernelClient = createKernelAccountClient({ + middleware: { + sponsorUserOperation: async ({ userOperation }) => { + const zerodevPaymaster = createZeroDevPaymasterClient({ + chain, + entryPoint, + // Get this RPC from ZeroDev dashboard + transport: http(PAYMASTER_RPC), + }) + return zerodevPaymaster.sponsorUserOperation({ + userOperation, + entryPoint, + }) + } + } + }) + + // Connect a TurnkeySigner to Zerodev + const smartAccountSigner = await toEcdsaKernelSmartAccount({ + client, + owners: [client], + entryPoint: { + address: entryPoint07Address, + version: "0.7", + }, + }); + + const chainId = client.chain.id; + const signerAddress = client.account.address; + const smartAccountAddress = smartAccountSigner.address; + + const transactionCount = await publicClient.getTransactionCount({ + address: smartAccountAddress, + }); + const nonce = await smartAccountSigner.getNonce(); + let balance = + (await publicClient.getBalance({ address: smartAccountAddress })) ?? 0; + + print("Network:", `${network} (chain ID ${chainId})`); + print("Signer address:", signerAddress); + print("Smart wallet address:", smartAccountAddress); + print("Balance:", `${formatEther(balance)} Ether`); + print("Transaction count:", `${transactionCount}`); + print("Nonce:", `${nonce}`); + + while (balance === 0n) { + console.log( + [ + `\nšŸ’ø Your onchain balance is at 0! To continue this demo you'll need testnet funds! You can use:`, + `- Any online faucet (e.g. https://www.alchemy.com/faucets/)`, + `\nTo check your balance: https://${network}.etherscan.io/address/${smartAccountAddress}`, + `\n--------`, + ].join("\n") + ); + + const { continue: _ } = await prompts([ + { + type: "text" as PromptType, + name: "continue", + message: "Ready to continue? y/n", + initial: "y", + }, + ]); + + balance = await publicClient.getBalance({ address: smartAccountAddress }); + } + + const { amount, destination } = await prompts([ + { + type: "number" as PromptType, + name: "amount", + message: "Amount to send (wei). Default to 0.0000001 ETH", + initial: 100000000000, + }, + { + type: "text" as PromptType, + name: "destination", + message: "Destination address (default to TKHQ warchest)", + initial: "0x08d2b0a37F869FF76BACB5Bab3278E26ab7067B7", + }, + ]); + const transactionRequest = { + to: destination, + value: amount, + // nonce, + // nonce: transactionCount, + type: 2, + }; + + // Make a simple send tx (which calls `signTransaction` under the hood) + const userOpResponse = await smartAccountSigner?.sendTransaction( + transactionRequest, + { + nonceOptions: { nonceKey: Number(0) }, + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + } + ); + + const { transactionHash } = await userOpResponse.waitForTxHash(); + + print( + `Sent ${ethers.formatEther(transactionRequest.value)} Ether to ${ + transactionRequest.to + }:`, + `https://${network}.etherscan.io/tx/${transactionHash}` + ); + + print( + `User Ops can be found here:`, + `https://jiffyscan.xyz/bundle/${transactionHash}?network=${network}&pageNo=0&pageSize=10` + ); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/examples/with-zerodev-aa/tsconfig.json b/examples/with-zerodev-aa/tsconfig.json new file mode 100644 index 000000000..6d4b83714 --- /dev/null +++ b/examples/with-zerodev-aa/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./.cache/.tsbuildinfo" + }, + "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.json"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88dd79d97..3b66eddec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1350,6 +1350,40 @@ importers: specifier: ^5 version: 5.1.5 + examples/with-zerodev-aa: + dependencies: + '@turnkey/ethers': + specifier: workspace:* + version: link:../../packages/ethers + '@turnkey/sdk-server': + specifier: workspace:* + version: link:../../packages/sdk-server + '@turnkey/viem': + specifier: workspace:* + version: link:../../packages/viem + '@zerodev/sdk': + specifier: ^5.3.22 + version: 5.3.22(permissionless@0.2.10)(viem@2.21.29) + dotenv: + specifier: ^16.0.3 + version: 16.0.3 + ethers: + specifier: ^6.10.0 + version: 6.10.0 + permissionless: + specifier: ^0.2.10 + version: 0.2.10(viem@2.21.29) + prompts: + specifier: ^2.4.2 + version: 2.4.2 + viem: + specifier: ^2.21.29 + version: 2.21.29(typescript@5.1.5) + devDependencies: + '@types/prompts': + specifier: ^2.4.2 + version: 2.4.2 + internal/jest-config: dependencies: '@babel/core': @@ -9921,7 +9955,7 @@ packages: dependencies: '@noble/curves': 1.6.0 '@noble/hashes': 1.5.0 - '@scure/base': 1.1.7 + '@scure/base': 1.1.9 dev: false /@scure/bip39@1.1.1: @@ -10036,7 +10070,7 @@ packages: /@silencelaboratories/walletprovider-sdk@0.1.0(typescript@5.1.5): resolution: {integrity: sha512-53fV1noQJDUN9JNydDohyzsFl4+QYoWNkkkAfRzmIgtv+6DR+Dksb0fKmme2WdtA8MPEw/HsRwN3Lr6YC3iF7A==} dependencies: - '@noble/curves': 1.4.2 + '@noble/curves': 1.6.0 viem: 2.21.29(typescript@5.1.5) transitivePeerDependencies: - bufferutil @@ -12840,6 +12874,17 @@ packages: tslib: 1.14.1 dev: false + /@zerodev/sdk@5.3.22(permissionless@0.2.10)(viem@2.21.29): + resolution: {integrity: sha512-d+WRt5NcRrTe/D/zxzVHcp3HFLmL4QzdbUOmCNRaDgc8IoYsDLBzNg69m9/SCDSEciOqdQKDA6bc0MzSACAjag==} + peerDependencies: + permissionless: '>=0.1.44 <=0.1.45' + viem: '>=2.16.3 <2.18.0' + dependencies: + permissionless: 0.2.10(viem@2.21.29) + semver: 7.5.4 + viem: 2.21.29(typescript@5.1.5) + dev: false + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -19678,6 +19723,14 @@ packages: safe-buffer: 5.2.1 sha.js: 2.4.11 + /permissionless@0.2.10(viem@2.21.29): + resolution: {integrity: sha512-l05ORtPTLo1B+36eHXSIsRAxHvUvsD9i2t12524NaWuwgAM8SMQX99FNWOQwNRb3oVIeYMXCygDXDdvyqwvNzA==} + peerDependencies: + viem: ^2.21.2 + dependencies: + viem: 2.21.29(typescript@5.1.5) + dev: false + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}