diff --git a/.env.example b/.env.example index a293b6aaf..fdf442b9d 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,6 @@ E2E_PRIVATE_KEY_ONE= E2E_PRIVATE_KEY_TWO= E2E_BICO_PAYMASTER_KEY_MUMBAI= E2E_BICO_PAYMASTER_KEY_BASE= -BICONOMY_SDK_DEBUG=true \ No newline at end of file +E2E_BICO_PAYMASTER_KEY_OP= +BICONOMY_SDK_DEBUG=true +WITH_MAINNET_TESTS=false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 77e5cc62b..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ -## SDK Version - - - -## Description - - - -## Steps to Reproduce - -1. -2. -3. - -## Expected Behavior - - - -## Actual Behavior - - - -## Possible Fix (optional) - - - -## Environment Details - -- **Browser Version**: -- **Node.js Version**: -- **NPM/Yarn Version**: - -## Additional Context - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..01619b1b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,70 @@ +name: Bug Report +description: File a bug/issue +title: 'bug: ' +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! The more info you provide, the more we can help you. + + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: - label: I have searched the existing issues + required: true + + - type: input + attributes: + label: Package Version + description: What version of the SDK are you using? + placeholder: 4.0.0 + validations: + required: true + + - type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: false + + - type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: false + + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps or code snippets to reproduce the behaviour. + validations: + required: false + + - type: textarea + attributes: + label: Package.json (or lockfile) content + description: Packages used in your project. This will help us understand the environment you are working in, and check if there are dependencies that might be causing the issue. + validations: + required: false + + - type: input + attributes: + label: Link to Minimal Reproducible Example (StackBlitz, CodeSandbox, GitHub repo etc.) + description: | + [Please provide by forking this project](https://stackblitz.com/~/github.com/bcnmy/sdk-examples) and making relevant changes to the boilerplate. This makes investigating issues and helping you out significantly easier! For most issues, you will likely get asked to provide one so why not add one now :) + validations: + required: false + + - type: textarea + attributes: + label: Anything else? + description: | + Browser info? Screenshots? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..5b7de7d5d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Ask Question + url: https://github.com/bcnmy/biconomy-client-sdk/discussions + about: Ask questions and discuss with other community members + - name: Request Feature + url: https://github.com/bcnmy/biconomy-client-sdk/discussions + about: Requests features or brainstorm ideas for new functionality diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5b5dd6522..188b963b1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,8 +3,7 @@ on: workflow_dispatch: push: branches: - - develop - - v4 + - docs permissions: contents: write diff --git a/.github/workflows/mainnet_tests.yml b/.github/workflows/mainnet_tests.yml new file mode 100644 index 000000000..3118a1e94 --- /dev/null +++ b/.github/workflows/mainnet_tests.yml @@ -0,0 +1,32 @@ +name: E2E Test workflow - with mainnet tests +on: + workflow_dispatch: +jobs: + e2e_test: + name: E2E tests - with mainnet tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + + steps: + - name: Checkout + uses: "actions/checkout@main" + + - name: Set Node.js + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: yarn install --frozen-lockfile && yarn build + + - name: Run tests + env: + E2E_PRIVATE_KEY_ONE: ${{ secrets.E2E_PRIVATE_KEY_ONE }} + E2E_PRIVATE_KEY_TWO: ${{ secrets.E2E_PRIVATE_KEY_TWO }} + E2E_BICO_PAYMASTER_KEY_MUMBAI: ${{ secrets.E2E_BICO_PAYMASTER_KEY_MUMBAI }} + E2E_BICO_PAYMASTER_KEY_BASE: ${{ secrets.E2E_BICO_PAYMASTER_KEY_BASE }} + E2E_BICO_PAYMASTER_KEY_OP: ${{ secrets.E2E_BICO_PAYMASTER_KEY_OP }} + WITH_MAINNET_TESTS: true + run: yarn test:e2e diff --git a/.github/workflows/pull_request_approved.yml b/.github/workflows/pull_request_approved.yml index d4f142659..84688b90b 100644 --- a/.github/workflows/pull_request_approved.yml +++ b/.github/workflows/pull_request_approved.yml @@ -30,4 +30,5 @@ jobs: E2E_PRIVATE_KEY_TWO: ${{ secrets.E2E_PRIVATE_KEY_TWO }} E2E_BICO_PAYMASTER_KEY_MUMBAI: ${{ secrets.E2E_BICO_PAYMASTER_KEY_MUMBAI }} E2E_BICO_PAYMASTER_KEY_BASE: ${{ secrets.E2E_BICO_PAYMASTER_KEY_BASE }} + E2E_BICO_PAYMASTER_KEY_OP: ${{ secrets.E2E_BICO_PAYMASTER_KEY_OP }} run: yarn test:e2e diff --git a/README.md b/README.md index 00c720607..5409568b0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ import { createSmartAccountClient } from "@biconomy/account"; const smartAccount = await createSmartAccountClient({ signer: viemWalletOrEthersSigner, bundlerUrl: "", // From dashboard.biconomy.io - biconomyPaymasterApiKey: "", // From dashboard.biconomy.io + paymasterUrl: "", // From dashboard.biconomy.io }); const { wait } = await smartAccount.sendTransaction({ to: "0x...", value: 1 }); @@ -55,7 +55,7 @@ npm i @biconomy/account | Key | Description | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | [signer](https://bcnmy.github.io/biconomy-client-sdk/packages/account/docs/interfaces/SmartAccountSigner.html) | This signer will be used for signing userOps for any transactions you build. Will accept ethers.JsonRpcSigner as well as a viemWallet | -| [biconomyPaymasterApiKey](https://dashboard.biconomy.io) | You can pass in a biconomyPaymasterApiKey necessary for sponsoring transactions (retrieved from the biconomy dashboard) | +| [paymasterUrl](https://dashboard.biconomy.io) | You can pass in a paymasterUrl necessary for sponsoring transactions (retrieved from the biconomy dashboard) | | [bundlerUrl](https://dashboard.biconomy.io) | You can pass in a bundlerUrl (retrieved from the biconomy dashboard) for sending transactions | ```typescript @@ -70,7 +70,7 @@ const signer = createWalletClient({ account, chain, transport: http() }); const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, }); ``` diff --git a/jest.config.ts b/jest.config.ts index 7cc8b4123..844398aba 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -74,6 +74,12 @@ const config: Config = { // "node_modules" // ], + workerThreads: true, + // This is experimental feature. Keep in mind that the worker threads use structured clone instead of JSON.stringify() to serialize messages. + // This means that built-in JavaScript objects as BigInt, Map or Set will get serialized properly. + // However extra properties set on Error, Map or Set will not be passed on through the serialization step. + // For more details see the article on structured clone. + // An array of file extensions your modules use moduleFileExtensions: ["js", "mjs", "cjs", "jsx", "ts", "tsx", "json", "node"], diff --git a/package.json b/package.json index 0939192fb..72746e0a2 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "lint": "eslint -c .eslintrc.js 'packages/*/src/**/*.{ts,tsx}'", "lint:fix": "eslint -c .eslintrc.js 'packages/*/src/**/*.{ts,tsx}' --fix", "test:run": "yarn jest --runInBand", - "test": "concurrently -k --success first 'yarn start:ganache' 'yarn test:run'", + "test": "concurrently -k --success first 'yarn start:ganache' 'yarn test:run' && exit 0", "start:ganache": "ganache -m 'direct buyer cliff train rice spirit census refuse glare expire innocent quote'", "test:ci": "FORCE_COLOR=1 lerna run test:ci --stream --npm-client=yarn", "test:coverage": "concurrently -k --success first 'yarn start:ganache' 'yarn jest --runInBand --coverage'", diff --git a/packages/account/CHANGELOG.md b/packages/account/CHANGELOG.md index 7d7ed9af6..68152064a 100644 --- a/packages/account/CHANGELOG.md +++ b/packages/account/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +Features: + +- Added Speed optimisation, removing redundant gasEstimate call to bundler ([2371b2](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/2371b230cd5806ec4c7c95ba604d6f924b4be768)) +- Added smartAccount.getBalances() method ([4b8bae](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/4b8bae412577b846e700b168976cefa6b0803ff6)) +- Added smartAccount.getSupportedTokens() method ([6d2fb27](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/6d2fb27d6f9b424e440e45990ea06820a9d16d4b)) +- Added smartAccount.deploy() method ([be9dc4](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/be9dc4d74a3e5a22e69416983436997cf2ea417c)) +- Increased checking of the chainId from the bundler, paymaster and the provider ([5d2f3](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/5d2f34d8f0fb4f9ff7c7ddc00336471e57efdcfd)) +- Added entity name to Logger calls ([9278ec](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/9278ecc21e060ef75ab29a0d054d95d69cd4ae27)) +- Export a 'getChain' by id helper, which returns a viem chain ([ab2ba](https://github.com/bcnmy/biconomy-client-sdk/pull/449/commits/ab2ba2c518ce867c52bf90b9018dfc1b4ec3b4d4)) +- Add "stateOverride" optional param ([20fd54](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/20fd54c817d2dcbc6b7d9a247d890d91b19a9c2f)) + +Fixes: + +- Fix for encodeAbiParameters inside batched session module ([b27061](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/b27061e2eec7bafb0620e88e6d94e56e9a13cb76)) +- added flag to skip calldata approval patch ([75698](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/75698c827015533e32acb1f535bdf6b738876217)) +- Fixed the particle auth build + +Chores: + +- Added tests for ecdsa module ([1a8f29](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/1a8f296c26c9fedd57023f8f6423d7662a3adfee)) +- Increased test coverage ([329003](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/329003cebb6b4034496e41651985804cdec0d311)) +- Improved issue reporting guidelines ([8b9fb5d](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/8b9fb5de9556870611307c12e57df333619d9252)) +- Added e2e tests for optimism, ran from GH actions ([5051ba](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/5051ba5ff14220ad616f1ec3bc93a3f42d6f8887)) +- Added ABI SVM test ([49c96](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/49c968220e2db0aeee5cc6419f45df2b98f9792c)) +- Added tests for batched session router testing ([2eb9765](https://github.com/bcnmy/biconomy-client-sdk/pull/447/commits/2eb9765d066fcb7b35d08223257aeb9b38c7a78b)) + ## 4.0.3 (2023-28-02) VERSION Bump Only. diff --git a/packages/account/Readme.md b/packages/account/Readme.md index 0aa74954b..dedd3e2e6 100644 --- a/packages/account/Readme.md +++ b/packages/account/Readme.md @@ -22,7 +22,7 @@ import { createSmartAccountClient } from "@biconomy/account"; const smartAccount = await createSmartAccountClient({ signer: viemWalletOrEthersSigner, bundlerUrl: "", // From dashboard.biconomy.io - biconomyPaymasterApiKey: "", // From dashboard.biconomy.io + paymasterUrl: "", // From dashboard.biconomy.io }); const { wait } = await smartAccount.sendTransaction({ to: "0x...", value: 1 }); @@ -55,7 +55,7 @@ For a step-by-step guide on integrating **ERC4337 Account Abstraction** and **Sm | Key | Description | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | [signer](https://bcnmy.github.io/biconomy-client-sdk/packages/account/docs/interfaces/SmartAccountSigner.html) | This signer will be used for signing userOps for any transactions you build. Will accept ethers.JsonRpcSigner as well as a viemWallet | -| [biconomyPaymasterApiKey](https://dashboard.biconomy.io) | You can pass in a biconomyPaymasterApiKey necessary for sponsoring transactions (retrieved from the biconomy dashboard) | +| [paymasterUrl](https://dashboard.biconomy.io) | You can pass in a paymasterUrl necessary for sponsoring transactions (retrieved from the biconomy dashboard) | | [bundlerUrl](https://dashboard.biconomy.io) | You can pass in a bundlerUrl (retrieved from the biconomy dashboard) for sending transactions | ```typescript @@ -70,7 +70,7 @@ const signer = createWalletClient({ account, chain, transport: http() }); const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, }); ``` diff --git a/packages/account/package.json b/packages/account/package.json index f5471cbcf..9ab3da686 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/account", - "version": "4.0.3", + "version": "4.1.0", "description": "This package provides apis for ERC-4337 based smart account implementations", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -68,10 +68,10 @@ }, "dependencies": { "@alchemy/aa-core": "^3.1.1", - "@biconomy/bundler": "^4.0.3", - "@biconomy/common": "^4.0.3", - "@biconomy/modules": "^4.0.3", - "@biconomy/paymaster": "^4.0.3", + "@biconomy/bundler": "^4.1.0", + "@biconomy/common": "^4.1.0", + "@biconomy/modules": "^4.1.0", + "@biconomy/paymaster": "^4.1.0", "viem": "^2.7.12" } } diff --git a/packages/account/src/BiconomySmartAccountV2.ts b/packages/account/src/BiconomySmartAccountV2.ts index e5a7bfb48..3aa81a4e2 100644 --- a/packages/account/src/BiconomySmartAccountV2.ts +++ b/packages/account/src/BiconomySmartAccountV2.ts @@ -15,6 +15,8 @@ import { GetContractReturnType, getContract, decodeFunctionData, + parseAbi, + formatUnits, } from "viem"; import { BaseSmartContractAccount, @@ -24,7 +26,7 @@ import { BatchUserOperationCallData, SmartAccountSigner, } from "@alchemy/aa-core"; -import { isNullOrUndefined, packUserOp } from "./utils/Utils.js"; +import { compareChainIds, isNullOrUndefined, packUserOp, isValidRpcUrl } from "./utils/Utils.js"; import { BaseValidationModule, ModuleInfo, SendUserOpParams, createECDSAOwnershipValidationModule } from "@biconomy/modules"; import { IHybridPaymaster, @@ -37,6 +39,7 @@ import { UserOpResponse, extractChainIdFromBundlerUrl, convertSigner, + NATIVE_TOKEN_ALIAS, } from "./index.js"; import { BiconomyTokenPaymasterRequest, @@ -49,6 +52,8 @@ import { BiconomySmartAccountV2ConfigConstructorProps, PaymasterUserOperationDto, SimulationType, + BalancePayload, + SupportedToken, } from "./utils/Types.js"; import { ADDRESS_RESOLVER_ADDRESS, @@ -59,12 +64,13 @@ import { ADDRESS_ZERO, DEFAULT_ENTRYPOINT_ADDRESS, ERROR_MESSAGES, + ERC20_ABI, } from "./utils/Constants.js"; import { BiconomyFactoryAbi } from "./abi/Factory.js"; import { BiconomyAccountAbi } from "./abi/SmartAccount.js"; import { AccountResolverAbi } from "./abi/AccountResolver.js"; -import { Logger } from "@biconomy/common"; -import { FeeQuotesOrDataDto, FeeQuotesOrDataResponse } from "@biconomy/paymaster"; +import { Logger, StateOverrideSet } from "@biconomy/common"; +import { BiconomyPaymaster, FeeQuotesOrDataDto, FeeQuotesOrDataResponse } from "@biconomy/paymaster"; type UserOperationKey = keyof UserOperationStruct; @@ -100,7 +106,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { private constructor(readonly biconomySmartAccountConfig: BiconomySmartAccountV2ConfigConstructorProps) { super({ ...biconomySmartAccountConfig, - chain: getChain(biconomySmartAccountConfig.chainId), + chain: biconomySmartAccountConfig.viemChain ?? getChain(biconomySmartAccountConfig.chainId), rpcClient: biconomySmartAccountConfig.rpcUrl || getChain(biconomySmartAccountConfig.chainId).rpcUrls.default.http[0], entryPointAddress: (biconomySmartAccountConfig.entryPointAddress as Hex) ?? DEFAULT_ENTRYPOINT_ADDRESS, accountAddress: (biconomySmartAccountConfig.accountAddress as Hex) ?? undefined, @@ -115,7 +121,11 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { this.bundler = biconomySmartAccountConfig.bundler; this.implementationAddress = biconomySmartAccountConfig.implementationAddress ?? (BICONOMY_IMPLEMENTATION_ADDRESSES_BY_VERSION.V2_0_0 as Hex); - if (biconomySmartAccountConfig.biconomyPaymasterApiKey) { + if (biconomySmartAccountConfig.paymasterUrl) { + this.paymaster = new Paymaster({ + paymasterUrl: biconomySmartAccountConfig.paymasterUrl, + }); + } else if (biconomySmartAccountConfig.biconomyPaymasterApiKey) { this.paymaster = new Paymaster({ paymasterUrl: `https://paymaster.biconomy.io/api/v1/${biconomySmartAccountConfig.chainId}/${biconomySmartAccountConfig.biconomyPaymasterApiKey}`, }); @@ -137,7 +147,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { this.activeValidationModule = biconomySmartAccountConfig.activeValidationModule!; this.provider = createPublicClient({ - chain: getChain(biconomySmartAccountConfig.chainId), + chain: biconomySmartAccountConfig.viemChain ?? getChain(biconomySmartAccountConfig.chainId), transport: http(biconomySmartAccountConfig.rpcUrl || getChain(biconomySmartAccountConfig.chainId).rpcUrls.default.http[0]), }); @@ -180,8 +190,8 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { */ public static async create(biconomySmartAccountConfig: BiconomySmartAccountV2Config): Promise<BiconomySmartAccountV2> { let chainId = biconomySmartAccountConfig.chainId; - let resolvedSmartAccountSigner!: SmartAccountSigner; let rpcUrl = biconomySmartAccountConfig.rpcUrl; + let resolvedSmartAccountSigner!: SmartAccountSigner; // Signer needs to be initialised here before defaultValidationModule is set if (biconomySmartAccountConfig.signer) { @@ -190,7 +200,9 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { chainId = signerResult.chainId; } if (!rpcUrl && !!signerResult.rpcUrl) { - rpcUrl = signerResult.rpcUrl; + if (isValidRpcUrl(signerResult.rpcUrl)) { + rpcUrl = signerResult.rpcUrl; + } } resolvedSmartAccountSigner = signerResult.signer; } @@ -231,6 +243,12 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { rpcUrl, }; + // We check if chain ids match (skip this if chainId is passed by in the config) + // This check is at the end of the function for cases when the signer is not passed in the config but a validation modules is and we get the signer from the validation module in this case + if (!biconomySmartAccountConfig.chainId) { + await compareChainIds(biconomySmartAccountConfig.signer || resolvedSmartAccountSigner, config, false); + } + return new BiconomySmartAccountV2(config); } @@ -252,6 +270,95 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { return this.accountAddress; } + /** + * Returns token balances (and native token balance) of the smartAccount instance. + * + * This method will fetch the token balances of the smartAccount instance. + * The balance of the native token will always be returned as the last element in the reponse array, with the address set to 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. + * + * @param addresses - Optional. Array of asset addresses to fetch the balances of. If not provided, the method will return only the balance of the native token. + * @returns Promise<Array<BalancePayload>> - An array of token balances (plus the native token balance) of the smartAccount instance. + * + * @example + * import { createClient } from "viem" + * import { createSmartAccountClient } from "@biconomy/account" + * import { createWalletClient, http } from "viem"; + * import { polygonMumbai } from "viem/chains"; + * + * const signer = createWalletClient({ + * account, + * chain: polygonMumbai, + * transport: http(), + * }); + * + * const usdt = "0xda5289fcaaf71d52a80a254da614a192b693e977"; + * const smartAccount = await createSmartAccountClient({ signer, bundlerUrl }); + * const [usdtBalanceFromSmartAccount, nativeTokenBalanceFromSmartAccount] = await smartAccount.getBalances([usdt]); + * + * console.log(usdtBalanceFromSmartAccount); + * // { + * // amount: 1000000000000000n, + * // decimals: 6, + * // address: "0xda5289fcaaf71d52a80a254da614a192b693e977", + * // formattedAmount: "1000000", + * // chainId: 80001 + * // } + * + * // or to get the nativeToken balance + * + * const [nativeTokenBalanceFromSmartAccount] = await smartAccount.getBalances(); + * + * console.log(nativeTokenBalanceFromSmartAccount); + * // { + * // amount: 1000000000000000n, + * // decimals: 18, + * // address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + * // formattedAmount: "1", + * // chainId: 80001 + * // } + * + */ + public async getBalances(addresses?: Array<Hex>): Promise<Array<BalancePayload>> { + const accountAddress = await this.getAccountAddress(); + const result: BalancePayload[] = []; + + if (addresses) { + const tokenContracts = addresses.map((address) => + getContract({ + address, + abi: parseAbi(ERC20_ABI), + client: this.provider, + }), + ); + + const balancePromises = tokenContracts.map((tokenContract) => tokenContract.read.balanceOf([accountAddress])) as Promise<bigint>[]; + const decimalsPromises = tokenContracts.map((tokenContract) => tokenContract.read.decimals()) as Promise<number>[]; + const [balances, decimalsPerToken] = await Promise.all([Promise.all(balancePromises), Promise.all(decimalsPromises)]); + + balances.forEach((amount, index) => + result.push({ + amount, + decimals: decimalsPerToken[index], + address: addresses[index], + formattedAmount: formatUnits(amount, decimalsPerToken[index]), + chainId: this.chainId, + }), + ); + } + + const balance = await this.provider.getBalance({ address: accountAddress }); + + result.push({ + amount: balance, + decimals: 18, + address: NATIVE_TOKEN_ALIAS, + formattedAmount: formatUnits(balance, 18), + chainId: this.chainId, + }); + + return result; + } + /** * Return the account's address. This value is valid even before deploying the contract. */ @@ -516,6 +623,12 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { Logger.log("there is a feeQuote: ", feeQuote); if (!spender) throw new Error(ERROR_MESSAGES.SPENDER_REQUIRED); if (!feeQuote) throw new Error(ERROR_MESSAGES.FAILED_FEE_QUOTE_FETCH); + if (paymasterServiceData.skipPatchCallData && paymasterServiceData.skipPatchCallData === true) { + return this.getPaymasterAndData(userOp, { + ...paymasterServiceData, + feeTokenAddress: feeQuote.tokenAddress, + }); + } const partialUserOp = await this.buildTokenPaymasterUserOp(userOp, { ...paymasterServiceData, spender, @@ -610,7 +723,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { * }, * }); * - * const { receipt } = await wait(); + * const { success, receipt } = await wait(); * */ public async getTokenFees( @@ -623,6 +736,51 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { return this.getPaymasterFeeQuotesOrData(userOp, buildUseropDto.paymasterServiceData); } + /** + * + * @description This function will return an array of supported tokens from the erc20 paymaster associated with the Smart Account + * @returns Promise<{@link SupportedToken}> + * + * @example + * import { createClient } from "viem" + * import { createSmartAccountClient } from "@biconomy/account" + * import { createWalletClient, http } from "viem"; + * import { polygonMumbai } from "viem/chains"; + * + * const signer = createWalletClient({ + * account, + * chain: polygonMumbai, + * transport: http(), + * }); + * + * const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, biconomyPaymasterApiKey }); // Retrieve bundler url from dasboard + * const tokens = await smartAccount.getSupportedTokens(); + * + * // [ + * // { + * // symbol: "USDC", + * // tokenAddress: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + * // decimal: 6, + * // logoUrl: "https://assets.coingecko.com/coins/images/279/large/usd-coin.png?1595353707", + * // premiumPercentage: 0.1, + * // } + * // ] + * + */ + public async getSupportedTokens(): Promise<SupportedToken[]> { + const feeQuotesResponse = await this.getTokenFees( + { + data: "0x", + value: BigInt(0), + to: await this.getAccountAddress(), + }, + { + paymasterServiceData: { mode: PaymasterMode.ERC20 }, + }, + ); + return (feeQuotesResponse?.feeQuotes ?? []).map(({ maxGasFee: _, maxGasFeeUSD: __, validUntil: ___, usdPayment: ____, ...rest }) => rest); + } + /** * * @param userOp @@ -635,7 +793,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { * * @param userOp Partial<{@link UserOperationStruct}> the userOp params to be sent. * @param params {@link SendUserOpParams}. - * @returns Promise<{@link UserOpResponse}> that you can use to track user operation. + * @returns Promise<{@link UserOpResponse}> that you can use to track the user operation. * * @example * import { createClient } from "viem" @@ -664,7 +822,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { * const userOp = await smartAccount.buildUserOp([transaction]); * * const { wait } = await smartAccount.sendUserOp(userOp); - * const { receipt } = await wait(); + * const { success, receipt } = await wait(); * */ async sendUserOp(userOp: Partial<UserOperationStruct>, params?: SendUserOpParams): Promise<UserOpResponse> { @@ -708,7 +866,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { return keccak256(enc); } - async estimateUserOpGas(userOp: Partial<UserOperationStruct>): Promise<Partial<UserOperationStruct>> { + async estimateUserOpGas(userOp: Partial<UserOperationStruct>, stateOverrideSet?: StateOverrideSet): Promise<Partial<UserOperationStruct>> { if (!this.bundler) throw new Error("Bundler is not provided"); const requiredFields: UserOperationKey[] = ["sender", "nonce", "initCode", "callData"]; this.validateUserOp(userOp, requiredFields); @@ -716,8 +874,10 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { const finalUserOp = userOp; // Making call to bundler to get gas estimations for userOp - const { callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas } = - await this.bundler.estimateUserOpGas(userOp); + const { callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas } = await this.bundler.estimateUserOpGas( + userOp, + stateOverrideSet, + ); // if neither user sent gas fee nor the bundler, estimate gas from provider if (!userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas && (!maxFeePerGas || !maxPriorityFeePerGas)) { const feeData = await this.provider.estimateFeesPerGas(); @@ -784,7 +944,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { * * @param manyOrOneTransactions Array of {@link Transaction} to be batched and sent. Can also be a single {@link Transaction}. * @param buildUseropDto {@link BuildUserOpOptions}. - * @returns Promise<{@link UserOpResponse}> that you can use to track user operation. + * @returns Promise<{@link UserOpResponse}> that you can use to track the user operation. * * @example * import { createClient } from "viem" @@ -873,33 +1033,46 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { throw new Error("Transactions array cannot be empty"); } let callData: Hex = "0x"; - if (transactions.length > 1 || buildUseropDto?.forceEncodeForBatch) { - callData = await this.encodeExecuteBatch(to, value, data); - } else { - // transactions.length must be 1 - callData = await this.encodeExecute(to[0], value[0], data[0]); + if (!buildUseropDto?.useEmptyDeployCallData) { + if (transactions.length > 1 || buildUseropDto?.forceEncodeForBatch) { + callData = await this.encodeExecuteBatch(to, value, data); + } else { + // transactions.length must be 1 + callData = await this.encodeExecute(to[0], value[0], data[0]); + } } let userOp: Partial<UserOperationStruct> = { sender: (await this.getAccountAddress()) as Hex, nonce: toHex(nonceFromFetch), initCode, - callData: callData, + callData, }; // for this Smart Account current validation module dummy signature will be used to estimate gas userOp.signature = signature; - // Note: Can change the default behaviour of calling estimations using bundler/local - userOp = await this.estimateUserOpGas(userOp); + if ( + buildUseropDto?.paymasterServiceData && + buildUseropDto?.paymasterServiceData.mode === PaymasterMode.SPONSORED && + this.paymaster instanceof BiconomyPaymaster + ) { + const gasFeeValues = await this.bundler?.getGasFeeValues(); - if (buildUseropDto?.paymasterServiceData) { - userOp = await this.getPaymasterUserOp(userOp, buildUseropDto.paymasterServiceData); - } + // populate gasfee values and make a call to paymaster + userOp.maxFeePerGas = gasFeeValues?.maxFeePerGas as Hex; + userOp.maxPriorityFeePerGas = gasFeeValues?.maxPriorityFeePerGas as Hex; - Logger.log("UserOp after estimation ", userOp); + userOp = await this.getPaymasterUserOp(userOp, buildUseropDto.paymasterServiceData); + return userOp; + } else { + userOp = await this.estimateUserOpGas(userOp); - return userOp; + if (buildUseropDto?.paymasterServiceData) { + userOp = await this.getPaymasterUserOp(userOp, buildUseropDto.paymasterServiceData); + } + return userOp; + } } private validateUserOpAndPaymasterRequest(userOp: Partial<UserOperationStruct>, tokenPaymasterRequest: BiconomyTokenPaymasterRequest): void { @@ -1025,6 +1198,78 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { return signatureWithModuleAddress; } + /** + * Deploys the smart contract + * + * This method will deploy a Smart Account contract. It is useful for deploying in a moment when you know that gas prices are low, + * and you want to deploy the account before sending the first user operation. This step can otherwise be skipped, + * as the deployment will alternatively be bundled with the first user operation. + * + * @param buildUseropDto {@link BuildUserOpOptions}. + * @returns Promise<{@link UserOpResponse}> that you can use to track the user operation. + * @error Throws an error if the account has already been deployed. + * @error Throws an error if the account has not enough native token balance to deploy, if not using a paymaster. + * + * @example + * import { createClient } from "viem" + * import { createSmartAccountClient } from "@biconomy/account" + * import { createWalletClient, http } from "viem"; + * import { polygonMumbai } from "viem/chains"; + * + * const signer = createWalletClient({ + * account, + * chain: polygonMumbai, + * transport: http(), + * }); + * + * const smartAccount = await createSmartAccountClient({ + * signer, + * biconomyPaymasterApiKey, + * bundlerUrl + * }); + * + * // If you want to use a paymaster... + * const { wait } = await smartAccount.deploy({ + * paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + * }); + * + * // Or if you can't use a paymaster send native token to this address: + * const counterfactualAddress = await smartAccount.getAccountAddress(); + * + * // Then deploy the account + * const { wait } = await smartAccount.deploy(); + * + * const { success, receipt } = await wait(); + * + */ + public async deploy(buildUseropDto?: BuildUserOpOptions): Promise<UserOpResponse> { + const accountAddress = this.accountAddress ?? (await this.getAccountAddress()); + + // Check that the account has not already been deployed + const byteCode = await this.provider?.getBytecode({ address: accountAddress as Hex }); + if (byteCode !== undefined) { + throw new Error(ERROR_MESSAGES.ACCOUNT_ALREADY_DEPLOYED); + } + + // Check that the account has enough native token balance to deploy, if not using a paymaster + if (!buildUseropDto?.paymasterServiceData?.mode) { + const nativeTokenBalance = await this.provider?.getBalance({ address: accountAddress }); + if (nativeTokenBalance === BigInt(0)) { + throw new Error(ERROR_MESSAGES.NO_NATIVE_TOKEN_BALANCE_DURING_DEPLOY); + } + } + + const useEmptyDeployCallData = true; + + return this.sendTransaction( + { + to: accountAddress, + data: "0x", + }, + { ...buildUseropDto, useEmptyDeployCallData }, + ); + } + async signMessage(message: string | Uint8Array): Promise<Hex> { this.isActiveValidationModuleDefined(); const dataHash = typeof message === "string" ? toBytes(message) : message; diff --git a/packages/account/src/index.ts b/packages/account/src/index.ts index 10fcab754..f46953a67 100644 --- a/packages/account/src/index.ts +++ b/packages/account/src/index.ts @@ -3,6 +3,7 @@ import { type BiconomySmartAccountV2Config } from "./utils/Types.js"; export * from "./utils/Types.js"; export * from "./utils/Constants.js"; +export * from "./utils/Utils.js"; export * from "./BiconomySmartAccountV2.js"; export { WalletClientSigner, LocalAccountSigner, type SmartAccountSigner } from "@alchemy/aa-core"; @@ -36,6 +37,11 @@ export { DEFAULT_SESSION_KEY_MANAGER_MODULE, DEFAULT_MULTICHAIN_MODULE, DEFAULT_BATCHED_SESSION_ROUTER_MODULE, + type ECDSAOwnershipValidationModuleConfig, + type BatchedSessionRouterModuleConfig, + type SessionKeyManagerModuleConfig, + type MultiChainValidationModuleConfig, + type SessionValidationModuleConfig, } from "@biconomy/modules"; export const createSmartAccountClient = BiconomySmartAccountV2.create; diff --git a/packages/account/src/utils/Constants.ts b/packages/account/src/utils/Constants.ts index d6f52fc7a..9ddfc7f0b 100644 --- a/packages/account/src/utils/Constants.ts +++ b/packages/account/src/utils/Constants.ts @@ -58,7 +58,20 @@ export const DefaultGasLimit = { }; export const ERROR_MESSAGES = { + ACCOUNT_ALREADY_DEPLOYED: "Account already deployed", + NO_NATIVE_TOKEN_BALANCE_DURING_DEPLOY: "Native token balance is not available during deploy", SPENDER_REQUIRED: "spender is required for ERC20 mode", NO_FEE_QUOTE: "FeeQuote was not provided, please call smartAccount.getTokenFees() to get feeQuote", FAILED_FEE_QUOTE_FETCH: "Failed to fetch fee quote", + CHAIN_NOT_FOUND: "Chain not found", }; + +export const NATIVE_TOKEN_ALIAS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; +export const ERC20_ABI = [ + "function transfer(address to, uint256 value) external returns (bool)", + "function transferFrom(address from, address to, uint256 value) external returns (bool)", + "function approve(address spender, uint256 value) external returns (bool)", + "function allowance(address owner, address spender) external view returns (uint256)", + "function balanceOf(address owner) external view returns (uint256)", + "function decimals() external view returns (uint8)", +]; diff --git a/packages/account/src/utils/Types.ts b/packages/account/src/utils/Types.ts index 0967d0f8f..f653796c1 100644 --- a/packages/account/src/utils/Types.ts +++ b/packages/account/src/utils/Types.ts @@ -9,8 +9,8 @@ import { type SponsorUserOperationDto, } from "@biconomy/paymaster"; import { BaseValidationModule, ModuleInfo } from "@biconomy/modules"; -import { Hex, WalletClient } from "viem"; -import { SupportedSigner } from "@biconomy/common"; +import { Chain, Hex, WalletClient } from "viem"; +import { SupportedSigner, StateOverrideSet } from "@biconomy/common"; export type EntryPointAddresses = Record<string, string>; export type BiconomyFactories = Record<string, string>; @@ -20,12 +20,25 @@ export type BiconomyFactoriesByVersion = Record<string, string>; export type BiconomyImplementationsByVersion = Record<string, string>; export type SmartAccountConfig = { - /** entryPointAddress: address of the smart account factory */ + /** entryPointAddress: address of the entry point */ entryPointAddress: string; /** factoryAddress: address of the smart account factory */ bundler?: IBundler; }; +export interface BalancePayload { + /** address: The address of the account */ + address: string; + /** chainId: The chainId of the network */ + chainId: number; + /** amount: The amount of the balance */ + amount: bigint; + /** decimals: The number of decimals */ + decimals: number; + /** formattedAmount: The amount of the balance formatted */ + formattedAmount: string; +} + export interface GasOverheads { /** fixed: fixed gas overhead */ fixed: number; @@ -67,6 +80,8 @@ export type BiconomyTokenPaymasterRequest = { spender: Hex; /** maxApproval: If set to true, the paymaster will approve the maximum amount of tokens required for the transaction. Not recommended */ maxApproval?: boolean; + /* skip option to patch callData if approval is already given to the paymaster */ + skipPatchCallData?: boolean; }; export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & @@ -112,8 +127,10 @@ export type BiconomySmartAccountV2ConfigBaseProps = { implementationAddress?: Hex; /** defaultFallbackHandler: override the default fallback contract address */ defaultFallbackHandler?: Hex; - /** rpcUrl: Explicitly set the rpc else it is pulled out of the signer. */ + /** rpcUrl: Rpc url, optional, we set default rpc url if not passed. */ rpcUrl?: string; // as good as Provider + /** paymasterUrl: The Paymaster URL retrieved from the Biconomy dashboard */ + paymasterUrl?: string; /** biconomyPaymasterApiKey: The API key retrieved from the Biconomy dashboard */ biconomyPaymasterApiKey?: string; /** activeValidationModule: The active validation module. Will default to the defaultValidationModule */ @@ -122,6 +139,8 @@ export type BiconomySmartAccountV2ConfigBaseProps = { scanForUpgradedAccountsFromV1?: boolean; /** the index of SA the EOA have generated and till which indexes the upgraded SA should scan */ maxIndexForScan?: number; + /** Can be used to optionally override the chain with a custom chain if it doesn't already exist in viems list of supported chains */ + viemChain?: Chain; }; export type BiconomySmartAccountV2Config = BiconomySmartAccountV2ConfigBaseProps & BaseSmartAccountConfig & @@ -148,6 +167,10 @@ export type BuildUserOpOptions = { paymasterServiceData?: PaymasterUserOperationDto; /** simulationType: Determine which parts of the tx a bundler will simulate: "validation" | "validation_and_execution". */ simulationType?: SimulationType; + /** stateOverrideSet: For overriding the state */ + stateOverrideSet?: StateOverrideSet; + /** set to true if the tx is being used *only* to deploy the smartContract, so "0x" is set as the userOp.callData */ + useEmptyDeployCallData?: boolean; }; export type NonceOptions = { @@ -201,6 +224,8 @@ export type PaymasterUserOperationDto = SponsorUserOperationDto & spender?: Hex; /** Not recommended */ maxApproval?: boolean; + /* skip option to patch callData if approval is already given to the paymaster */ + skipPatchCallData?: boolean; }; export type InitializeV2Data = { @@ -275,3 +300,5 @@ export type ValueOrData = RequireAtLeastOne< export type Transaction = { to: string; } & ValueOrData; + +export type SupportedToken = Omit<PaymasterFeeQuote, "maxGasFeeUSD" | "usdPayment" | "maxGasFee" | "validUntil">; diff --git a/packages/account/src/utils/Utils.ts b/packages/account/src/utils/Utils.ts index d905506c9..efd8e977c 100644 --- a/packages/account/src/utils/Utils.ts +++ b/packages/account/src/utils/Utils.ts @@ -1,5 +1,11 @@ -import { encodeAbiParameters, parseAbiParameters, keccak256, Hex } from "viem"; +import { encodeAbiParameters, parseAbiParameters, keccak256, Hex, Chain } from "viem"; import type { UserOperationStruct } from "@alchemy/aa-core"; +import { SupportedSigner, convertSigner } from "@biconomy/common"; +import { extractChainIdFromBundlerUrl } from "@biconomy/bundler"; +import { BiconomySmartAccountV2Config } from "./Types"; +import { extractChainIdFromPaymasterUrl } from "@biconomy/bundler"; +import * as chains from "viem/chains"; +import { ERROR_MESSAGES } from "./Constants"; /** * pack the userOperation @@ -43,3 +49,57 @@ export function packUserOp(op: Partial<UserOperationStruct>, forSignature = true export const isNullOrUndefined = (value: any): value is undefined => { return value === null || value === undefined; }; + +export const compareChainIds = async ( + signer: SupportedSigner, + biconomySmartAccountConfig: BiconomySmartAccountV2Config, + skipChainIdCalls: boolean, +): Promise<Error | void> => { + const signerResult = await convertSigner(signer, skipChainIdCalls); + + const chainIdFromBundler = biconomySmartAccountConfig.bundlerUrl + ? extractChainIdFromBundlerUrl(biconomySmartAccountConfig.bundlerUrl) + : biconomySmartAccountConfig.bundler + ? extractChainIdFromBundlerUrl(biconomySmartAccountConfig.bundler.getBundlerUrl()) + : undefined; + + const chainIdFromPaymasterUrl = biconomySmartAccountConfig.paymasterUrl + ? extractChainIdFromPaymasterUrl(biconomySmartAccountConfig.paymasterUrl) + : undefined; + + if (!isNullOrUndefined(signerResult.chainId)) { + if (chainIdFromBundler !== undefined && signerResult.chainId !== chainIdFromBundler) { + throw new Error(`Chain IDs from signer (${signerResult.chainId}) and bundler (${chainIdFromBundler}) do not match.`); + } + if (chainIdFromPaymasterUrl !== undefined && signerResult.chainId !== chainIdFromPaymasterUrl) { + throw new Error(`Chain IDs from signer (${signerResult.chainId}) and paymaster (${chainIdFromPaymasterUrl}) do not match.`); + } + } else { + if (chainIdFromBundler !== undefined && chainIdFromPaymasterUrl !== undefined && chainIdFromBundler !== chainIdFromPaymasterUrl) { + throw new Error(`Chain IDs from bundler (${chainIdFromBundler}) and paymaster (${chainIdFromPaymasterUrl}) do not match.`); + } + } +}; + +export const isValidRpcUrl = (url: string): boolean => { + const regex = /^(https:\/\/|wss:\/\/).*/; + return regex.test(url); +}; + +export const addressEquals = (a?: string, b?: string): boolean => !!a && !!b && a?.toLowerCase() === b.toLowerCase(); + +/** + * Utility method for converting a chainId to a {@link Chain} object + * + * @param chainId + * @returns a {@link Chain} object for the given chainId + * @throws if the chainId is not found + */ +export const getChain = (chainId: number): Chain => { + for (const chain of Object.values(chains)) { + if (chain.id === chainId) { + return chain; + } + } + throw new Error(ERROR_MESSAGES.CHAIN_NOT_FOUND); +}; diff --git a/packages/account/tests/account.e2e.spec.ts b/packages/account/tests/account.e2e.spec.ts index c7594b732..82f9649f1 100644 --- a/packages/account/tests/account.e2e.spec.ts +++ b/packages/account/tests/account.e2e.spec.ts @@ -1,17 +1,19 @@ import { TestData } from "../../../tests"; -import { createSmartAccountClient, ERROR_MESSAGES, FeeQuotesOrDataResponse, IHybridPaymaster, PaymasterMode } from "../src/index"; -import { Hex, encodeFunctionData, getContract, parseAbi } from "viem"; +import { createSmartAccountClient, ERROR_MESSAGES, FeeQuotesOrDataResponse, NATIVE_TOKEN_ALIAS, PaymasterMode } from "../src/index"; +import { Hex, createWalletClient, encodeFunctionData, getContract, http, parseAbi } from "viem"; import { UserOperationStruct } from "@alchemy/aa-core"; import { checkBalance, entryPointABI } from "../../../tests/utils"; import { ERC20_ABI } from "@biconomy/modules"; +import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; describe("Account Tests", () => { let mumbai: TestData; let baseSepolia: TestData; + let optimism: TestData; beforeEach(() => { // @ts-ignore: Comes from setup-e2e-tests - [mumbai, baseSepolia] = testDataPerChain; + [mumbai, baseSepolia, optimism] = testDataPerChain; }); it("should have addresses", async () => { @@ -27,6 +29,12 @@ describe("Account Tests", () => { bundlerUrl: bundlerUrlBase, } = baseSepolia; + const { + whale: { viemWallet: signerOp, publicAddress: senderOp }, + minnow: { viemWallet: recipientSignerOp, publicAddress: recipientOp }, + bundlerUrl: bundlerUrlOp, + } = optimism; + const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, @@ -44,7 +52,17 @@ describe("Account Tests", () => { const reciepientSmartAccountBase = await createSmartAccountClient({ signer: recipientSignerBase, - bundlerUrl, + bundlerUrl: bundlerUrlBase, + }); + + const smartAccountOp = await createSmartAccountClient({ + signer: signerOp, + bundlerUrl: bundlerUrlOp, + }); + + const reciepientSmartAccountOp = await createSmartAccountClient({ + signer: recipientSignerOp, + bundlerUrl: bundlerUrlOp, }); const addresses = await Promise.all([ @@ -56,6 +74,10 @@ describe("Account Tests", () => { smartAccountBase.getAddress(), recipientBase, reciepientSmartAccountBase.getAddress(), + senderOp, + smartAccountOp.getAddress(), + recipientOp, + reciepientSmartAccountOp.getAddress(), ]); expect(addresses.every(Boolean)).toBeTruthy(); }); @@ -78,7 +100,6 @@ describe("Account Tests", () => { { to: recipient, value: 1, - data: "0x", }, { simulationType: "validation_and_execution", @@ -96,12 +117,12 @@ describe("Account Tests", () => { const { whale: { viemWallet: signer }, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, - biconomyPaymasterApiKey, + paymasterUrl, bundlerUrl, }); @@ -111,18 +132,18 @@ describe("Account Tests", () => { }); it("Should gaslessly mint an NFT on Mumbai", async () => { - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; const { whale: { viemWallet: signer, publicAddress: recipient }, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, publicClient, + nftAddress, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, }); const encodedCall = encodeFunctionData({ @@ -158,21 +179,23 @@ describe("Account Tests", () => { expect(newBalance - balance).toBe(1n); }, 60000); - it("Should mint an NFT on Mumbai and pay with ERC20 - with preferredToken", async () => { - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; + // TODO(Remove when Yash fixes approvals issue) + it.skip("Should mint an NFT on Mumbai and pay with ERC20 - with preferredToken", async () => { const { whale: { viemWallet: signer, publicAddress: recipient }, bundlerUrl, - publicClient, - biconomyPaymasterApiKey, + nftAddress, + publicClient } = mumbai; const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, - biconomyPaymasterApiKey, + biconomyPaymasterApiKey: "7K_k68BFN.ed274da8-69a1-496d-a897-508fc2653666", }); + const accountAddress = await smartAccount.getAddress(); + const encodedCall = encodeFunctionData({ abi: parseAbi(["function safeMint(address _to)"]), functionName: "safeMint", @@ -185,8 +208,8 @@ describe("Account Tests", () => { }; const balance = (await checkBalance(publicClient, recipient, nftAddress)) as bigint; - const maticBalanceBefore = await checkBalance(publicClient, await smartAccount.getAddress()); - const usdcBalanceBefore = await checkBalance(publicClient, await smartAccount.getAddress(), "0xda5289fcaaf71d52a80a254da614a192b693e977"); + const maticBalanceBefore = await checkBalance(publicClient, accountAddress); + const usdcBalanceBefore = await checkBalance(publicClient, accountAddress, "0xda5289fcaaf71d52a80a254da614a192b693e977"); const { wait } = await smartAccount.sendTransaction([transaction], { paymasterServiceData: { @@ -216,11 +239,11 @@ describe("Account Tests", () => { }, 60000); it("Should expect several feeQuotes in resonse to empty tokenInfo fields", async () => { - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; const { whale: { viemWallet: signer, publicAddress: recipient }, bundlerUrl, biconomyPaymasterApiKey, + nftAddress, } = mumbai; const smartAccount = await createSmartAccountClient({ @@ -244,20 +267,21 @@ describe("Account Tests", () => { expect(feeQuotesResponse.feeQuotes?.length).toBeGreaterThan(1); }); - it("Should mint an NFT on Mumbai and pay with ERC20 - with token selection and no maxApproval", async () => { - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; + // TODO(Remove when Yash fixes approvals issue) + it.skip("Should mint an NFT on Mumbai and pay with ERC20 - with token selection and no maxApproval", async () => { const preferredToken: Hex = "0xda5289fcaaf71d52a80a254da614a192b693e977"; const { whale: { viemWallet: signer, publicAddress: recipient }, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, publicClient, + nftAddress, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, }); const smartAccountAddress = await smartAccount.getAddress(); @@ -280,7 +304,7 @@ describe("Account Tests", () => { }, }); - const selectedFeeQuote = feeQuotesResponse.feeQuotes?.[0]!; + const selectedFeeQuote = feeQuotesResponse.feeQuotes?.[0]; const spender = feeQuotesResponse.tokenPaymasterAddress!; const contract = getContract({ @@ -292,12 +316,18 @@ describe("Account Tests", () => { const allowanceBefore = (await contract.read.allowance([smartAccountAddress, spender])) as bigint; if (allowanceBefore > 0) { - const setAllowanceToZeroTransaction = await (smartAccount?.paymaster as IHybridPaymaster<any>)?.buildTokenApprovalTransaction({ - feeQuote: { ...selectedFeeQuote, maxGasFee: 0 }, - spender, + const decreaseAllowanceData = encodeFunctionData({ + abi: parseAbi(["function decreaseAllowance(address spender, uint256 subtractedValue)"]), + functionName: "decreaseAllowance", + args: [spender, allowanceBefore], }); - const { wait } = await smartAccount.sendTransaction([setAllowanceToZeroTransaction]); + const decreaseAllowanceTx = { + to: "0xda5289fcaaf71d52a80a254da614a192b693e977", + data: decreaseAllowanceData, + }; + + const { wait } = await smartAccount.sendTransaction(decreaseAllowanceTx, { paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); const { success } = await wait(); expect(success).toBe("true"); @@ -338,11 +368,11 @@ describe("Account Tests", () => { }, 60000); it("Should throw and error if missing field for ERC20 Paymaster user op", async () => { - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; const { whale: { viemWallet: signer, publicAddress: recipient }, bundlerUrl, biconomyPaymasterApiKey, + nftAddress, } = mumbai; const smartAccount = await createSmartAccountClient({ @@ -386,12 +416,12 @@ describe("Account Tests", () => { bundlerUrl, entryPointAddress, publicClient, - biconomyPaymasterApiKey, + paymasterUrl, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, - biconomyPaymasterApiKey, + paymasterUrl, bundlerUrl, }); @@ -426,12 +456,12 @@ describe("Account Tests", () => { whale: { viemWallet: signer }, bundlerUrl, publicClient, - biconomyPaymasterApiKey, + paymasterUrl, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, - biconomyPaymasterApiKey, + paymasterUrl, bundlerUrl, }); @@ -447,13 +477,195 @@ describe("Account Tests", () => { const { whale: { viemWallet: signer }, bundlerUrl, + viemChain, } = mumbai; const smartAccount = await createSmartAccountClient({ signer, bundlerUrl, + rpcUrl: viemChain.rpcUrls.default.http[0], }); expect(ecdsaOwnershipModule).toBe(smartAccount.activeValidationModule.getAddress()); }); + + it("Should throw, chain id from signer and bundlerUrl do not match", async () => { + const { + whale: { viemWallet: signer }, + } = mumbai; + + const createAccount = createSmartAccountClient({ + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44", // mock + }); + + await expect(createAccount).rejects.toThrow(); + }); + + it("Should throw, chain id from paymasterUrl and bundlerUrl do not match", async () => { + const { + whale: { viemWallet: signer }, + } = mumbai; + + const createAccount = createSmartAccountClient({ + signer, + paymasterUrl: "https://paymaster.biconomy.io/api/v1/1/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71", + bundlerUrl: "https://bundler.biconomy.io/api/v2/80001/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44", // mock + }); + + await expect(createAccount).rejects.toThrow(); + }); + it("should deploy a smart account with native token balance", async () => { + const { + bundlerUrl, + biconomyPaymasterApiKey, + viemChain, + publicClient, + whale: { viemWallet: signer, publicAddress, account }, + deploymentCost, + } = mumbai; + + const newPrivateKey = generatePrivateKey(); + const newAccount = privateKeyToAccount(newPrivateKey); + + const newViemWallet = createWalletClient({ + account: newAccount, + chain: viemChain, + transport: http(viemChain.rpcUrls.default.http[0]), + }); + + const smartAccount = await createSmartAccountClient({ + signer: newViemWallet, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + const smartAccountAddress = await smartAccount.getAccountAddress(); + + // Setup: + const hash = await signer.sendTransaction({ to: smartAccountAddress, value: BigInt(deploymentCost), account, chain: viemChain }); // Send enough native token to counterfactual address to deploy the smart account + const transaction = await publicClient.waitForTransactionReceipt({ hash }); + expect(transaction).toBeTruthy(); + + // Test: + const { wait } = await smartAccount.deploy(); + const { success } = await wait(); + + const byteCode = await publicClient.getBytecode({ address: smartAccountAddress }); + expect(success).toBe("true"); + expect(byteCode).toBeTruthy(); + }, 60000); + + it("should deploy a smart account with sponsorship", async () => { + const { bundlerUrl, biconomyPaymasterApiKey, viemChain, publicClient } = mumbai; + + const newPrivateKey = generatePrivateKey(); + const newAccount = privateKeyToAccount(newPrivateKey); + + const newViemWallet = createWalletClient({ + account: newAccount, + chain: viemChain, + transport: http(viemChain.rpcUrls.default.http[0]), + }); + + const smartAccount = await createSmartAccountClient({ + signer: newViemWallet, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + const smartAccountAddress = await smartAccount.getAccountAddress(); + const balance = await publicClient.getBalance({ address: smartAccountAddress }); + expect(balance).toBe(0n); + + const { wait } = await smartAccount.deploy({ + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + }); + const { success } = await wait(); + + const byteCode = await publicClient.getBytecode({ address: smartAccountAddress }); + expect(success).toBe("true"); + expect(byteCode).toBeTruthy(); + }, 60000); + + it("should fail to deploy a smart account if no native token balance or paymaster", async () => { + const { bundlerUrl, biconomyPaymasterApiKey, viemChain } = mumbai; + + const newPrivateKey = generatePrivateKey(); + const newAccount = privateKeyToAccount(newPrivateKey); + + const newViemWallet = createWalletClient({ + account: newAccount, + chain: viemChain, + transport: http(viemChain.rpcUrls.default.http[0]), + }); + + const smartAccount = await createSmartAccountClient({ + signer: newViemWallet, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + expect(async () => smartAccount.deploy()).rejects.toThrow(ERROR_MESSAGES.NO_NATIVE_TOKEN_BALANCE_DURING_DEPLOY); + }); + + it("should get supported tokens from the paymaster", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + const tokens = await smartAccount.getSupportedTokens(); + + expect(tokens.length).toBeGreaterThan(0); + expect(tokens[0]).toHaveProperty("tokenAddress"); + expect(tokens[0]).toHaveProperty("symbol"); + expect(tokens[0]).toHaveProperty("decimal"); + expect(tokens[0]).toHaveProperty("premiumPercentage"); + expect(tokens[0]).toHaveProperty("logoUrl"); + }, 60000); + + it("should fail to deploy a smart account if already deployed", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + expect(async () => smartAccount.deploy()).rejects.toThrow(ERROR_MESSAGES.ACCOUNT_ALREADY_DEPLOYED); + }, 60000); + + it("should fetch balances for smartAccount", async () => { + const usdt = "0xda5289fcaaf71d52a80a254da614a192b693e977"; + const { + whale: { viemWallet: signer }, + bundlerUrl, + publicClient, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + const usdcBalanceBefore = await checkBalance(publicClient, await smartAccount.getAddress(), usdt); + const [usdtBalanceFromSmartAccount] = await smartAccount.getBalances([usdt]); + + expect(usdcBalanceBefore).toBe(usdtBalanceFromSmartAccount.amount); + }); }); diff --git a/packages/account/tests/account.optimism.e2e.spec.ts b/packages/account/tests/account.optimism.e2e.spec.ts new file mode 100644 index 000000000..d558ba4ed --- /dev/null +++ b/packages/account/tests/account.optimism.e2e.spec.ts @@ -0,0 +1,118 @@ +import { TestData } from "../../../tests"; +import { createSmartAccountClient, PaymasterMode } from "../src/index"; +import { encodeFunctionData, parseAbi } from "viem"; +import { checkBalance } from "../../../tests/utils"; + +const maybe = process.env.WITH_MAINNET_TESTS === "true" ? describe : describe.skip; + +maybe("Account Tests", () => { + let optimism: TestData; + let _: TestData; + let __: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-e2e-tests + [_, __, optimism] = testDataPerChain; + }); + + it("should send some native token to a recipient on optimism", async () => { + const { + whale: { viemWallet: signer, publicAddress: sender }, + minnow: { publicAddress: recipient }, + bundlerUrl, + publicClient, + } = optimism; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl, + }); + + const accountAddress = await smartAccount.getAddress(); + + const balanceOfRecipient = (await checkBalance(publicClient, recipient)) as bigint; + const smartAccountBalance = (await checkBalance(publicClient, accountAddress)) as bigint; + const { wait } = await smartAccount.sendTransaction( + { + to: recipient, + value: BigInt(1), + }, + { + simulationType: "validation_and_execution", + }, + ); + + const result = await wait(); + const newBalanceOfRecipient = (await checkBalance(publicClient, recipient)) as bigint; + + expect(result?.receipt?.transactionHash).toBeTruthy(); + expect(result.success).toBe("true"); + expect(newBalanceOfRecipient).toBeGreaterThan(balanceOfRecipient); + }, 50000); + + it("Create a smart account with paymaster with an api key on optimism", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + biconomyPaymasterApiKey, + } = optimism; + + const smartAccount = await createSmartAccountClient({ + signer, + biconomyPaymasterApiKey, + bundlerUrl, + }); + + const paymaster = smartAccount.paymaster; + expect(paymaster).not.toBeNull(); + expect(paymaster).not.toBeUndefined(); + }); + + it("Should gaslessly mint an NFT on optimism", async () => { + const { + whale: { viemWallet: signer, publicAddress: recipient }, + bundlerUrl, + biconomyPaymasterApiKey, + publicClient, + nftAddress, + } = optimism; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl, + biconomyPaymasterApiKey, + }); + + const encodedCall = encodeFunctionData({ + abi: parseAbi(["function safeMint(address to) public"]), + functionName: "safeMint", + args: [recipient], + }); + + const transaction = { + to: nftAddress, // NFT address + data: encodedCall, + }; + + const balance = (await checkBalance(publicClient, recipient, nftAddress)) as bigint; + + const maticBalanceBefore = await checkBalance(publicClient, await smartAccount.getAddress()); + + const response = await smartAccount.sendTransaction(transaction, { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + simulationType: "validation_and_execution", + }); + + const userOpReceipt = await response.wait(3); + expect(userOpReceipt.userOpHash).toBeTruthy(); + expect(userOpReceipt.success).toBe("true"); + + const maticBalanceAfter = await checkBalance(publicClient, await smartAccount.getAddress()); + + expect(maticBalanceAfter).toEqual(maticBalanceBefore); + + const newBalance = (await checkBalance(publicClient, recipient, nftAddress)) as bigint; + + expect(newBalance - balance).toBe(1n); + }, 50000); +}); diff --git a/packages/account/tests/account.read.e2e.spec.ts b/packages/account/tests/account.read.e2e.spec.ts new file mode 100644 index 000000000..80abf9d21 --- /dev/null +++ b/packages/account/tests/account.read.e2e.spec.ts @@ -0,0 +1,108 @@ +import { TestData } from "../../../tests"; +import { createSmartAccountClient } from "../src/index"; +import { DEFAULT_ECDSA_OWNERSHIP_MODULE, DEFAULT_SESSION_KEY_MANAGER_MODULE, createECDSAOwnershipValidationModule } from "@biconomy/modules"; + +describe("Account Tests", () => { + let mumbai: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-e2e-tests + [mumbai] = testDataPerChain; + }); + + it("should check if module is enabled on the smart account", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + } = mumbai; + + const smartWallet = await createSmartAccountClient({ + signer, + bundlerUrl, + }); + + const isEnabled = await smartWallet.isModuleEnabled(DEFAULT_ECDSA_OWNERSHIP_MODULE); + expect(isEnabled).toBeTruthy(); + }, 30000); + + it("should get all modules", async () => { + const { + whale: { + viemWallet: signer, + account: { address: accountAddress }, + }, + bundlerUrl, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl, + biconomyPaymasterApiKey, + }); + + const modules = await smartAccount.getAllModules(); + + expect(modules).toContain(DEFAULT_SESSION_KEY_MANAGER_MODULE); // session manager module + expect(modules).toContain(DEFAULT_ECDSA_OWNERSHIP_MODULE); // ecdsa ownership module + }, 30000); + + it("should get disabled module data", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + } = mumbai; + + const smartWallet = await createSmartAccountClient({ + signer, + bundlerUrl, + }); + + const disableModuleData = await smartWallet.getDisableModuleData(DEFAULT_ECDSA_OWNERSHIP_MODULE, DEFAULT_ECDSA_OWNERSHIP_MODULE); + expect(disableModuleData).toBeTruthy(); + }, 30000); + + it("should get setup and enable module data", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + } = mumbai; + + const smartWallet = await createSmartAccountClient({ + signer, + bundlerUrl, + }); + + const module = await createECDSAOwnershipValidationModule({ signer }); + const initData = await module.getInitData(); + const setupAndEnableModuleData = await smartWallet.getSetupAndEnableModuleData(DEFAULT_ECDSA_OWNERSHIP_MODULE, initData); + expect(setupAndEnableModuleData).toBeTruthy(); + }, 30000); + + it("should read estimated user op gas values", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + } = mumbai; + + const smartWallet = await createSmartAccountClient({ + signer, + bundlerUrl, + }); + + const tx = { + to: "0x000000D50C68705bd6897B2d17c7de32FB519fDA", + data: "0x", + }; + + const userOp = await smartWallet.buildUserOp([tx]); + + const estimatedGas = await smartWallet.estimateUserOpGas(userOp); + expect(estimatedGas.maxFeePerGas).toBeTruthy(); + expect(estimatedGas.maxPriorityFeePerGas).toBeTruthy(); + expect(estimatedGas.verificationGasLimit).toBeTruthy(); + expect(estimatedGas.callGasLimit).toBeTruthy(); + expect(estimatedGas.preVerificationGas).toBeTruthy(); + expect(estimatedGas).toHaveProperty("paymasterAndData", "0x"); + }, 35000); +}); diff --git a/packages/account/tests/account.spec.ts b/packages/account/tests/account.spec.ts index 7b70cb178..42cc90355 100644 --- a/packages/account/tests/account.spec.ts +++ b/packages/account/tests/account.spec.ts @@ -1,13 +1,15 @@ -import { Paymaster, createSmartAccountClient } from "../src"; -import { createWalletClient, http } from "viem"; +import { Bundler, Paymaster, createBundler, createSmartAccountClient } from "../src"; +import { Chain, createWalletClient, http } from "viem"; import { localhost } from "viem/chains"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; import { TestData } from "../../../tests"; import { JsonRpcProvider } from "@ethersproject/providers"; import { Wallet } from "@ethersproject/wallet"; +import { getMockBundlerUrl } from "../../../tests/utils"; describe("Account Tests", () => { let ganache: TestData; + const mockBundlerUrl = "https://bundler.biconomy.io/api/v2/1337/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f14"; beforeEach(() => { // @ts-ignore: Comes from setup-unit-tests @@ -16,13 +18,28 @@ describe("Account Tests", () => { it("should create a smartAccountClient from an ethers signer", async () => { const { - bundlerUrl, minnow: { ethersSigner: signer }, } = ganache; const smartAccount = await createSmartAccountClient({ signer, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], + }); + const address = await smartAccount.getAccountAddress(); + expect(address).toBeTruthy(); + }); + + it("should create a whale smartAccountClient from an ethers signer", async () => { + const { bundlerUrl, + whale: { ethersSigner: signer }, + } = ganache; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); @@ -31,21 +48,73 @@ describe("Account Tests", () => { it("should create a smartAccountClient from a walletClient", async () => { const { whale: { viemWallet: signer }, - bundlerUrl, } = ganache; const smartAccount = await createSmartAccountClient({ signer, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); }); + it("should pickup the rpcUrl when a custom chain is used", async () => { + const customBlastChain = { + id: 81_457, + name: "Blast", + // network: "blast", + nativeCurrency: { + decimals: 18, + name: "Ethereum", + symbol: "ETH", + }, + rpcUrls: { + public: { http: ["https://rpc.blast.io"] }, + default: { http: ["https://rpc.blast.io"] }, + }, + blockExplorers: { + etherscan: { name: "Blastscan", url: "https://blastscan.io/" }, + default: { name: "Blastscan", url: "https://blastscan.io/" }, + }, + contracts: { + multicall3: { + address: "0xca11bde05977b3631167028862be2a173976ca11", + blockCreated: 88_189, + }, + }, + } as const satisfies Chain; + + const { + whale: { privateKey }, + } = ganache; + + const accountOne = privateKeyToAccount(privateKey); + + const walletClientWithCustomChain = createWalletClient({ + account: accountOne, + chain: customBlastChain, + transport: http(customBlastChain.rpcUrls.default.http[0]), + }); + + const blastBundler = await createBundler({ + bundlerUrl: getMockBundlerUrl(customBlastChain.id), + viemChain: customBlastChain, + }); + const smartAccountFromViemWithCustomChain = await createSmartAccountClient({ + viemChain: customBlastChain, + signer: walletClientWithCustomChain, + bundler: blastBundler, + rpcUrl: customBlastChain.rpcUrls.default.http[0], + }); + + expect(smartAccountFromViemWithCustomChain.rpcProvider.transport.url).toBe("https://rpc.blast.io"); + expect(blastBundler.getBundlerUrl()).toBe(getMockBundlerUrl(customBlastChain.id)); + }); + it("should pickup the rpcUrl from viem wallet and ethers", async () => { const { chainId, - bundlerUrl, viemChain, whale: { privateKey, viemWallet: originalViemSigner, ethersSigner: originalEthersSigner }, } = ganache; @@ -68,22 +137,26 @@ describe("Account Tests", () => { createSmartAccountClient({ chainId, signer: ethersSignerWithNewRpcUrl, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: newRpcUrl, }), createSmartAccountClient({ chainId, signer: walletClientWithNewRpcUrl, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: newRpcUrl, }), createSmartAccountClient({ chainId, signer: originalEthersSigner, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: viemChain.rpcUrls.default.http[0], }), createSmartAccountClient({ chainId, signer: originalViemSigner, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: viemChain.rpcUrls.default.http[0], }), ]); @@ -118,13 +191,13 @@ describe("Account Tests", () => { const { chainId, whale: { alchemyWalletClientSigner: signer }, - bundlerUrl, } = ganache; const smartAccount = await createSmartAccountClient({ chainId, signer, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); @@ -132,48 +205,27 @@ describe("Account Tests", () => { it("should provide an account address", async () => { const { - bundlerUrl, whale: { viemWallet: signer }, } = ganache; const smartAccount = await createSmartAccountClient({ signer, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); }); - it("Nonce should be zero", async () => { - const { - entryPointAddress, - bundlerUrl, - whale: { viemWallet: signer }, - minnow: { publicAddress: recipient }, - } = ganache; - - const smartAccount = await createSmartAccountClient({ - entryPointAddress, - signer, - bundlerUrl, - }); - const address = await smartAccount.getAccountAddress(); - expect(address).toBeTruthy(); - - const builtUserOp = await smartAccount.buildUserOp([{ to: recipient, value: 1 }]); - console.log("builtUserOp", builtUserOp); - expect(builtUserOp?.nonce?.toString()).toBe("0x0"); - }, 10000); - it("should have an active validation module", async () => { const { - bundlerUrl, whale: { viemWallet: signer }, } = ganache; const smartAccount = await createSmartAccountClient({ signer, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }); const module = smartAccount.activeValidationModule; @@ -183,25 +235,22 @@ describe("Account Tests", () => { it("Create a smart account with paymaster by creating instance", async () => { const { whale: { viemWallet: signer }, - bundlerUrl, - biconomyPaymasterApiKey, + paymasterUrl, } = ganache; - const paymasterUrl = "https://paymaster.biconomy.io/api/v1/80001/" + biconomyPaymasterApiKey; const paymaster = new Paymaster({ paymasterUrl }); const smartAccount = await createSmartAccountClient({ signer, - bundlerUrl, + bundlerUrl: mockBundlerUrl, paymaster, + rpcUrl: localhost.rpcUrls.default.http[0], }); expect(smartAccount.paymaster).not.toBeNull(); expect(smartAccount.paymaster).not.toBeUndefined(); }, 10000); it("should fail to create a smartAccountClient from a walletClient without a chainId", async () => { - const { bundlerUrl } = ganache; - const account = privateKeyToAccount(generatePrivateKey()); const viemWalletClientNoChainId = createWalletClient({ account, @@ -212,15 +261,14 @@ describe("Account Tests", () => { await expect( createSmartAccountClient({ signer: viemWalletClientNoChainId, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }), ).rejects.toThrow("Cannot consume a viem wallet without a chainId"), ); }); it("should fail to create a smartAccountClient from a walletClient without an account", async () => { - const { bundlerUrl } = ganache; - const viemWalletNoAccount = createWalletClient({ transport: http(localhost.rpcUrls.default.http[0]), }); @@ -228,7 +276,8 @@ describe("Account Tests", () => { expect(async () => createSmartAccountClient({ signer: viemWalletNoAccount, - bundlerUrl, + bundlerUrl: mockBundlerUrl, + rpcUrl: localhost.rpcUrls.default.http[0], }), ).rejects.toThrow("Cannot consume a viem wallet without an account"); }); diff --git a/packages/account/tests/utils.spec.ts b/packages/account/tests/utils.spec.ts new file mode 100644 index 000000000..d8dfc2a79 --- /dev/null +++ b/packages/account/tests/utils.spec.ts @@ -0,0 +1,130 @@ +import { BiconomySmartAccountV2Config, ERROR_MESSAGES, createECDSAOwnershipValidationModule } from "../src"; +import { TestData } from "../../../tests"; +import { compareChainIds, getChain } from "../src/utils"; +import { createWalletClient, http } from "viem"; +import { bsc } from "viem/chains"; + +describe("Utils tests", () => { + let ganache: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-unit-tests + [ganache] = testDataPerChain; + }); + + it("Should not throw and error, chain ids match", async () => { + const { + minnow: { viemWallet: walletClient }, + } = ganache; + + const mockBundlerUrl = "https://bundler.biconomy.io/api/v2/1337/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44"; + const mockPaymasterUrl = "https://paymaster.biconomy.io/api/v1/1337/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71"; + + const config: BiconomySmartAccountV2Config = { + signer: walletClient, + bundlerUrl: mockBundlerUrl, + paymasterUrl: mockPaymasterUrl, + }; + + await expect(compareChainIds(walletClient, config, false)).resolves.not.toThrow(); + }); + + it("Should throw and error, bundlerUrl chain id and signer chain id does not match", async () => { + const { + minnow: { viemWallet: walletClient }, + paymasterUrl, + } = ganache; + + const mockBundlerUrl = "https://bundler.biconomy.io/api/v2/1/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44"; + + const config: BiconomySmartAccountV2Config = { + signer: walletClient, + bundlerUrl: mockBundlerUrl, + paymasterUrl, + }; + + await expect(compareChainIds(walletClient, config, false)).rejects.toThrow(); + }); + + it("Should throw and error, bundlerUrl chain id and paymaster url chain id does not match", async () => { + const { + bundlerUrl, + minnow: { viemWallet: walletClient }, + } = ganache; + + const mockPaymasterUrl = "https://paymaster.biconomy.io/api/v1/80001/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71"; + + const config: BiconomySmartAccountV2Config = { + signer: walletClient, + bundlerUrl, + paymasterUrl: mockPaymasterUrl, + }; + + await expect(compareChainIds(walletClient, config, false)).rejects.toThrow(); + }); + + it("Should throw and error, bundlerUrl chain id and paymaster url chain id does not match", async () => { + const { + bundlerUrl, + minnow: { viemWallet: walletClient }, + } = ganache; + + const mockPaymasterUrl = "https://paymaster.biconomy.io/api/v1/80001/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71"; + + const ecdsaModule = await createECDSAOwnershipValidationModule({ + signer: walletClient, + }); + + const config: BiconomySmartAccountV2Config = { + defaultValidationModule: ecdsaModule, + activeValidationModule: ecdsaModule, + bundlerUrl, + paymasterUrl: mockPaymasterUrl, + }; + + await expect(compareChainIds(walletClient, config, false)).rejects.toThrow(); + }); + + it("Should throw and error, signer has chain id (56) and paymasterUrl has chain id (80001)", async () => { + const { bundlerUrl, whale } = ganache; + + const mockPaymasterUrl = "https://paymaster.biconomy.io/api/v1/80001/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71"; + + const walletClient = createWalletClient({ + account: whale.viemWallet.account, + chain: bsc, + transport: http(bsc.rpcUrls.default.http[0]), + }); + + const config: BiconomySmartAccountV2Config = { + signer: walletClient, + bundlerUrl, + paymasterUrl: mockPaymasterUrl, + }; + + await expect(compareChainIds(walletClient, config, false)).rejects.toThrow(); + }); + + // test chains + it("Should return chain object for chain id 1", () => { + const chainId = 1; + const chain = getChain(chainId); + expect(chain.id).toBe(chainId); + }); + + // should have correct fields + it("Should have correct fields", () => { + const chainId = 1; + const chain = getChain(chainId); + + ["blockExplorers", "contracts", "fees", "formatters", "id", "name", "nativeCurrency", "rpcUrls", "serializers"].every((field) => { + expect(chain).toHaveProperty(field); + }); + }); + + // Should throw an error, chain id not found + it("Should throw an error, chain id not found", () => { + const chainId = 0; + expect(() => getChain(chainId)).toThrow(ERROR_MESSAGES.CHAIN_NOT_FOUND); + }); +}); diff --git a/packages/bundler/CHANGELOG.md b/packages/bundler/CHANGELOG.md index 08d5af51d..addb7650c 100644 --- a/packages/bundler/CHANGELOG.md +++ b/packages/bundler/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +VERSION Bump Only. + ## 4.0.3 (2023-28-02) VERSION Bump Only. diff --git a/packages/bundler/package.json b/packages/bundler/package.json index c73f8f10c..4eade66e2 100644 --- a/packages/bundler/package.json +++ b/packages/bundler/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/bundler", - "version": "4.0.3", + "version": "4.1.0", "description": "Biconomy Bundler package to interact with any bundler node as per ERC4337 standard", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -57,7 +57,7 @@ }, "dependencies": { "@alchemy/aa-core": "^3.1.1", - "@biconomy/common": "^4.0.3", + "@biconomy/common": "^4.1.0", "viem": "^2.7.12" }, "devDependencies": { diff --git a/packages/bundler/src/Bundler.ts b/packages/bundler/src/Bundler.ts index a20fadfb4..3782d7066 100644 --- a/packages/bundler/src/Bundler.ts +++ b/packages/bundler/src/Bundler.ts @@ -1,5 +1,5 @@ import { getChain, type UserOperationStruct } from "@alchemy/aa-core"; -import { createPublicClient, http } from "viem"; +import { createPublicClient, http, PublicClient } from "viem"; import { IBundler } from "./interfaces/IBundler.js"; import { GetUserOperationReceiptResponse, @@ -27,7 +27,7 @@ import { DEFAULT_ENTRYPOINT_ADDRESS, } from "./utils/Constants.js"; import { extractChainIdFromBundlerUrl } from "./utils/Utils.js"; -import { sendRequest, HttpMethod } from "@biconomy/common"; +import { sendRequest, HttpMethod, StateOverrideSet } from "@biconomy/common"; /** * This class implements IBundler interface. @@ -46,10 +46,17 @@ export class Bundler implements IBundler { UserOpWaitForTxHashMaxDurationIntervals!: { [key in number]?: number }; + private provider: PublicClient; + constructor(bundlerConfig: Bundlerconfig) { const parsedChainId: number = bundlerConfig?.chainId || extractChainIdFromBundlerUrl(bundlerConfig.bundlerUrl); this.bundlerConfig = { ...bundlerConfig, chainId: parsedChainId }; + this.provider = createPublicClient({ + chain: bundlerConfig.viemChain ?? getChain(parsedChainId), + transport: http((bundlerConfig.viemChain || getChain(parsedChainId)).rpcUrls.default.http[0]), + }); + this.UserOpReceiptIntervals = { ...UserOpReceiptIntervals, ...bundlerConfig.userOpReceiptIntervals, @@ -82,11 +89,10 @@ export class Bundler implements IBundler { * @description This function will fetch gasPrices from bundler * @returns Promise<UserOpGasPricesResponse> */ - async estimateUserOpGas(userOp: UserOperationStruct): Promise<UserOpGasResponse> { + async estimateUserOpGas(userOp: UserOperationStruct, stateOverrideSet?: StateOverrideSet): Promise<UserOpGasResponse> { // expected dummySig and possibly dummmy paymasterAndData should be provided by the caller // bundler doesn't know account and paymaster implementation userOp = transformUserOP(userOp); - const bundlerUrl = this.getBundlerUrl(); const response: EstimateUserOpGasResponse = await sendRequest( @@ -95,7 +101,9 @@ export class Bundler implements IBundler { method: HttpMethod.Post, body: { method: "eth_estimateUserOperationGas", - params: [userOp, this.bundlerConfig.entryPointAddress], + params: stateOverrideSet + ? [userOp, this.bundlerConfig.entryPointAddress, stateOverrideSet] + : [userOp, this.bundlerConfig.entryPointAddress], id: getTimestampInSeconds(), jsonrpc: "2.0", }, @@ -144,10 +152,6 @@ export class Bundler implements IBundler { const response: UserOpResponse = { userOpHash: sendUserOperationResponse.result, wait: (confirmations?: number): Promise<UserOpReceipt> => { - const providerClient = createPublicClient({ - chain: getChain(chainId), - transport: http(), - }); // Note: maxDuration can be defined per chainId const maxDuration = this.UserOpReceiptMaxDurationIntervals[chainId] || 30000; // default 30 seconds let totalDuration = 0; @@ -159,7 +163,7 @@ export class Bundler implements IBundler { const userOpResponse = await this.getUserOpReceipt(sendUserOperationResponse.result); if (userOpResponse && userOpResponse.receipt && userOpResponse.receipt.blockNumber) { if (confirmations) { - const latestBlock = await providerClient.getBlockNumber(); + const latestBlock = await this.provider.getBlockNumber(); const confirmedBlocks = Number(latestBlock) - userOpResponse.receipt.blockNumber; if (confirmations >= confirmedBlocks) { clearInterval(intervalId); diff --git a/packages/bundler/src/interfaces/IBundler.ts b/packages/bundler/src/interfaces/IBundler.ts index 8ba7ccb50..6ff6d4e63 100644 --- a/packages/bundler/src/interfaces/IBundler.ts +++ b/packages/bundler/src/interfaces/IBundler.ts @@ -1,8 +1,9 @@ +import { StateOverrideSet } from "@biconomy/common"; import { UserOpResponse, UserOpGasResponse, UserOpReceipt, UserOpByHashResponse, UserOpStatus, SimulationType, GasFeeValues } from "../utils/Types"; import { UserOperationStruct } from "@alchemy/aa-core"; export interface IBundler { - estimateUserOpGas(_userOp: Partial<UserOperationStruct>): Promise<UserOpGasResponse>; + estimateUserOpGas(_userOp: Partial<UserOperationStruct>, stateOverrideSet?: StateOverrideSet): Promise<UserOpGasResponse>; sendUserOp(_userOp: UserOperationStruct, _simulationType?: SimulationType): Promise<UserOpResponse>; getUserOpReceipt(_userOpHash: string): Promise<UserOpReceipt>; getUserOpByHash(_userOpHash: string): Promise<UserOpByHashResponse>; diff --git a/packages/bundler/src/utils/Types.ts b/packages/bundler/src/utils/Types.ts index 746d1b24e..1da57281f 100644 --- a/packages/bundler/src/utils/Types.ts +++ b/packages/bundler/src/utils/Types.ts @@ -1,5 +1,5 @@ import { UserOperationStruct } from "@alchemy/aa-core"; -import { Hex } from "viem"; +import { Chain, Hex } from "viem"; export type Bundlerconfig = { bundlerUrl: string; @@ -10,6 +10,8 @@ export type Bundlerconfig = { userOpWaitForTxHashIntervals?: { [key in number]?: number }; userOpReceiptMaxDurationIntervals?: { [key in number]?: number }; userOpWaitForTxHashMaxDurationIntervals?: { [key in number]?: number }; + /** Can be used to optionally override the chain with a custom chain if it doesn't already exist in viems list of supported chains */ + viemChain?: Chain; }; export type BundlerConfigWithChainId = Bundlerconfig & { chainId: number }; diff --git a/packages/bundler/src/utils/Utils.ts b/packages/bundler/src/utils/Utils.ts index a64420100..692cf1bdd 100644 --- a/packages/bundler/src/utils/Utils.ts +++ b/packages/bundler/src/utils/Utils.ts @@ -7,3 +7,16 @@ export const extractChainIdFromBundlerUrl = (url: string): number => { throw new Error("Invalid chain id"); } }; + +export const extractChainIdFromPaymasterUrl = (url: string): number => { + try { + const regex = /\/api\/v\d+\/(\d+)\//; + const match = regex.exec(url); + if (!match) { + throw new Error("Invalid URL format"); + } + return parseInt(match[1]); + } catch (error) { + throw new Error("Invalid chain id"); + } +}; diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md deleted file mode 100644 index 6fa8722c0..000000000 --- a/packages/common/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 4.0.3 (2023-28-02) - -VERSION Bump Only. - -## 4.0.2 (2023-26-02) - -VERSION Bump Only. - -## 4.0.1 (2023-02-22) - -VERSION Bump Only. - -## 4.0.0 (2023-07-02) - -VERSION Bump Only. diff --git a/packages/common/package.json b/packages/common/package.json index 8336c7675..f36d7c059 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/common", - "version": "4.0.3", + "version": "4.1.0", "description": "common utils to be used for aa transactions", "keywords": [ "utils" diff --git a/packages/common/src/utils/HttpRequests.ts b/packages/common/src/utils/HttpRequests.ts index 429ff1103..192a33e4e 100644 --- a/packages/common/src/utils/HttpRequests.ts +++ b/packages/common/src/utils/HttpRequests.ts @@ -38,7 +38,7 @@ export async function sendRequest<T>({ url, method, body }: HttpRequest, service return jsonResponse as T; } if (jsonResponse.error) { - throw new Error(`${jsonResponse.error.message} from ${service}`); + throw new Error(`Error coming from ${service}: ${jsonResponse.error.message}`); } if (jsonResponse.message) { throw new Error(jsonResponse.message); diff --git a/packages/common/src/utils/Types.ts b/packages/common/src/utils/Types.ts index 69508e21f..52eac0216 100644 --- a/packages/common/src/utils/Types.ts +++ b/packages/common/src/utils/Types.ts @@ -11,3 +11,13 @@ export interface LightSigner { getAddress(): Promise<string>; signMessage(message: string | Uint8Array): Promise<string>; } + +export type StateOverrideSet = { + [key: string]: { + balance?: string; + nonce?: string; + code?: string; + state?: object; + stateDiff?: object; + }; +}; diff --git a/packages/modules/CHANGELOG.md b/packages/modules/CHANGELOG.md index 76d05229f..29c3d6c3b 100644 --- a/packages/modules/CHANGELOG.md +++ b/packages/modules/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +VERSION Bump Only. + ## 4.0.3 (2023-28-02) VERSION Bump Only. diff --git a/packages/modules/package.json b/packages/modules/package.json index f9bc8f520..222be7f4e 100644 --- a/packages/modules/package.json +++ b/packages/modules/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/modules", - "version": "4.0.3", + "version": "4.1.0", "description": "This package provides different validation modules/plugins for ERC4337 compatible modular account", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -54,7 +54,7 @@ }, "dependencies": { "@alchemy/aa-core": "^3.1.1", - "@biconomy/common": "^4.0.3", + "@biconomy/common": "^4.1.0", "@ethersproject/abi": "^5.7.0", "merkletreejs": "^0.3.11", "viem": "^2.7.12" @@ -64,7 +64,7 @@ "esbuild": "^0.19.11", "esbuild-plugin-tsc": "^0.4.0", "npm-dts": "^1.3.12", - "@biconomy/paymaster": "^4.0.3", - "@biconomy/modules": "^4.0.3" + "@biconomy/paymaster": "^4.1.0", + "@biconomy/modules": "^4.1.0" } } diff --git a/packages/modules/src/BatchedSessionRouterModule.ts b/packages/modules/src/BatchedSessionRouterModule.ts index d3b222791..cec1cddd4 100644 --- a/packages/modules/src/BatchedSessionRouterModule.ts +++ b/packages/modules/src/BatchedSessionRouterModule.ts @@ -62,7 +62,6 @@ export class BatchedSessionRouterModule extends BaseValidationModule { const sessionModule = await SessionKeyManagerModule.create({ moduleAddress: instance.sessionManagerModuleAddress, smartAccountAddress: moduleConfig.smartAccountAddress, - nodeClientUrl: moduleConfig.nodeClientUrl, storageType: moduleConfig.storageType, }); @@ -99,7 +98,7 @@ export class BatchedSessionRouterModule extends BaseValidationModule { const sessionDataTupleArray = []; // signer must be the same for all the sessions - const { signer: sessionSigner } = await convertSigner(sessionParams[0].sessionSigner); + const { signer: sessionSigner } = await convertSigner(sessionParams[0].sessionSigner, false); const signature = await sessionSigner.signMessage({ raw: toBytes(userOpHash) }); @@ -209,7 +208,7 @@ export class BatchedSessionRouterModule extends BaseValidationModule { // if needed we could do mock signature over userOpHashAndModuleAddress // signer must be the same for all the sessions - const { signer: sessionSigner } = await convertSigner(sessionParams[0].sessionSigner); + const { signer: sessionSigner } = await convertSigner(sessionParams[0].sessionSigner, false); for (const sessionParam of sessionParams) { if (!sessionParam.sessionSigner) { diff --git a/packages/modules/src/ECDSAOwnershipValidationModule.ts b/packages/modules/src/ECDSAOwnershipValidationModule.ts index 71c515f9a..32cfc9af2 100644 --- a/packages/modules/src/ECDSAOwnershipValidationModule.ts +++ b/packages/modules/src/ECDSAOwnershipValidationModule.ts @@ -20,7 +20,7 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { public static async create(moduleConfig: ECDSAOwnershipValidationModuleConfig): Promise<ECDSAOwnershipValidationModule> { // Signer needs to be initialised here before defaultValidationModule is set - const { signer } = await convertSigner(moduleConfig.signer, true); + const { signer } = await convertSigner(moduleConfig.signer, false); const configForConstructor: ECDSAOwnershipValidationModuleConfigConstructorProps = { ...moduleConfig, signer }; // TODO: (Joe) stop doing things in a 'create' call after the instance has been created diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index be5815210..0846b0f83 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -26,7 +26,7 @@ export class MultiChainValidationModule extends BaseValidationModule { public static async create(moduleConfig: MultiChainValidationModuleConfig): Promise<MultiChainValidationModule> { // Signer needs to be initialised here before defaultValidationModule is set - const { signer } = await convertSigner(moduleConfig.signer, true); + const { signer } = await convertSigner(moduleConfig.signer, false); const configForConstructor: MultiChainValidationModuleConfigConstructorProps = { ...moduleConfig, signer }; // TODO: (Joe) stop doing things in a 'create' call after the instance has been created diff --git a/packages/modules/src/SessionKeyManagerModule.ts b/packages/modules/src/SessionKeyManagerModule.ts index b0a45886b..560c9ac60 100644 --- a/packages/modules/src/SessionKeyManagerModule.ts +++ b/packages/modules/src/SessionKeyManagerModule.ts @@ -159,7 +159,7 @@ export class SessionKeyManagerModule extends BaseValidationModule { if (!(params && params.sessionSigner)) { throw new Error("Session signer is not provided."); } - const { signer: sessionSigner } = await convertSigner(params.sessionSigner); + const { signer: sessionSigner } = await convertSigner(params.sessionSigner, false); // Use the sessionSigner to sign the user operation const signature = await sessionSigner.signMessage({ raw: toBytes(userOpHash) }); @@ -194,7 +194,7 @@ export class SessionKeyManagerModule extends BaseValidationModule { if (!(params && params.sessionSigner)) { throw new Error("Session signer is not provided."); } - const { signer: sessionSigner } = await convertSigner(params.sessionSigner); + const { signer: sessionSigner } = await convertSigner(params.sessionSigner, false); let sessionSignerData; if (params?.sessionID) { sessionSignerData = await this.sessionStorageClient.getSessionData({ diff --git a/packages/modules/src/utils/Constants.ts b/packages/modules/src/utils/Constants.ts index 407e374d0..7a2a308fa 100644 --- a/packages/modules/src/utils/Constants.ts +++ b/packages/modules/src/utils/Constants.ts @@ -33,6 +33,8 @@ export const BATCHED_SESSION_ROUTER_MODULE_ADDRESSES_BY_VERSION = { V1_0_0: "0x00000D09967410f8C76752A104c9848b57ebba55", }; +export const DEFAULT_ERC20_MODULE = "0x000000D50C68705bd6897B2d17c7de32FB519fDA"; + export const DEFAULT_MULTICHAIN_MODULE = "0x000000824dc138db84FD9109fc154bdad332Aa8E"; export const MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VERSION = { diff --git a/packages/modules/src/utils/Helper.ts b/packages/modules/src/utils/Helper.ts index 0754a9f30..b0b6fc43d 100644 --- a/packages/modules/src/utils/Helper.ts +++ b/packages/modules/src/utils/Helper.ts @@ -1,5 +1,18 @@ import { UserOperationStruct } from "@alchemy/aa-core"; -import { Hex, encodeAbiParameters, keccak256, parseAbiParameters } from "viem"; +import { Hex, encodeAbiParameters, keccak256, parseAbiParameters, concat, pad, toHex } from "viem"; + +export interface Rule { + offset: number; + condition: number; + referenceValue: `0x${string}`; +} + +export interface Permission { + destContract: `0x${string}`; + functionSelector: `0x${string}`; + valueLimit: bigint; + rules: Rule[]; +} function packUserOp(op: Partial<UserOperationStruct>, forSignature = true): string { if (!op.initCode || !op.callData || !op.paymasterAndData) throw new Error("Missing userOp properties"); @@ -39,3 +52,23 @@ export const getUserOpHash = (userOp: Partial<UserOperationStruct>, entryPointAd const enc = encodeAbiParameters(parseAbiParameters("bytes32, address, uint256"), [userOpHash, entryPointAddress, BigInt(chainId)]); return keccak256(enc); }; + +export async function getABISVMSessionKeyData(sessionKey: `0x${string}` | Uint8Array, permission: Permission): Promise<`0x${string}` | Uint8Array> { + let sessionKeyData = concat([ + sessionKey, + permission.destContract, + permission.functionSelector, + pad(toHex(permission.valueLimit), { size: 16 }), + pad(toHex(permission.rules.length), { size: 2 }), // this can't be more 2**11 (see below), so uint16 (2 bytes) is enough + ]) as `0x${string}`; + + for (let i = 0; i < permission.rules.length; i++) { + sessionKeyData = concat([ + sessionKeyData, + pad(toHex(permission.rules[i].offset), { size: 2 }), // offset is uint16, so there can't be more than 2**16/32 args = 2**11 + pad(toHex(permission.rules[i].condition), { size: 1 }), // uint8 + permission.rules[i].referenceValue, + ]); + } + return sessionKeyData; +} diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index b4b96a2c7..e55d5c8a5 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -1,44 +1,56 @@ import { Chain, Hex } from "viem"; -import { UserOperationStruct, SmartAccountSigner } from "@alchemy/aa-core"; +import { SmartAccountSigner, UserOperationStruct } from "@alchemy/aa-core"; import { SessionKeyManagerModule } from "../SessionKeyManagerModule"; import { ISessionStorage } from "../interfaces/ISessionStorage.js"; import { SupportedSigner } from "@biconomy/common"; export type ModuleVersion = "V1_0_0"; // | 'V1_0_1' export interface BaseValidationModuleConfig { + /** entryPointAddress: address of the entry point */ entryPointAddress?: Hex; } export interface ECDSAOwnershipValidationModuleConfig extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; + /** Signer: viemWallet or ethers signer. Ingested when passed into smartAccount */ signer: SupportedSigner; } export interface ECDSAOwnershipValidationModuleConfigConstructorProps extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; + /** Signer: Converted from viemWallet or ethers signer to SmartAccountSigner */ signer: SmartAccountSigner; } export interface SessionKeyManagerModuleConfig extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; - nodeClientUrl?: string; + /** SmartAccount address */ smartAccountAddress: string; storageType?: StorageType; sessionStorageClient?: ISessionStorage; } export interface BatchedSessionRouterModuleConfig extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; - - sessionKeyManagerModule?: SessionKeyManagerModule; // could be BaseValidationModule - + /** Session Key Manager module: Could be BaseValidationModule */ + sessionKeyManagerModule?: SessionKeyManagerModule; + /** Session Key Manager module address */ sessionManagerModuleAddress?: Hex; - nodeClientUrl?: string; + /** Address of the associated smart account */ smartAccountAddress: string; + /** Storage type, e.g. local storage */ storageType?: StorageType; } @@ -47,9 +59,13 @@ export enum StorageType { } export type SessionParams = { + /** Redundant now as we've favoured uuid() */ sessionID?: string; + /** Session Signer: viemWallet or ethers signer. Ingested when passed into smartAccount */ sessionSigner: SupportedSigner; + /** The session validation module is a sub-module smart-contract which works with session key manager validation module. It validates the userop calldata against the defined session permissions (session key data) within the contract. */ sessionValidationModule?: Hex; + /** Additional info if needed to be appended in signature */ additionalSessionData?: string; }; @@ -57,21 +73,28 @@ export type ModuleInfo = { // Could be a full object of below params and that way it can be an array too! // sessionParams?: SessionParams[] // where SessionParams is below four sessionID?: string; + /** Session Signer: viemWallet or ethers signer. Ingested when passed into smartAccount */ sessionSigner?: SupportedSigner; + /** The session validation module is a sub-module smart-contract which works with session key manager validation module. It validates the userop calldata against the defined session permissions (session key data) within the contract. */ sessionValidationModule?: Hex; + /** Additional info if needed to be appended in signature */ additionalSessionData?: string; batchSessionParams?: SessionParams[]; }; export interface SendUserOpParams extends ModuleInfo { + /** "validation_and_execution" is recommended during development for improved debugging & devEx, but will add some additional latency to calls. "validation" can be used in production ro remove this latency once flows have been tested. */ simulationType?: SimulationType; } export type SimulationType = "validation" | "validation_and_execution"; export type SignerData = { + /** Public key */ pbKey: string; + /** Private key */ pvKey: `0x${string}`; + /** Network Id */ chainId?: Chain; }; @@ -81,27 +104,38 @@ export type CreateSessionDataResponse = { }; export interface CreateSessionDataParams { + /** window end for the session key */ validUntil: number; + /** window start for the session key */ validAfter: number; sessionValidationModule: Hex; sessionPublicKey: Hex; sessionKeyData: Hex; + /** we generate uuid based sessionId. but if you prefer to track it on your side and attach custom session identifier this can be passed */ preferredSessionId?: string; } export interface MultiChainValidationModuleConfig extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; + /** Signer: viemWallet or ethers signer. Ingested when passed into smartAccount */ signer: SupportedSigner; } export interface MultiChainValidationModuleConfigConstructorProps extends BaseValidationModuleConfig { + /** Address of the module */ moduleAddress?: Hex; + /** Version of the module */ version?: ModuleVersion; + /** Signer: viemWallet or ethers signer. Ingested when passed into smartAccount */ signer: SmartAccountSigner; } export type MultiChainUserOpDto = { + /** window end timestamp */ validUntil?: number; + /** window start timestamp */ validAfter?: number; chainId: number; userOp: Partial<UserOperationStruct>; @@ -112,11 +146,15 @@ export interface BaseSessionKeyData { } export interface ERC20SessionKeyData extends BaseSessionKeyData { + /** ERC20 token address */ token: Hex; + /** Recipient address */ recipient: Hex; + /** ERC20 amount (Bigint) */ maxAmount: bigint; } export interface SessionValidationModuleConfig { + /** Address of the module */ moduleAddress: string; } diff --git a/packages/modules/tests/batchedSessionValidationModule.e2e.spec.ts b/packages/modules/tests/batchedSessionValidationModule.e2e.spec.ts new file mode 100644 index 000000000..47f542e54 --- /dev/null +++ b/packages/modules/tests/batchedSessionValidationModule.e2e.spec.ts @@ -0,0 +1,214 @@ +import { + DEFAULT_BATCHED_SESSION_ROUTER_MODULE, + DEFAULT_SESSION_KEY_MANAGER_MODULE, + createBatchedSessionRouterModule, + createSessionKeyManagerModule, +} from "@biconomy/modules"; +import { SessionFileStorage } from "./utils/customSession"; +import { WalletClientSigner, createSmartAccountClient } from "../../account/src/index"; +import { encodeAbiParameters, encodeFunctionData, parseAbi, parseUnits } from "viem"; +import { TestData } from "../../../tests"; +import { checkBalance } from "../../../tests/utils"; +import { PaymasterMode } from "@biconomy/paymaster"; +import { Logger } from "@biconomy/common"; + +describe("Batched Session Router Tests", () => { + let mumbai: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-e2e-tests + [mumbai] = testDataPerChain; + }); + + // TODO(Gabi): Fix Batched Session Router Module tests + it.skip("Should send a user op using Batched Session Validation Module", async () => { + let sessionSigner: WalletClientSigner; + + const { + whale: { + account: { address: sessionKeyEOA }, + privateKey: pvKey, + viemWallet, + }, + minnow: { publicAddress: recipient }, + publicClient, + bundlerUrl, + biconomyPaymasterApiKey, + chainId, + viemChain, + } = mumbai; + + // Create smart account + let smartAccount = await createSmartAccountClient({ + signer: viemWallet, + bundlerUrl, + biconomyPaymasterApiKey, + index: 3, // Increasing index to not conflict with other test cases and use a new smart account + }); + + const smartAccountAddress = await smartAccount.getAddress(); + + const sessionFileStorage: SessionFileStorage = new SessionFileStorage(smartAccountAddress); + + try { + sessionSigner = await sessionFileStorage.getSignerByKey(sessionKeyEOA); + } catch (error) { + sessionSigner = await sessionFileStorage.addSigner({ pbKey: sessionKeyEOA, pvKey, chainId: viemChain }); + } + + expect(sessionSigner).toBeTruthy(); + + // First we need to check if smart account is deployed + // if not deployed, send an empty transaction to deploy it + const isDeployed = await smartAccount.isAccountDeployed(); + + if (!isDeployed) { + const { wait } = await smartAccount.deploy({ paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); + const { success } = await wait(); + expect(success).toBe("true"); + } + + // Create session module + const sessionModule = await createSessionKeyManagerModule({ + moduleAddress: DEFAULT_SESSION_KEY_MANAGER_MODULE, + smartAccountAddress, + sessionStorageClient: sessionFileStorage, + }); + + // Create batched session module + const batchedSessionModule = await createBatchedSessionRouterModule({ + moduleAddress: DEFAULT_BATCHED_SESSION_ROUTER_MODULE, + smartAccountAddress, + sessionKeyManagerModule: sessionModule, + }); + + // Set enabled call on session, only allows calling USDC contract transfer with <= 10 USDC + const sessionKeyData = encodeAbiParameters( + [{ type: "address" }, { type: "address" }, { type: "address" }, { type: "uint256" }], + [ + sessionKeyEOA, + "0xdA5289fCAAF71d52a80A254da614a192b693e977", // erc20 token address + recipient, // receiver address + parseUnits("10", 6), + ], + ); + + // only requires that the caller is the session key + // can call anything using the mock session module + const sessionKeyData2 = encodeAbiParameters([{ type: "address" }], [sessionKeyEOA]); + + const erc20ModuleAddr = "0x000000D50C68705bd6897B2d17c7de32FB519fDA"; + const mockSessionModuleAddr = "0x7Ba4a7338D7A90dfA465cF975Cc6691812C3772E"; + + const sessionTxData = await batchedSessionModule.createSessionData([ + { + validUntil: 0, + validAfter: 0, + sessionValidationModule: erc20ModuleAddr, + sessionPublicKey: sessionKeyEOA, + sessionKeyData: sessionKeyData, + }, + { + validUntil: 0, + validAfter: 0, + sessionValidationModule: mockSessionModuleAddr, + sessionPublicKey: sessionKeyEOA, + sessionKeyData: sessionKeyData2, + }, + ]); + + const setSessionAllowedTrx = { + to: DEFAULT_SESSION_KEY_MANAGER_MODULE, + data: sessionTxData.data, + }; + + const txArray: any = []; + + // Check if session module is enabled + const isEnabled = await smartAccount.isModuleEnabled(DEFAULT_SESSION_KEY_MANAGER_MODULE); + if (!isEnabled) { + const enableModuleTrx = await smartAccount.getEnableModuleData(DEFAULT_SESSION_KEY_MANAGER_MODULE); + txArray.push(enableModuleTrx); + } + + // Check if batched session module is enabled + const isBRMenabled = await smartAccount.isModuleEnabled(DEFAULT_BATCHED_SESSION_ROUTER_MODULE); + if (!isBRMenabled) { + // -----> enableModule batched session router module + const tx2 = await smartAccount.getEnableModuleData(DEFAULT_BATCHED_SESSION_ROUTER_MODULE); + txArray.push(tx2); + } + + txArray.push(setSessionAllowedTrx); + + const userOpResponse1 = await smartAccount.sendTransaction(txArray, { paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); // this user op will enable the modules and setup session allowed calls + const transactionDetails = await userOpResponse1.wait(); + expect(transactionDetails.success).toBe("true"); + Logger.log("Tx Hash: ", transactionDetails.receipt.transactionHash); + + const usdcBalance = await checkBalance(publicClient, smartAccountAddress, "0xdA5289fCAAF71d52a80A254da614a192b693e977"); + expect(usdcBalance).toBeGreaterThan(0); + + smartAccount = smartAccount.setActiveValidationModule(batchedSessionModule); + + // WARNING* If the smart account does not have enough USDC, user op execution will FAIL + const encodedCall = encodeFunctionData({ + abi: parseAbi(["function transfer(address _to, uint256 _value)"]), + functionName: "transfer", + args: [recipient, parseUnits("0.001", 6)], + }); + + const encodedCall2 = encodeFunctionData({ + abi: parseAbi(["function transfer(address _to, uint256 _value)"]), + functionName: "transfer", + args: ["0xd3C85Fdd3695Aee3f0A12B3376aCD8DC54020549", parseUnits("0.001", 6)], + }); + + const transferTx = { + to: "0xdA5289fCAAF71d52a80A254da614a192b693e977", + data: encodedCall, + }; + + const transferTx2 = { + to: "0xdA5289fCAAF71d52a80A254da614a192b693e977", + data: encodedCall2, + }; + + const activeModule = smartAccount.activeValidationModule; + expect(activeModule).toEqual(batchedSessionModule); + + const maticBalanceBefore = await checkBalance(publicClient, smartAccountAddress); + + // failing with dummyTx because of invalid sessionKeyData + const userOpResponse2 = await smartAccount.sendTransaction([transferTx, transferTx2], { + params: { + batchSessionParams: [ + { + sessionSigner: sessionSigner, + sessionValidationModule: erc20ModuleAddr, + }, + { + sessionSigner: sessionSigner, + sessionValidationModule: mockSessionModuleAddr, + }, + ], + }, + paymasterServiceData: { + mode: PaymasterMode.SPONSORED, + }, + }); + + const receipt = await userOpResponse2.wait(); + console.log(receipt.userOpHash, "Batched user op hash"); + expect(receipt.success).toBe("true"); + + expect(userOpResponse2.userOpHash).toBeTruthy(); + expect(userOpResponse2.userOpHash).not.toBeNull(); + + const maticBalanceAfter = await checkBalance(publicClient, smartAccountAddress); + + expect(maticBalanceAfter).toEqual(maticBalanceBefore); + + Logger.log(`Tx at: https://jiffyscan.xyz/userOpHash/${userOpResponse2.userOpHash}?network=mumbai`); + }, 60000); +}); diff --git a/packages/modules/tests/ecdsaValidationModule.e2e.spec.ts b/packages/modules/tests/ecdsaValidationModule.e2e.spec.ts new file mode 100644 index 000000000..84b3f627e --- /dev/null +++ b/packages/modules/tests/ecdsaValidationModule.e2e.spec.ts @@ -0,0 +1,63 @@ +import { PaymasterMode } from "@biconomy/paymaster"; +import { TestData } from "../../../tests"; +import { createSmartAccountClient } from "../../account/src/index"; +import { Hex, encodeFunctionData, parseAbi } from "viem"; +import { DEFAULT_MULTICHAIN_MODULE, createECDSAOwnershipValidationModule } from "@biconomy/modules"; + +describe("Account with ECDSAOwnershipValidationModule Module Tests", () => { + let mumbai: TestData; + let baseSepolia: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-e2e-tests + [mumbai, baseSepolia] = testDataPerChain; + }); + + it("should create a ECDSAOwnershipValidationModule with signer", async () => { + const { + bundlerUrl, + whale: { viemWallet: signer }, + } = mumbai; + + const defaultValidationModule = await createECDSAOwnershipValidationModule({ signer }); + // Should not require a signer or chainId + const smartAccount = await createSmartAccountClient({ + bundlerUrl, + defaultValidationModule, + signer, + }); + const address = await smartAccount.getAccountAddress(); + expect(address).toBeTruthy(); + expect(smartAccount.activeValidationModule).toEqual(defaultValidationModule); + }); + + it("should create a ECDSAOwnershipValidationModule without signer", async () => { + const { + bundlerUrl, + whale: { viemWallet: signer }, + } = mumbai; + + const defaultValidationModule = await createECDSAOwnershipValidationModule({ signer }); + // Should not require a signer or chainId + const smartAccount = await createSmartAccountClient({ + bundlerUrl, + defaultValidationModule, + }); + const address = await smartAccount.getAccountAddress(); + expect(address).toBeTruthy(); + expect(smartAccount.activeValidationModule).toEqual(defaultValidationModule); + }); + + it("should create a ECDSAOwnershipValidationModule by default, without explicitly setting it on the smart account", async () => { + const { + bundlerUrl, + whale: { viemWallet: signer }, + } = mumbai; + const defaultValidationModule = await createECDSAOwnershipValidationModule({ signer }); + const smartAccount = await createSmartAccountClient({ bundlerUrl, signer }); + const address = await smartAccount.getAccountAddress(); + expect(address).toBeTruthy(); + const smartAccountValidationModuleAddress = await smartAccount.activeValidationModule.getAddress(); + expect(smartAccountValidationModuleAddress).toEqual(defaultValidationModule.moduleAddress); + }); +}); diff --git a/packages/modules/tests/modules.e2e.spec.ts b/packages/modules/tests/modules.e2e.spec.ts new file mode 100644 index 000000000..9de6d50ee --- /dev/null +++ b/packages/modules/tests/modules.e2e.spec.ts @@ -0,0 +1,62 @@ +import { TestData } from "../../../tests"; +import { PaymasterMode, Transaction, createSmartAccountClient } from "@biconomy/account"; +import { DEFAULT_BATCHED_SESSION_ROUTER_MODULE, DEFAULT_ERC20_MODULE, DEFAULT_SESSION_KEY_MANAGER_MODULE } from "../src"; + +describe("Account Tests", () => { + let mumbai: TestData; + + beforeEach(() => { + // @ts-ignore: Comes from setup-unit-tests + [mumbai] = testDataPerChain; + }); + + it("should enable batched module", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl, + biconomyPaymasterApiKey, + }); + + const isBRMenabled = await smartAccount.isModuleEnabled(DEFAULT_BATCHED_SESSION_ROUTER_MODULE); + + if (!isBRMenabled) { + const tx = await smartAccount.getEnableModuleData(DEFAULT_BATCHED_SESSION_ROUTER_MODULE); + const { wait } = await smartAccount.sendTransaction(tx, { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + }); + const { success } = await wait(); + expect(success).toBe("true"); + } + }, 50000); + + it("should enable session module", async () => { + const { + whale: { viemWallet: signer }, + bundlerUrl, + biconomyPaymasterApiKey, + } = mumbai; + + const smartAccount = await createSmartAccountClient({ + signer, + bundlerUrl, + biconomyPaymasterApiKey, + }); + + const isSessionKeyEnabled = await smartAccount.isModuleEnabled(DEFAULT_SESSION_KEY_MANAGER_MODULE); + + if (!isSessionKeyEnabled) { + const tx = await smartAccount.getEnableModuleData(DEFAULT_SESSION_KEY_MANAGER_MODULE); + const { wait } = await smartAccount.sendTransaction(tx, { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + }); + const { success } = await wait(); + expect(success).toBe("true"); + } + }, 50000); +}); diff --git a/packages/modules/tests/modules.spec.ts b/packages/modules/tests/modules.spec.ts index 9785b2d2d..a799a6760 100644 --- a/packages/modules/tests/modules.spec.ts +++ b/packages/modules/tests/modules.spec.ts @@ -14,21 +14,23 @@ describe("Account Tests", () => { const { bundlerUrl, whale: { ethersSigner: signer }, + viemChain, } = ganache; const defaultValidationModule = await createMultiChainValidationModule({ signer }); // Should not require a signer or chainId - const smartAccount = await createSmartAccountClient({ bundlerUrl, defaultValidationModule }); + const smartAccount = await createSmartAccountClient({ bundlerUrl, defaultValidationModule, rpcUrl: viemChain.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); // expect the relevant module to be set expect(smartAccount.activeValidationModule).toEqual(defaultValidationModule); - }); + }, 50000); it("should create a ECDSAOwnershipValidationModule from a viem signer using convertSigner", async () => { const { bundlerUrl, whale: { viemWallet: signer }, + viemChain, } = ganache; const defaultValidationModule = await createECDSAOwnershipValidationModule({ signer }); @@ -36,10 +38,11 @@ describe("Account Tests", () => { const smartAccount = await createSmartAccountClient({ bundlerUrl, defaultValidationModule, + rpcUrl: viemChain.rpcUrls.default.http[0], }); const address = await smartAccount.getAccountAddress(); expect(address).toBeTruthy(); // expect the relevant module to be set expect(smartAccount.activeValidationModule).toEqual(defaultValidationModule); - }); + }, 50000); }); diff --git a/packages/modules/tests/multiChainValidationModule.e2e.spec.ts b/packages/modules/tests/multiChainValidationModule.e2e.spec.ts index 5871102f1..996be004c 100644 --- a/packages/modules/tests/multiChainValidationModule.e2e.spec.ts +++ b/packages/modules/tests/multiChainValidationModule.e2e.spec.ts @@ -3,8 +3,9 @@ import { TestData } from "../../../tests"; import { createSmartAccountClient } from "../../account/src/index"; import { Hex, encodeFunctionData, parseAbi } from "viem"; import { DEFAULT_MULTICHAIN_MODULE, MultiChainValidationModule } from "@biconomy/modules"; +import { Logger } from "@biconomy/common"; -describe("Account with MultiChainValidation Module Tests", () => { +describe("MultiChainValidation Module Tests", () => { let mumbai: TestData; let baseSepolia: TestData; @@ -16,14 +17,14 @@ describe("Account with MultiChainValidation Module Tests", () => { it("Should mint an NFT gasless on baseSepolia and mumbai", async () => { const { whale: { alchemyWalletClientSigner: signerMumbai, publicAddress: recipientForBothChains }, - biconomyPaymasterApiKey: biconomyPaymasterApiKeyMumbai, + paymasterUrl: biconomyPaymasterApiKeyMumbai, bundlerUrl: bundlerUrlMumbai, chainId: chainIdMumbai, } = mumbai; const { whale: { alchemyWalletClientSigner: signerBase }, - biconomyPaymasterApiKey: biconomyPaymasterApiKeyBase, + paymasterUrl: biconomyPaymasterApiKeyBase, bundlerUrl: bundlerUrlBase, chainId: chainIdBase, } = baseSepolia; @@ -42,7 +43,7 @@ describe("Account with MultiChainValidation Module Tests", () => { bundlerUrl: bundlerUrlMumbai, defaultValidationModule: multiChainModule, activeValidationModule: multiChainModule, - biconomyPaymasterApiKey: biconomyPaymasterApiKeyMumbai, + paymasterUrl: biconomyPaymasterApiKeyMumbai, }), createSmartAccountClient({ chainId: chainIdBase, @@ -50,10 +51,33 @@ describe("Account with MultiChainValidation Module Tests", () => { bundlerUrl: bundlerUrlBase, defaultValidationModule: multiChainModule, activeValidationModule: multiChainModule, - biconomyPaymasterApiKey: biconomyPaymasterApiKeyBase, + paymasterUrl: biconomyPaymasterApiKeyBase, }), ]); + // Check if the smart account has been deployed + const [isPolygonDeployed, isBaseDeployed] = await Promise.all([polygonAccount.isAccountDeployed(), baseAccount.isAccountDeployed()]); + if (!isPolygonDeployed) { + const { wait } = await polygonAccount.deploy({ paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); + const { success } = await wait(); + expect(success).toBe("true"); + } + if (!isBaseDeployed) { + const { wait } = await baseAccount.deploy({ paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); + const { success } = await wait(); + expect(success).toBe("true"); + } + + const moduleEnabled1 = await polygonAccount.isModuleEnabled(DEFAULT_MULTICHAIN_MODULE); + const moduleActive1 = polygonAccount.activeValidationModule; + expect(moduleEnabled1).toBeTruthy(); + expect(moduleActive1.getAddress()).toBe(DEFAULT_MULTICHAIN_MODULE); + + const moduleEnabled2 = await baseAccount.isModuleEnabled(DEFAULT_MULTICHAIN_MODULE); + const moduleActive2 = polygonAccount.activeValidationModule; + expect(moduleEnabled2).toBeTruthy(); + expect(moduleActive2.getAddress()).toBe(DEFAULT_MULTICHAIN_MODULE); + const encodedCall = encodeFunctionData({ abi: parseAbi(["function safeMint(address owner) view returns (uint balance)"]), functionName: "safeMint", @@ -83,10 +107,16 @@ describe("Account with MultiChainValidation Module Tests", () => { const userOpResponse1 = await baseAccount.sendSignedUserOp(returnedOps[0] as any); const userOpResponse2 = await polygonAccount.sendSignedUserOp(returnedOps[1] as any); - console.log(userOpResponse1.userOpHash, "MULTICHAIN BASE USER OP HASH"); - console.log(userOpResponse2.userOpHash, "MULTICHAIN POLYGON USER OP HASH"); + Logger.log(userOpResponse1.userOpHash, "MULTICHAIN BASE USER OP HASH"); + Logger.log(userOpResponse2.userOpHash, "MULTICHAIN POLYGON USER OP HASH"); expect(userOpResponse1.userOpHash).toBeTruthy(); expect(userOpResponse2.userOpHash).toBeTruthy(); - }, 30000); + + const { success: success1 } = await userOpResponse1.wait(); + const { success: success2 } = await userOpResponse2.wait(); + + expect(success1).toBe("true"); + expect(success2).toBe("true"); + }, 50000); }); diff --git a/packages/modules/tests/sessionValidationModule.e2e.spec.ts b/packages/modules/tests/sessionValidationModule.e2e.spec.ts index 87b139cf0..286cfb9a7 100644 --- a/packages/modules/tests/sessionValidationModule.e2e.spec.ts +++ b/packages/modules/tests/sessionValidationModule.e2e.spec.ts @@ -1,12 +1,15 @@ import { DEFAULT_SESSION_KEY_MANAGER_MODULE, createSessionKeyManagerModule } from "@biconomy/modules"; import { SessionFileStorage } from "./utils/customSession"; import { WalletClientSigner, createSmartAccountClient } from "../../account/src/index"; -import { Hex, encodeAbiParameters, encodeFunctionData, parseAbi, parseUnits } from "viem"; +import { Hex, encodeAbiParameters, encodeFunctionData, pad, parseAbi, parseEther, parseUnits, slice, toFunctionSelector } from "viem"; import { TestData } from "../../../tests"; import { checkBalance } from "../../../tests/utils"; import { PaymasterMode } from "@biconomy/paymaster"; +import { Logger } from "@biconomy/common"; +import { getABISVMSessionKeyData } from "../src/utils/Helper"; +import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; -describe("Account Tests", () => { +describe("Session Validation Module Tests", () => { let mumbai: TestData; beforeEach(() => { @@ -14,16 +17,16 @@ describe("Account Tests", () => { [mumbai] = testDataPerChain; }); - const sessionFileStorage: SessionFileStorage = new SessionFileStorage(DEFAULT_SESSION_KEY_MANAGER_MODULE); - - it("Should send a user op using Session Validation Module", async () => { + // TODO(Gabi): Fix Session Validation Module tests + it.skip("Should send a user op using Session Validation Module", async () => { let sessionSigner: WalletClientSigner; - const { whale: { account: { address: sessionKeyEOA }, privateKey: pvKey, + viemWallet, }, + viemChain, minnow: { publicAddress: recipient }, publicClient, chainId, @@ -31,23 +34,37 @@ describe("Account Tests", () => { biconomyPaymasterApiKey, } = mumbai; - try { - sessionSigner = await sessionFileStorage.getSignerByKey(sessionKeyEOA); - } catch (error) { - sessionSigner = await sessionFileStorage.addSigner({ pbKey: sessionKeyEOA, pvKey }); - } - - expect(sessionSigner).toBeTruthy(); - // Create smart account let smartAccount = await createSmartAccountClient({ chainId, - signer: sessionSigner, + signer: viemWallet, bundlerUrl, biconomyPaymasterApiKey, index: 1, // Increasing index to not conflict with other test cases and use a new smart account }); + const accountAddress = await smartAccount.getAccountAddress(); + const sessionFileStorage: SessionFileStorage = new SessionFileStorage(accountAddress); + + // First we need to check if smart account is deployed + // if not deployed, send an empty transaction to deploy it + const isDeployed = await smartAccount.isAccountDeployed(); + + Logger.log("session", { isDeployed }); + + if (!isDeployed) { + const { wait } = await smartAccount.deploy({ paymasterServiceData: { mode: PaymasterMode.SPONSORED } }); + const { success } = await wait(); + expect(success).toBe("true"); + } + + try { + sessionSigner = await sessionFileStorage.getSignerByKey(sessionKeyEOA); + } catch (error) { + sessionSigner = await sessionFileStorage.addSigner({ pbKey: sessionKeyEOA, pvKey, chainId: viemChain }); + } + expect(sessionSigner).toBeTruthy(); + // Create session module const sessionModule = await createSessionKeyManagerModule({ moduleAddress: DEFAULT_SESSION_KEY_MANAGER_MODULE, @@ -55,26 +72,30 @@ describe("Account Tests", () => { sessionStorageClient: sessionFileStorage, }); + const functionSelector = slice(toFunctionSelector("safeMint(address)"), 0, 4); // Set enabled call on session - const sessionKeyData = encodeAbiParameters( - [{ type: "address" }, { type: "address" }, { type: "address" }, { type: "uint256" }], - [ - sessionKeyEOA, - "0xdA5289fCAAF71d52a80A254da614a192b693e977", // erc20 token address - recipient, // receiver address - parseUnits("10", 6), + const sessionKeyData = await getABISVMSessionKeyData(sessionKeyEOA as Hex, { + destContract: "0xdd526eba63ef200ed95f0f0fb8993fe3e20a23d0" as Hex, // nft address + functionSelector: functionSelector, + valueLimit: parseEther("0"), + rules: [ + { + offset: 0, // offset 0 means we are checking first parameter of safeMint (recipient address) + condition: 0, // 0 = Condition.EQUAL + referenceValue: pad("0xd3C85Fdd3695Aee3f0A12B3376aCD8DC54020549", { size: 32 }), // recipient address + }, ], - ); + }); - const erc20ModuleAddr = "0x000000D50C68705bd6897B2d17c7de32FB519fDA"; + const abiSvmAddress = "0x000006bC2eCdAe38113929293d241Cf252D91861"; const sessionTxData = await sessionModule.createSessionData([ { validUntil: 0, validAfter: 0, - sessionValidationModule: erc20ModuleAddr, - sessionPublicKey: sessionKeyEOA, - sessionKeyData: sessionKeyData, + sessionValidationModule: abiSvmAddress, + sessionPublicKey: sessionKeyEOA as Hex, + sessionKeyData: sessionKeyData as Hex, }, ]); @@ -86,31 +107,36 @@ describe("Account Tests", () => { const txArray: any = []; // Check if module is enabled - const isEnabled = await smartAccount.isModuleEnabled(DEFAULT_SESSION_KEY_MANAGER_MODULE); + if (!isEnabled) { const enableModuleTrx = await smartAccount.getEnableModuleData(DEFAULT_SESSION_KEY_MANAGER_MODULE); txArray.push(enableModuleTrx); txArray.push(setSessionAllowedTrx); } else { - console.log("MODULE ALREADY ENABLED"); + Logger.log("MODULE ALREADY ENABLED"); txArray.push(setSessionAllowedTrx); } - const userOp = await smartAccount.buildUserOp(txArray); + const userOp = await smartAccount.buildUserOp(txArray, { + paymasterServiceData: { + mode: PaymasterMode.SPONSORED, + }, + }); const userOpResponse1 = await smartAccount.sendUserOp(userOp); const transactionDetails = await userOpResponse1.wait(); - console.log("Tx Hash: ", transactionDetails.receipt.transactionHash); + expect(transactionDetails.success).toBe("true"); + Logger.log("Tx Hash: ", transactionDetails.receipt.transactionHash); const encodedCall = encodeFunctionData({ - abi: parseAbi(["function transfer(address _to, uint256 _value)"]), - functionName: "transfer", - args: [recipient, parseUnits("0.01", 6)], + abi: parseAbi(["function safeMint(address _to)"]), + functionName: "safeMint", + args: ["0xd3C85Fdd3695Aee3f0A12B3376aCD8DC54020549"], }); - const transferTx = { - to: "0xdA5289fCAAF71d52a80A254da614a192b693e977", //erc20 token address + const nftMintTx = { + to: "0xdd526eba63ef200ed95f0f0fb8993fe3e20a23d0", data: encodedCall, }; @@ -118,24 +144,10 @@ describe("Account Tests", () => { const maticBalanceBefore = await checkBalance(publicClient, await smartAccount.getAccountAddress()); - const transferUserOp = await smartAccount.buildUserOp([transferTx], { - params: { - sessionSigner: sessionSigner, - sessionValidationModule: erc20ModuleAddr.toLowerCase() as Hex, - }, - paymasterServiceData: { - mode: PaymasterMode.SPONSORED, - }, - }); - - expect(transferUserOp.paymasterAndData).toBeDefined(); - expect(transferUserOp.paymasterAndData).not.toBeNull(); - expect(transferUserOp.paymasterAndData).not.toBe("0x"); - - const userOpResponse2 = await smartAccount.sendTransaction(transferTx, { + const userOpResponse2 = await smartAccount.sendTransaction(nftMintTx, { params: { sessionSigner: sessionSigner, - sessionValidationModule: erc20ModuleAddr.toLowerCase() as Hex, + sessionValidationModule: abiSvmAddress, }, paymasterServiceData: { mode: PaymasterMode.SPONSORED, @@ -149,6 +161,6 @@ describe("Account Tests", () => { expect(maticBalanceAfter).toEqual(maticBalanceBefore); - console.log(`Tx at: https://jiffyscan.xyz/userOpHash/${userOpResponse2.userOpHash}?network=mumbai`); + Logger.log(`Tx at: https://jiffyscan.xyz/userOpHash/${userOpResponse2.userOpHash}?network=mumbai`); }, 60000); }); diff --git a/packages/modules/tests/utils/customSession.ts b/packages/modules/tests/utils/customSession.ts index 002bcd2a5..1326c4871 100644 --- a/packages/modules/tests/utils/customSession.ts +++ b/packages/modules/tests/utils/customSession.ts @@ -1,5 +1,5 @@ import * as fs from "fs"; -import { SmartAccountSigner, WalletClientSigner } from "@alchemy/aa-core"; +import { SmartAccountSigner, WalletClientSigner, getChain } from "@alchemy/aa-core"; import { SignerData } from "@biconomy/modules/src"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { Hex, createWalletClient, http } from "viem"; @@ -92,10 +92,21 @@ export class SessionFileStorage implements ISessionStorage { return address.toLowerCase() as Hex; } - async getSessionData(): Promise<SessionLeafNode> { + async getSessionData(param: SessionSearchParam): Promise<SessionLeafNode> { const sessions = (await this.getSessionStore()).leafNodes; - Logger.log("Got sessions", sessions); - const session = sessions[0]; + const session = sessions.find((s: SessionLeafNode) => { + if (param.sessionID) { + return s.sessionID === param.sessionID && (!param.status || s.status === param.status); + } else if (param.sessionPublicKey && param.sessionValidationModule) { + return ( + s.sessionPublicKey === this.toLowercaseAddress(param.sessionPublicKey) && + s.sessionValidationModule === this.toLowercaseAddress(param.sessionValidationModule) && + (!param.status || s.status === param.status) + ); + } else { + return undefined; + } + }); if (!session) { throw new Error("Session not found."); @@ -156,10 +167,11 @@ export class SessionFileStorage implements ISessionStorage { signer = signerData; } const accountSigner = privateKeyToAccount(signer.pvKey); + const viemChain = getChain(signerData?.chainId?.id || 1); const client = createWalletClient({ account: accountSigner, chain: signerData.chainId, - transport: http(polygonMumbai.rpcUrls.default.http[0]), + transport: http(viemChain.rpcUrls.default.http[0]), }); const walletClientSigner: SmartAccountSigner = new WalletClientSigner( client, @@ -191,8 +203,8 @@ export class SessionFileStorage implements ISessionStorage { return new WalletClientSigner(walletClient, "json-rpc"); } - async getSignerBySession(): Promise<WalletClientSigner> { - const session = await this.getSessionData(); + async getSignerBySession(param: SessionSearchParam): Promise<WalletClientSigner> { + const session = await this.getSessionData(param); Logger.log("got session", session); const walletClientSinger = await this.getSignerByKey(session.sessionPublicKey); return walletClientSinger; diff --git a/packages/particle-auth/CHANGELOG.md b/packages/particle-auth/CHANGELOG.md index 0fe532b3e..2805153cc 100644 --- a/packages/particle-auth/CHANGELOG.md +++ b/packages/particle-auth/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +VERSION Bump Only. + ## 4.0.3 (2023-28-02) Fix build diff --git a/packages/particle-auth/package.json b/packages/particle-auth/package.json index e0c1ee819..b52766c2d 100644 --- a/packages/particle-auth/package.json +++ b/packages/particle-auth/package.json @@ -1,9 +1,19 @@ { "name": "@biconomy/particle-auth", - "version": "4.0.3", + "version": "4.1.0", "description": "Particle auth for Biconomy SDK", - "main": "./dist/src/index.js", - "typings": "./dist/src/index.d.ts", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "typings": "./dist/types/index.d.ts", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "./package.json": "./package.json" + }, "keywords": [ "legos", "batching", @@ -25,7 +35,16 @@ }, "scripts": { "unbuild": "rimraf dist *.tsbuildinfo", - "build": "rimraf dist && tsc", + "build:watch": "yarn build:tsc --watch", + "dist:minify": "esbuild ./dist/esm/**/*.js ./dist/esm/*.js --minify --outdir=./dist/esm --bundle=false --allow-overwrite", + "build": "yarn unbuild && yarn build:tsc", + "build:esbuild": "yarn build:esbuild:cjs && yarn build:esbuild:esm && yarn build:typ", + "build:tsc:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir ./dist/cjs --removeComments --verbatimModuleSyntax false && echo > ./dist/cjs/package.json '{\"type\":\"commonjs\"}'", + "build:tsc:esm": "tsc --project tsconfig.build.json --module esnext --outDir ./dist/esm --removeComments && echo > ./dist/esm/package.json '{\"type\":\"module\"}'", + "build:esbuild:cjs": "node .esbuild.js CJS && echo > ./dist/cjs/package.json '{\"type\":\"commonjs\"}'", + "build:esbuild:esm": "node .esbuild.js ESM && echo > ./dist/esm/package.json '{\"type\":\"module\"}'", + "build:typ": "tsc --project tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap", + "build:tsc": "yarn build:tsc:cjs && yarn build:tsc:esm && yarn build:typ && yarn dist:minify", "format": "prettier --write \"{src,tests}/**/*.ts\"", "lint": "tslint -p tsconfig.json" }, diff --git a/packages/particle-auth/tsconfig.json b/packages/particle-auth/tsconfig.json index 3dc5293ed..cda17a184 100644 --- a/packages/particle-auth/tsconfig.json +++ b/packages/particle-auth/tsconfig.json @@ -5,7 +5,8 @@ "outDir": "dist", "baseUrl": "src", "resolveJsonModule": true, - "esModuleInterop": true + "esModuleInterop": true, + "lib": ["es2020"], }, "include": ["src", "src/**/*.json"] } diff --git a/packages/paymaster/CHANGELOG.md b/packages/paymaster/CHANGELOG.md index aa60e1174..3003cef25 100644 --- a/packages/paymaster/CHANGELOG.md +++ b/packages/paymaster/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +VERSION Bump Only. + ## 4.0.3 (2023-28-02) VERSION Bump Only. diff --git a/packages/paymaster/package.json b/packages/paymaster/package.json index 6360011b7..34f093113 100644 --- a/packages/paymaster/package.json +++ b/packages/paymaster/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/paymaster", - "version": "4.0.3", + "version": "4.1.0", "description": "Biconomy Paymaster to interact with Paymaster Services that interacts with ( veriying and token ) paymasters", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -54,7 +54,7 @@ }, "dependencies": { "@alchemy/aa-core": "^3.1.1", - "@biconomy/common": "^4.0.3", + "@biconomy/common": "^4.1.0", "viem": "^2.7.12" }, "devDependencies": { diff --git a/packages/paymaster/src/BiconomyPaymaster.ts b/packages/paymaster/src/BiconomyPaymaster.ts index bfe04699c..aa0af3f86 100644 --- a/packages/paymaster/src/BiconomyPaymaster.ts +++ b/packages/paymaster/src/BiconomyPaymaster.ts @@ -189,7 +189,7 @@ export class BiconomyPaymaster implements IHybridPaymaster<SponsorUserOperationD jsonrpc: "2.0", }, }, - "Bundler", + "Paymaster", ); if (response && response.result) { diff --git a/packages/paymaster/src/utils/Types.ts b/packages/paymaster/src/utils/Types.ts index d1dd7ec9f..9d5f849d4 100644 --- a/packages/paymaster/src/utils/Types.ts +++ b/packages/paymaster/src/utils/Types.ts @@ -93,6 +93,7 @@ export type PaymasterFeeQuote = { /** maxGasFee: in dollars */ maxGasFeeUSD?: number; usdPayment?: number; + /** The premium paid on the token */ premiumPercentage: number; /** validUntil: Unix timestamp */ validUntil?: number; @@ -105,6 +106,8 @@ export type BiconomyTokenPaymasterRequest = { spender: Hex; /** Not recommended */ maxApproval?: boolean; + /* skip option to patch callData if approval is already given to the paymaster */ + skipPatchCallData?: boolean; }; export type FeeQuotesOrDataResponse = { diff --git a/packages/transak/CHANGELOG.md b/packages/transak/CHANGELOG.md index 9d1cd4246..552743755 100644 --- a/packages/transak/CHANGELOG.md +++ b/packages/transak/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 4.1.0 (2023-04-03) + +VERSION Bump Only. + ## 4.0.3 (2023-28-02) VERSION Bump Only. @@ -11,7 +15,9 @@ VERSION Bump Only. VERSION Bump Only. -## 4.0.1 (2023-02-22) +## 4.0.1 (2023-26-02) + +VERSION Bump Only. ## 4.0.0 (2023-12-28) diff --git a/packages/transak/package.json b/packages/transak/package.json index 9afe8a662..eedc20c21 100644 --- a/packages/transak/package.json +++ b/packages/transak/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/transak", - "version": "4.0.3", + "version": "4.1.0", "description": "transak for biconomy sdk", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", diff --git a/tests/chains.config.ts b/tests/chains.config.ts index c82722a70..97e453c29 100644 --- a/tests/chains.config.ts +++ b/tests/chains.config.ts @@ -1,10 +1,10 @@ import { localhost, Chain } from "viem/chains"; -import { polygonMumbai, baseSepolia } from "viem/chains"; +import { polygonMumbai, baseSepolia, optimism } from "viem/chains"; import { config } from "dotenv"; config(); -export type SupportedTestChain = "ganache" | "baseSepolia" | "mumbai"; +export type SupportedTestChain = "ganache" | "baseSepolia" | "mumbai" | "optimism"; type BaseChainConfig = { chainId: number; entryPointAddress: string; @@ -12,6 +12,8 @@ type BaseChainConfig = { paymasterUrl?: string; viemChain: Chain; biconomyPaymasterApiKey?: string; + deploymentCost: number; + nftAddress: string; }; export const CHAIN_CONFIG: Record<SupportedTestChain, BaseChainConfig> = { ganache: { // No useful bundler or paymaster tests for ganache @@ -19,6 +21,8 @@ export const CHAIN_CONFIG: Record<SupportedTestChain, BaseChainConfig> = { entryPointAddress: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", bundlerUrl: "https://bundler.biconomy.io/api/v2/1/cJPK7B3ru.dd7f7861-190d-45ic-af80-6877f74b8f44", viemChain: localhost, + deploymentCost: 100000000000000000, + nftAddress: "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e", }, baseSepolia: { chainId: 84532, @@ -27,6 +31,8 @@ export const CHAIN_CONFIG: Record<SupportedTestChain, BaseChainConfig> = { paymasterUrl: "https://paymaster.biconomy.io/api/v1/84532/" + process.env.E2E_BICO_PAYMASTER_KEY_BASE!, viemChain: baseSepolia, biconomyPaymasterApiKey: process.env.E2E_BICO_PAYMASTER_KEY_BASE!, + deploymentCost: 100000000000000000, + nftAddress: "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e", }, mumbai: { chainId: 80001, @@ -35,9 +41,21 @@ export const CHAIN_CONFIG: Record<SupportedTestChain, BaseChainConfig> = { paymasterUrl: "https://paymaster.biconomy.io/api/v1/80001/" + process.env.E2E_BICO_PAYMASTER_KEY_MUMBAI!, viemChain: polygonMumbai, biconomyPaymasterApiKey: process.env.E2E_BICO_PAYMASTER_KEY_MUMBAI!, + deploymentCost: 100000000000000000, + nftAddress: "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e", }, + optimism: { + chainId: 10, + entryPointAddress: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", + bundlerUrl: "https://bundler.biconomy.io/api/v2/10/cJPK7B3ru.dd7f7861-190d-45ic-af80-6877f74b8f44", + paymasterUrl: "https://paymaster.biconomy.io/api/v1/10/" + process.env.E2E_BICO_PAYMASTER_KEY_OP!, + viemChain: optimism, + biconomyPaymasterApiKey: process.env.E2E_BICO_PAYMASTER_KEY_OP!, + deploymentCost: 100000000000000000, + nftAddress: "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e", + } }; -export const E2E_TEST_CHAINS = [CHAIN_CONFIG.mumbai, CHAIN_CONFIG.baseSepolia]; +export const E2E_TEST_CHAINS = [CHAIN_CONFIG.mumbai, CHAIN_CONFIG.baseSepolia, CHAIN_CONFIG.optimism]; export const UNIT_TEST_CHAIN = CHAIN_CONFIG.ganache; export default CHAIN_CONFIG; \ No newline at end of file diff --git a/tests/index.d.ts b/tests/index.d.ts index d3093f47a..25e39cf50 100644 --- a/tests/index.d.ts +++ b/tests/index.d.ts @@ -6,7 +6,7 @@ import { Signer } from "@ethersproject/abstract-signer"; interface WalletProps { alchemyWalletClientSigner: WalletClientSigner; viemWallet: WalletClient; - balance: BigInt; + balance: bigint; publicAddress: Hex; account: PrivateKeyAccount; privateKey: Hex; @@ -14,6 +14,8 @@ interface WalletProps { } export type TestData = { + nftAddress: Hex; + deploymentCost: number; whale: WalletProps; minnow: WalletProps; publicClient: PublicClient; @@ -21,6 +23,7 @@ export type TestData = { bundlerUrl: string; entryPointAddress: string; viemChain: Chain; + paymasterUrl: string; biconomyPaymasterApiKey: string; ethersProvider: JsonRpcProvider; }; diff --git a/tests/setup-e2e-tests.ts b/tests/setup-e2e-tests.ts index a95150f4a..4d3d8af77 100644 --- a/tests/setup-e2e-tests.ts +++ b/tests/setup-e2e-tests.ts @@ -85,9 +85,9 @@ beforeAll(async () => { })) .sort((a, b) => { if (a.balance > b.balance) { - return 1; - } else if (a.balance > b.balance) { return -1; + } else if (a.balance > b.balance) { + return 1; } else { return 0; } @@ -100,11 +100,13 @@ beforeAll(async () => { publicClient: whaleBalance.publicClient, chainId: whaleBalance.chainId, bundlerUrl: whaleBalance.bundlerUrl, + deploymentCost: whaleBalance.deploymentCost, + nftAddress: whaleBalance.nftAddress, entryPointAddress: whaleBalance.entryPointAddress, viemChain: whaleBalance.viemChain, - biconomyPaymasterApiKey: whaleBalance.biconomyPaymasterApiKey, ethersProvider: whaleBalance.ethersProvider, paymasterUrl: whaleBalance.paymasterUrl, + biconomyPaymasterApiKey: whaleBalance.biconomyPaymasterApiKey, }; const datum = { @@ -133,10 +135,16 @@ beforeAll(async () => { }); const envVarCheck = () => { - const REQUIRED_FIELDS = ["E2E_PRIVATE_KEY_ONE", "E2E_PRIVATE_KEY_TWO", "E2E_BICO_PAYMASTER_KEY_MUMBAI", "E2E_BICO_PAYMASTER_KEY_BASE"]; + const REQUIRED_FIELDS = [ + "E2E_PRIVATE_KEY_ONE", + "E2E_PRIVATE_KEY_TWO", + "E2E_BICO_PAYMASTER_KEY_MUMBAI", + "E2E_BICO_PAYMASTER_KEY_BASE", + "E2E_BICO_PAYMASTER_KEY_OP", + ]; const hasFields = REQUIRED_FIELDS.every((field) => !!process.env[field]); if (!hasFields) { console.error("Missing env var"); - process.exit(0); + process.exit(1); } }; diff --git a/tests/setup-unit-tests.ts b/tests/setup-unit-tests.ts index 03360b043..d2fdba7d3 100644 --- a/tests/setup-unit-tests.ts +++ b/tests/setup-unit-tests.ts @@ -6,7 +6,7 @@ import { UNIT_TEST_CHAIN } from "./chains.config"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; beforeAll(() => { - const { chainId, bundlerUrl, viemChain, entryPointAddress } = UNIT_TEST_CHAIN; + const { chainId, bundlerUrl, viemChain, entryPointAddress, deploymentCost, nftAddress } = UNIT_TEST_CHAIN; const privateKeyOne = generatePrivateKey(); const accountOne = privateKeyToAccount(privateKeyOne); @@ -60,16 +60,5 @@ beforeAll(() => { }; // @ts-ignore - testDataPerChain = [ - { - whale, - minnow, - publicClient, - chainId, - bundlerUrl, - entryPointAddress, - viemChain, - ethersProvider, - }, - ]; + testDataPerChain = [{ nftAddress, deploymentCost, whale, minnow, publicClient, chainId, bundlerUrl, entryPointAddress, viemChain, ethersProvider }]; }); diff --git a/tests/utils.ts b/tests/utils.ts index c010435f6..b7fd5ea13 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -14,6 +14,8 @@ export const checkBalance = (publicClient: PublicClient, address: Hex, tokenAddr } }; +export const getMockBundlerUrl = (chainId: number) => `https://bundler.biconomy.io/api/v2/${chainId}/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f14`; + // TODO(Joe): Make human readable export const entryPointABI = [ { diff --git a/yarn.lock b/yarn.lock index 952ef86ce..819d36e70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13,9 +13,9 @@ integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== "@alchemy/aa-core@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@alchemy/aa-core/-/aa-core-3.1.1.tgz#5b93078b13168b3874a6596cfff9150834926356" - integrity sha512-ze607aFT5pReC0al7WV6MTCkW5kTO1GkDnm80BVU8rL4FZ7CW355tDxvgufnss05BwYUoXrlXfTseqMoAVKjVA== + version "3.4.0" + resolved "https://registry.yarnpkg.com/@alchemy/aa-core/-/aa-core-3.4.0.tgz#08e514bf2f97a9c1452424a7f7915aa48daf4db2" + integrity sha512-kEqsMwweMxQU6b8mKNmkUacyZRxdU7WBBiAYNmGxJK5djOdsGlfCBZGHu2AyAb8ogfrGWX8J+uVuYTnCIZS6ew== dependencies: abitype "^0.8.3" eventemitter3 "^5.0.1" @@ -23,12 +23,12 @@ zod "^3.22.4" "@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5": version "7.23.5" @@ -44,20 +44,20 @@ integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b" + integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" + "@babel/helpers" "^7.24.0" + "@babel/parser" "^7.24.0" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.0" + "@babel/types" "^7.24.0" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -124,9 +124,9 @@ "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" + integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -157,14 +157,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helpers@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" - integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== +"@babel/helpers@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b" + integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA== dependencies: - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.0" + "@babel/types" "^7.24.0" "@babel/highlight@^7.23.4": version "7.23.4" @@ -175,10 +175,10 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" - integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac" + integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -279,25 +279,25 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/runtime@^7.21.0": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" - integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e" + integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" - integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== +"@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== dependencies: "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" -"@babel/traverse@^7.23.9": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" - integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== +"@babel/traverse@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e" + integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw== dependencies: "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" @@ -305,15 +305,15 @@ "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" - integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.24.0", "@babel/types@^7.3.3": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== dependencies: "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" @@ -812,9 +812,9 @@ "@ethersproject/strings" "^5.7.0" "@fastify/busboy@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" - integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -1065,24 +1065,24 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: - "@jridgewell/set-array" "^1.0.1" + "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" @@ -1097,10 +1097,10 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -1251,33 +1251,65 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomicfoundation/ethereumjs-block@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.4.tgz#ff2acb98a86b9290e35e315a6abfb9aebb9cf39e" - integrity sha512-AcyacJ9eX/uPEvqsPiB+WO1ymE+kyH48qGGiGV+YTojdtas8itUTW5dehDSOXEEItWGbbzEJ4PRqnQZlWaPvDw== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-trie" "6.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - ethereum-cryptography "0.1.3" +"@nomicfoundation/edr-darwin-arm64@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.2.0.tgz#b208fe65f90f8113ad634482f7382f73e2189858" + integrity sha512-OfXruInMbc6+J6BnAlYlpTS8lj5hHmfLdzqthhiQaayuHxT6iBMrefe6N+2DC9hBxD3VjCApUWtLfV3pJzpbCg== -"@nomicfoundation/ethereumjs-blockchain@7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.4.tgz#b77511b389290b186c8d999e70f4b15c27ef44ea" - integrity sha512-jYsd/kwzbmpnxx86tXsYV8wZ5xGvFL+7/P0c6OlzpClHsbFzeF41KrYA9scON8Rg6bZu3ZTv6JOAgj3t7USUfg== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.4" - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-ethash" "3.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-trie" "6.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - lru-cache "^10.0.0" +"@nomicfoundation/edr-darwin-x64@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.2.0.tgz#b7b0a285a7a004da7908d475fefb086eac1ae61f" + integrity sha512-tfNhHYSgro3nOTGCQzBvFniUy0cvUBtPCSeniNleu5M4nolArnxlZfEkNdpYRB92QRjfaREZttuBP1nrIO/b+w== + +"@nomicfoundation/edr-linux-arm64-gnu@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.2.0.tgz#dfb9b8bf3718143b31e24b00e4f18f1aca0e1905" + integrity sha512-Km4rZIsARkiIR7HfpU6ybCkAHpD+Gg68x+5+dhQsv+eT3XvQ9pRv3jz14v3aimOjwpCd5/uUw9LhQrPtFyMGGA== + +"@nomicfoundation/edr-linux-arm64-musl@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.2.0.tgz#ac53bfaa4828922f300025955b51c92c98e40f12" + integrity sha512-pD4g2r5Q54b3AzEaI0okDktFrYjhcdCxO3lvP1pYGCvha8KYrUv9DM3Z/0kfnn3vP9y/PxzcJUBfXjG4NZuHpw== + +"@nomicfoundation/edr-linux-x64-gnu@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.2.0.tgz#dbab76c3dc53c4dcea502df302b8c7ee1ccf3d89" + integrity sha512-xjw8yNiEED0jlM5HuWXF/61+4bBkEpSZpMmb39XChPJXVxtZIIBzj0AcGTdzkSyH/atgkEaNutkEb1PeEuFwnQ== + +"@nomicfoundation/edr-linux-x64-musl@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.2.0.tgz#31989fd1e9e548897d7843987e2bdef35d0c877c" + integrity sha512-aqqR0usfHt6V2j+7pQiMqIrIBpUwDeU27w27kuvZsHDUhrvg4sgGm3FBG1QUxN8tv9E/UrbUuW0kVt7tbEmKMA== + +"@nomicfoundation/edr-win32-arm64-msvc@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-arm64-msvc/-/edr-win32-arm64-msvc-0.2.0.tgz#6d028107064adc032af912268b9b55eb9dba7fd5" + integrity sha512-+S4Qnx5CVdUAxGUXa3rNq0h/qALIHkGdlKLT5KDsk/qGTmI/uuAB4tnoOaaHMc5ANckPtBdWfSwnLJjWPZbR6w== + +"@nomicfoundation/edr-win32-ia32-msvc@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-ia32-msvc/-/edr-win32-ia32-msvc-0.2.0.tgz#570a504df4cb30197302435b89dc9a92b7dbf9fb" + integrity sha512-hK0RVcNog8sJ63QmeEJ+WIhnCLfUCl5jXYCBjQtGOWlIkC7EzNddkZ28MmrFOMrV3xstSGOmdPvvq8q1HNVakA== + +"@nomicfoundation/edr-win32-x64-msvc@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.2.0.tgz#90de20c19c4c20c7d69563b176d92425a8e8bb5a" + integrity sha512-gWgMU4I94fHIeda3xOnHBYcCOzRF6ySB89vgENK4Y1S1Un/qpZ+tQwf+/hX0HCaZGMw/LqBG61ltOYUXVfZ6Yg== + +"@nomicfoundation/edr@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.2.0.tgz#dfdecce6382f9faf640d937357418825a10c5483" + integrity sha512-RRWJepP4ozI4jVxqNtuw53ZbPcUB4FcKry2aYVQw8KAp9o8j/I5H3SsfpmKT+lgHRSL/5/KK0RxOx1GQSyDAZw== + optionalDependencies: + "@nomicfoundation/edr-darwin-arm64" "0.2.0" + "@nomicfoundation/edr-darwin-x64" "0.2.0" + "@nomicfoundation/edr-linux-arm64-gnu" "0.2.0" + "@nomicfoundation/edr-linux-arm64-musl" "0.2.0" + "@nomicfoundation/edr-linux-x64-gnu" "0.2.0" + "@nomicfoundation/edr-linux-x64-musl" "0.2.0" + "@nomicfoundation/edr-win32-arm64-msvc" "0.2.0" + "@nomicfoundation/edr-win32-ia32-msvc" "0.2.0" + "@nomicfoundation/edr-win32-x64-msvc" "0.2.0" "@nomicfoundation/ethereumjs-common@4.0.4": version "4.0.4" @@ -1286,62 +1318,11 @@ dependencies: "@nomicfoundation/ethereumjs-util" "9.0.4" -"@nomicfoundation/ethereumjs-ethash@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.4.tgz#06cb2502b3012fb6c11cffd44af08aecf71310da" - integrity sha512-xvIrwIMl9sSaiYKRem68+O7vYdj7Q2XWv5P7JXiIkn83918QzWHvqbswTRsH7+r6X1UEvdsURRnZbvZszEjAaQ== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - bigint-crypto-utils "^3.2.2" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-evm@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.4.tgz#c9c761767283ac53946185474362230b169f8f63" - integrity sha512-lTyZZi1KpeMHzaO6cSVisR2tjiTTedjo7PcmhI/+GNFo9BmyY6QYzGeSti0sFttmjbEMioHgXxl5yrLNRg6+1w== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-statemanager" "2.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - "@types/debug" "^4.1.9" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - rustbn-wasm "^0.2.0" - "@nomicfoundation/ethereumjs-rlp@5.0.4": version "5.0.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== -"@nomicfoundation/ethereumjs-statemanager@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.4.tgz#bf14415e1f31b5ea8b98a0c027c547d0555059b6" - integrity sha512-HPDjeFrxw6llEi+BzqXkZ+KkvFnTOPczuHBtk21hRlDiuKuZz32dPzlhpRsDBGV1b5JTmRDUVqCS1lp3Gghw4Q== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-trie" "6.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - js-sdsl "^4.1.4" - lru-cache "^10.0.0" - -"@nomicfoundation/ethereumjs-trie@6.0.4": - version "6.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.4.tgz#688a3f76646c209365ee6d959c3d7330ede5e609" - integrity sha512-3nSwQiFMvr2VFe/aZUyinuohYvtytUqZCUCvIWcPJ/BwJH6oQdZRB42aNFBJ/8nAh2s3OcroWpBLskzW01mFKA== - dependencies: - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - "@types/readable-stream" "^2.3.13" - ethereum-cryptography "0.1.3" - lru-cache "^10.0.0" - readable-stream "^3.6.0" - "@nomicfoundation/ethereumjs-tx@5.0.4": version "5.0.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" @@ -1360,33 +1341,6 @@ "@nomicfoundation/ethereumjs-rlp" "5.0.4" ethereum-cryptography "0.1.3" -"@nomicfoundation/ethereumjs-verkle@0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-verkle/-/ethereumjs-verkle-0.0.2.tgz#7686689edec775b2efea5a71548f417c18f7dea4" - integrity sha512-bjnfZElpYGK/XuuVRmLS3yDvr+cDs85D9oonZ0YUa5A3lgFgokWMp76zXrxX2jVQ0BfHaw12y860n1+iOi6yFQ== - dependencies: - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - lru-cache "^10.0.0" - rust-verkle-wasm "^0.0.1" - -"@nomicfoundation/ethereumjs-vm@7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.4.tgz#e5a6eec4877dc62dda93003c6d7afd1fe4b9625b" - integrity sha512-gsA4IhmtWHI4BofKy3kio9W+dqZQs5Ji5mLjLYxHCkat+JQBUt5szjRKra2F9nGDJ2XcI/wWb0YWUFNgln4zRQ== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.4" - "@nomicfoundation/ethereumjs-blockchain" "7.0.4" - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-evm" "2.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-statemanager" "2.0.4" - "@nomicfoundation/ethereumjs-trie" "6.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - "@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" @@ -1767,9 +1721,9 @@ uuid "^8.3.2" "@particle-network/chains@*": - version "1.3.19" - resolved "https://registry.yarnpkg.com/@particle-network/chains/-/chains-1.3.19.tgz#63013cbbebf36c37e970f83406ef955f08309499" - integrity sha512-cQOGS0dH6LMDyJ7bkaLj6FShYSyz8OZ2hBvtBPdTtuMCsTlQPFcXKR6QI2CpJZBbb6m1fIXZEXlFYFZzY/t6ng== + version "1.3.21" + resolved "https://registry.yarnpkg.com/@particle-network/chains/-/chains-1.3.21.tgz#82d2b098e165fc198e6e6e3e4c8b2154235e3aa1" + integrity sha512-tuUVuOPf+el+kDlHLFMyDy4IkoGjk+P3QvVrxT7WnmEma1NgWTE7RaNsniwqn6SYkAwAxksL/D9aADUXZxqPmw== "@particle-network/crypto@^1.0.1": version "1.0.1" @@ -1798,7 +1752,7 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@scure/base@^1.1.1", "@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.4": +"@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.4": version "1.1.5" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" integrity sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ== @@ -2157,9 +2111,9 @@ integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== "@types/node@*", "@types/node@^20.11.10": - version "20.11.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.20.tgz#f0a2aee575215149a62784210ad88b3a34843659" - integrity sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg== + version "20.11.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.24.tgz#cc207511104694e84e9fb17f9a0c4c42d4517792" + integrity sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long== dependencies: undici-types "~5.26.4" @@ -2175,14 +2129,6 @@ dependencies: "@types/node" "*" -"@types/readable-stream@^2.3.13": - version "2.3.15" - resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" - integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== - dependencies: - "@types/node" "*" - safe-buffer "~5.1.1" - "@types/secp256k1@^4.0.1": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" @@ -2196,9 +2142,9 @@ integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== "@types/semver@^7.5.0": - version "7.5.7" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.7.tgz#326f5fdda70d13580777bcaa1bc6fa772a5aef0e" - integrity sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg== + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/stack-utils@^2.0.0": version "2.0.3" @@ -2803,11 +2749,6 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== -bigint-crypto-utils@^3.2.2: - version "3.3.0" - resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77" - integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg== - bignumber.js@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" @@ -3119,9 +3060,9 @@ camelcase@^6.0.0, camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001587: - version "1.0.30001589" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz#7ad6dba4c9bf6561aec8291976402339dc157dfb" - integrity sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg== + version "1.0.30001594" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz#bea552414cd52c2d0c985ed9206314a696e685f5" + integrity sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g== catering@^2.0.0, catering@^2.1.0: version "2.1.1" @@ -3824,11 +3765,11 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.668: - version "1.4.681" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.681.tgz#5f23fad8aa7e1f64cbb7dd9d15c7e39a1cd7e6e3" - integrity sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg== + version "1.4.692" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz#82139d20585a4b2318a02066af7593a3e6bec993" + integrity sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA== -elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: +elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -3841,6 +3782,19 @@ elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.2, elliptic@^6.5.4: + version "6.5.5" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" + integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + email-addresses@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-5.0.0.tgz#7ae9e7f58eef7d5e3e2c2c2d3ea49b78dc854fa6" @@ -3923,17 +3877,17 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.22.1, es-abstract@^1.22.3: - version "1.22.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.4.tgz#26eb2e7538c3271141f5754d31aabfdb215f27bf" - integrity sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg== + version "1.22.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.5.tgz#1417df4e97cc55f09bf7e58d1e614bc61cb8df46" + integrity sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w== dependencies: array-buffer-byte-length "^1.0.1" arraybuffer.prototype.slice "^1.0.3" - available-typed-arrays "^1.0.6" + available-typed-arrays "^1.0.7" call-bind "^1.0.7" es-define-property "^1.0.0" es-errors "^1.3.0" - es-set-tostringtag "^2.0.2" + es-set-tostringtag "^2.0.3" es-to-primitive "^1.2.1" function.prototype.name "^1.1.6" get-intrinsic "^1.2.4" @@ -3941,15 +3895,15 @@ es-abstract@^1.22.1, es-abstract@^1.22.3: globalthis "^1.0.3" gopd "^1.0.1" has-property-descriptors "^1.0.2" - has-proto "^1.0.1" + has-proto "^1.0.3" has-symbols "^1.0.3" hasown "^2.0.1" internal-slot "^1.0.7" is-array-buffer "^3.0.4" is-callable "^1.2.7" - is-negative-zero "^2.0.2" + is-negative-zero "^2.0.3" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" + is-shared-array-buffer "^1.0.3" is-string "^1.0.7" is-typed-array "^1.1.13" is-weakref "^1.0.2" @@ -3962,10 +3916,10 @@ es-abstract@^1.22.1, es-abstract@^1.22.3: string.prototype.trim "^1.2.8" string.prototype.trimend "^1.0.7" string.prototype.trimstart "^1.0.7" - typed-array-buffer "^1.0.1" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.5" unbox-primitive "^1.0.2" which-typed-array "^1.1.14" @@ -3986,7 +3940,7 @@ es-errors@^1.0.0, es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-set-tostringtag@^2.0.2: +es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== @@ -4099,9 +4053,9 @@ eslint-import-resolver-node@^0.3.9: resolve "^1.22.4" eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + version "2.8.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== dependencies: debug "^3.2.7" @@ -5045,23 +4999,16 @@ hard-rejection@^2.1.0: integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== hardhat@^2.17.3: - version "2.20.1" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.20.1.tgz#3ad8f2b003a96c9ce80a55fec3575580ff2ddcd4" - integrity sha512-q75xDQiQtCZcTMBwjTovrXEU5ECr49baxr4/OBkIu/ULTPzlB20yk1dRWNmD2IFbAeAeXggaWvQAdpiScaHtPw== + version "2.21.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.21.0.tgz#2e23126310a6c77cd7e149e6af1dd67626b7a74f" + integrity sha512-8DlJAVJDEVHaV1sh9FLuKLLgCFv9EAJ+M+8IbjSIPgoeNo3ss5L1HgGBMfnI88c7OzMEZkdcuyGoobFeK3Orqw== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/ethereumjs-block" "5.0.4" - "@nomicfoundation/ethereumjs-blockchain" "7.0.4" + "@nomicfoundation/edr" "^0.2.0" "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-evm" "2.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-statemanager" "2.0.4" - "@nomicfoundation/ethereumjs-trie" "6.0.4" "@nomicfoundation/ethereumjs-tx" "5.0.4" "@nomicfoundation/ethereumjs-util" "9.0.4" - "@nomicfoundation/ethereumjs-verkle" "0.0.2" - "@nomicfoundation/ethereumjs-vm" "7.0.4" "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" "@types/bn.js" "^5.1.0" @@ -5542,7 +5489,7 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== -is-negative-zero@^2.0.2: +is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== @@ -5599,7 +5546,7 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-shared-array-buffer@^1.0.2: +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== @@ -6130,11 +6077,6 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -js-sdsl@^4.1.4: - version "4.4.2" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847" - integrity sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w== - js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -6563,7 +6505,7 @@ logform@^2.3.2, logform@^2.4.0: safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" -lru-cache@^10.0.0, lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0": +lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0": version "10.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== @@ -7082,9 +7024,9 @@ neo-async@^2.6.2: integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nock@^13.2.9: - version "13.5.3" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.3.tgz#9858adf5b840696a410baf98bda720d5fad4f075" - integrity sha512-2NlGmHIK2rTeyy7UaY1ZNg0YZfEJMxghXgZi0b4DBsUyoDNTTxZeCSG1nmirAWF44RkkoV8NnegLVQijgVapNQ== + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" @@ -8235,18 +8177,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rust-verkle-wasm@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/rust-verkle-wasm/-/rust-verkle-wasm-0.0.1.tgz#fd8396a7060d8ee8ea10da50ab6e862948095a74" - integrity sha512-BN6fiTsxcd2dCECz/cHtGTt9cdLJR925nh7iAuRcj8ymKw7OOaPmCneQZ7JePOJ/ia27TjEL91VdOi88Yf+mcA== - -rustbn-wasm@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/rustbn-wasm/-/rustbn-wasm-0.2.0.tgz#0407521fb55ae69eeb4968d01885d63efd1c4ff9" - integrity sha512-FThvYFNTqrEKGqXuseeg0zR7yROh/6U1617mCHF68OVqrN1tNKRN7Tdwy4WayPVsCmmK+eMxtIZX1qL6JxTkMg== - dependencies: - "@scure/base" "^1.1.1" - rxjs@^7.5.5, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" @@ -8432,11 +8362,11 @@ shiki@^0.14.7: vscode-textmate "^8.0.0" side-channel@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" - integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.6" + call-bind "^1.0.7" es-errors "^1.3.0" get-intrinsic "^1.2.4" object-inspect "^1.13.1" @@ -8926,13 +8856,18 @@ tmp@0.0.33, tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@0.2.1, tmp@~0.2.1: +tmp@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: rimraf "^3.0.0" +tmp@~0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -9125,7 +9060,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typed-array-buffer@^1.0.1: +typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== @@ -9134,7 +9069,7 @@ typed-array-buffer@^1.0.1: es-errors "^1.3.0" is-typed-array "^1.1.13" -typed-array-byte-length@^1.0.0: +typed-array-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== @@ -9145,7 +9080,7 @@ typed-array-byte-length@^1.0.0: has-proto "^1.0.3" is-typed-array "^1.1.13" -typed-array-byte-offset@^1.0.0: +typed-array-byte-offset@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== @@ -9157,7 +9092,7 @@ typed-array-byte-offset@^1.0.0: has-proto "^1.0.3" is-typed-array "^1.1.13" -typed-array-length@^1.0.4: +typed-array-length@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.5.tgz#57d44da160296d8663fd63180a1802ebf25905d5" integrity sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA== @@ -9175,9 +9110,9 @@ typedarray@^0.0.6: integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typedoc@^0.25.7: - version "0.25.8" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.8.tgz#7d0e1bf12d23bf1c459fd4893c82cb855911ff12" - integrity sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A== + version "0.25.10" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.10.tgz#572f566498e4752fdbc793ccc14b8eb517944770" + integrity sha512-v10rtOFojrjW9og3T+6wAKeJaGMuojU87DXGZ33sfs+554wgPTRG+s07Ag1BjPZI85Y5QPVouPI63JQ6fcQM5w== dependencies: lunr "^2.3.9" marked "^4.3.0" @@ -9389,9 +9324,9 @@ validate-npm-package-name@^3.0.0: builtins "^1.0.3" viem@^2.7.12, viem@^2.7.8: - version "2.7.13" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.13.tgz#bf3f8e973f532f9f9c86c97b645ac8add740e1bd" - integrity sha512-NGWLEocRp2UTqGidzI9bnL9u6WHlG2ik7IwqXNe6/QC2dL6jE3Z1mUnUUVcSx71h81nx74EflD9ahtleK3RQdA== + version "2.7.19" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.19.tgz#fa6bd8f46df2f0332e5ca6d116772dff6f161a72" + integrity sha512-UOMeqy+8p2709ra2j9HEOL1NfjsXZzlJ8gwR6YO/zXH8KIZvyzW07t4iQARF5+ShVZ/7+/1ec8oPjVi1M//33A== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.2.0"