diff --git a/.github/workflows/ci_4337_gas_metering.yml b/.github/workflows/ci_4337_gas_metering.yml new file mode 100644 index 00000000..5b2da0a1 --- /dev/null +++ b/.github/workflows/ci_4337_gas_metering.yml @@ -0,0 +1,20 @@ +name: safe-modules-4337-gas-metering +on: [push] + +jobs: + checks: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./4337-gas-metering + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 20.x + cache: npm + cache-dependency-path: 4337-gas-metering/package-lock.json + - run: npm ci + - run: npm run fmt:check + - run: npm run lint + - run: npm run build diff --git a/4337-gas-metering/.env.example b/4337-gas-metering/.env.example new file mode 100644 index 00000000..5d4a35fc --- /dev/null +++ b/4337-gas-metering/.env.example @@ -0,0 +1,57 @@ +# Dummy ETH Address Private Key +PRIVATE_KEY = "0x..." # Add "0x" to Private Key if not already added. + +# Pimlico Values +PIMLICO_CHAIN = "mumbai" # or "goerli" +PIMLICO_CHAIN_ID = "80001" +PIMLICO_RPC_URL = "https://rpc.ankr.com/polygon_mumbai" +PIMLICO_API_KEY = "" # https://dashboard.pimlico.io/apikeys +PIMLICO_GAS_POLICY = "" # https://dashboard.pimlico.io/sponsorship-policies +PIMLICO_ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" +PIMLICO_MULTISEND_ADDRESS = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526" # https://github.com/safe-global/safe-deployments/blob/main/src/assets/v1.4.1/multi_send.json +PIMLICO_ERC20_PAYMASTER_ADDRESS = "0x32aCDFeA07a614E52403d2c1feB747aa8079A353" # https://docs.pimlico.io/erc20-paymaster/reference/contracts +PIMLICO_USDC_TOKEN_ADDRESS = "0x0FA8781a83E46826621b3BC094Ea2A0212e71B23" # Mumbai USDC Address used by Pimlico +# Make sure all nonce are unique for it to deploy account when run initially. +PIMLICO_NONCE = "1" +# Pimlico Token Operation Values (Based on the chain selected, these values should also change accordingly.) +PIMLICO_ERC20_TOKEN_CONTRACT = "0x255de08fb52fde17a3aab145de8e2ffb7fd0310f" +PIMLICO_ERC721_TOKEN_CONTRACT = "0x16bc5fba06f3f5875e915c0ba6963377eb6651e1" + +# Alchemy Values +ALCHEMY_CHAIN = "sepolia" # or "goerli" +ALCHEMY_CHAIN_ID = "11155111" +ALCHEMY_RPC_URL = "https://eth-sepolia.g.alchemy.com/v2/Your_API_Key" +ALCHEMY_API_KEY = "" # https://dashboard.alchemy.com/apps +ALCHEMY_GAS_POLICY = "" # https://dashboard.alchemy.com/gas-manager +ALCHEMY_ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" +ALCHEMY_MULTISEND_ADDRESS = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526" # https://github.com/safe-global/safe-deployments/blob/main/src/assets/v1.4.1/multi_send.json +# Make sure all nonce are unique for it to deploy account when run initially. +ALCHEMY_NONCE = "2" +# Alchemy Token Operation Values (Based on the chain selected, these values should also change accordingly.) +ALCHEMY_ERC20_TOKEN_CONTRACT = "0x255de08fb52fde17a3aab145de8e2ffb7fd0310f" +ALCHEMY_ERC721_TOKEN_CONTRACT = "0x16bc5fba06f3f5875e915c0ba6963377eb6651e1" + +# Gelato Values +GELATO_CHAIN = "sepolia" +GELATO_CHAIN_ID = "11155111" +GELATO_RPC_URL = "https://rpc.ankr.com/eth_sepolia" +GELATO_API_KEY = "" # Sponsor API Key +GELATO_GAS_POLICY = "" +GELATO_ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" +GELATO_MULTISEND_ADDRESS = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526" # https://github.com/safe-global/safe-deployments/blob/main/src/assets/v1.4.1/multi_send.json +# Make sure all nonce are unique for it to deploy account when run initially. +GELATO_NONCE = "3" +# Gelato Token Operation Values (Based on the chain selected, these values should also change accordingly.) +GELATO_ERC20_TOKEN_CONTRACT = "0x255de08fb52fde17a3aab145de8e2ffb7fd0310f" +GELATO_ERC721_TOKEN_CONTRACT = "0x16bc5fba06f3f5875e915c0ba6963377eb6651e1" + +# Safe Values +SAFE_VERSION = "1.4.1" + +# Publicly Mintable ERC20 and ERC721 Token for easier testing. Can be used for Token Operation Values. +# ERC20 Token Goerli: https://goerli.etherscan.io/token/0x3Aa475E24F7c076632467444b593E332Bc3355F9 +# ERC721 Token Goerli: https://goerli.etherscan.io/token/0xf190c05f968c53962604454ebfa380e5cda600d7 +# ERC20 Token Sepolia: https://sepolia.etherscan.io/token/0x255de08fb52fde17a3aab145de8e2ffb7fd0310f +# ERC721 Token Sepolia: https://sepolia.etherscan.io/token/0x16bc5fba06f3f5875e915c0ba6963377eb6651e1 +# ERC20 Token Mumbai: https://mumbai.polygonscan.com/address/0x255de08fb52fde17a3aab145de8e2ffb7fd0310f +# ERC721 Token Mumbai: https://mumbai.polygonscan.com/address/0x16bc5fba06f3f5875e915c0ba6963377eb6651e1 diff --git a/4337-gas-metering/.eslintrc.cjs b/4337-gas-metering/.eslintrc.cjs new file mode 100644 index 00000000..9ced7b62 --- /dev/null +++ b/4337-gas-metering/.eslintrc.cjs @@ -0,0 +1,30 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + overrides: [ + { + env: { + node: true, + }, + files: [".eslintrc.{js,cjs}"], + parserOptions: { + sourceType: "script", + }, + }, + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["@typescript-eslint"], + rules: { + "@typescript-eslint/no-explicit-any": "warn", + }, +}; diff --git a/4337-gas-metering/.gitignore b/4337-gas-metering/.gitignore new file mode 100644 index 00000000..96c1663d --- /dev/null +++ b/4337-gas-metering/.gitignore @@ -0,0 +1,16 @@ +node_modules +.env + +# Hardhat files +/cache +/artifacts + +# TypeChain files +/typechain +/typechain-types + +# solidity-coverage files +/coverage +/coverage.json + +/dist \ No newline at end of file diff --git a/4337-gas-metering/README.md b/4337-gas-metering/README.md new file mode 100644 index 00000000..a59c6099 --- /dev/null +++ b/4337-gas-metering/README.md @@ -0,0 +1,82 @@ +# 4337 Gas Metering Analysis + +## How to run? + +1. Rename the `.env.example` to `.env`. +2. Fill the required values of `.env`. +3. Based on which paymaster to run, check the `package.json` file to see the `script`. Furthermore, you can check the `README.md` files in the corresponding paymaster folders to see the individual command and their possible results. + +NOTE: If you run a paymaster analysis twice or more without changing the salt for Safe Creation, then only the operation will execute through paymaster (if any), rather than Safe Creation and Operation. + +## Gas Usage Results + +| | **With 4337?** | **Account Creation** | **Account Creation + Native Transfer** | **Native Transfer** | **Account Creation + ERC20 Transfer** | **ERC20 Transfer** | **Account Creation + ERC721 Minting** | **ERC721 Minting** | +| :---------------------------------------------------: | :------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| **[Without Paymaster](../4337/test/gas/Gas.spec.ts)** | Yes | 359756 | 364687 | 88471 | 370661 | 94445 | 412448 | 136232 | +| **Gelato (4337 Compatible - 1Balance)** | Yes | 397421 ([TX](https://sepolia.etherscan.io/tx/0x21205216b55d0f48aa09ff4289ae982c3b16e7a9905494815cabd1fb01a0d505)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x21205216b55d0f48aa09ff4289ae982c3b16e7a9905494815cabd1fb01a0d505/gas-usage)) | 404828 ([TX](https://sepolia.etherscan.io/tx/0x7bb36c93d21c911a2c1bdc7e50f55030cc7f006a1f7e2e651251dca9651383e0)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x7bb36c93d21c911a2c1bdc7e50f55030cc7f006a1f7e2e651251dca9651383e0/gas-usage)) | 114692 ([TX](https://sepolia.etherscan.io/tx/0xefa122224466e9f1d09d42683aaec2b37f9871f7f5569099f0cc066961b39f15)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xefa122224466e9f1d09d42683aaec2b37f9871f7f5569099f0cc066961b39f15/gas-usage)) | 408160 ([TX](https://sepolia.etherscan.io/tx/0x4f55488ecc542be4effc2d7a4743345db6790ef80e7ca94f3e939a290738fa2d)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x4f55488ecc542be4effc2d7a4743345db6790ef80e7ca94f3e939a290738fa2d/gas-usage)) | 118033 ([TX](https://sepolia.etherscan.io/tx/0x152c78871b6940215ba37cac5f5231fa2bd4bcf40742ebcf741903ce64adc405)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x152c78871b6940215ba37cac5f5231fa2bd4bcf40742ebcf741903ce64adc405/gas-usage)) | 437372 ([TX](https://sepolia.etherscan.io/tx/0x4aa37845d5c9fc0ad0713caefbbc9931263040d1502f076a98c993282257e51d)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x4aa37845d5c9fc0ad0713caefbbc9931263040d1502f076a98c993282257e51d/gas-usage)) | 147232 ([TX](https://sepolia.etherscan.io/tx/0xfac73b16d0932ba97a93f12ddc230024b102e581a37a53625dfe8108ca581bb5)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xfac73b16d0932ba97a93f12ddc230024b102e581a37a53625dfe8108ca581bb5/gas-usage)) | +| **Pimlico (USDC Paymaster)** | Yes | 506573 ([TX](https://mumbai.polygonscan.com/tx/0x3c6284e4df1686d699d2bc4cca04a25ecc76d68a73665ca53d466e6bd6bedf28)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0x3c6284e4df1686d699d2bc4cca04a25ecc76d68a73665ca53d466e6bd6bedf28/gas-usage)) | 511055 ([TX](https://mumbai.polygonscan.com/tx/0x8bc4e42b076d22e0fc3418eba40c65caab6e3a10c1fbb10cbeee4a7fbfa8b4b3)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0x8bc4e42b076d22e0fc3418eba40c65caab6e3a10c1fbb10cbeee4a7fbfa8b4b3/gas-usage)) | 199262 ([TX](https://mumbai.polygonscan.com/tx/0x46cdfc14649087609f69411fc41f5feb4dc23a6ea9255928b932841858e5f186)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0x46cdfc14649087609f69411fc41f5feb4dc23a6ea9255928b932841858e5f186/gas-usage)) | 514156 ([TX](https://mumbai.polygonscan.com/tx/0xa5cf461800341c2e9934608ff55aeda26d1a3e7da4f5bc9f3cce3fd185409623)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xa5cf461800341c2e9934608ff55aeda26d1a3e7da4f5bc9f3cce3fd185409623/gas-usage)) | 202387 ([TX](https://mumbai.polygonscan.com/tx/0xdc21ae13dc92eb48851fa62f57c74f3a0085acf81343d9aaaa14fcc3c6911f91)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xdc21ae13dc92eb48851fa62f57c74f3a0085acf81343d9aaaa14fcc3c6911f91/gas-usage)) | 543411 ([TX](https://mumbai.polygonscan.com/tx/0xcd6c137474be4f002822498e032ad9b78b0505bd4db495ee65fc602ec1a7f006)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xcd6c137474be4f002822498e032ad9b78b0505bd4db495ee65fc602ec1a7f006/gas-usage)) | 231619 ([TX](https://mumbai.polygonscan.com/tx/0x31732175d3f3b35c9c2a38e841bcd485085edf79e7f3c532ec7997c4993c0192)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0x31732175d3f3b35c9c2a38e841bcd485085edf79e7f3c532ec7997c4993c0192/gas-usage)) | +| **Pimlico (MATIC - Gas Policy)** | Yes | 448172 ([TX](https://mumbai.polygonscan.com/tx/0xd51d026ecfa6dbafa8aac8a138badc6e3b397683117878e360bae9051a3b733a)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xd51d026ecfa6dbafa8aac8a138badc6e3b397683117878e360bae9051a3b733a/gas-usage)) | 455615 ([TX](https://mumbai.polygonscan.com/tx/0xdd966b95b6625be33ae37f6c5bb1ad33798afbbd899089acad1180005d0637c4)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xdd966b95b6625be33ae37f6c5bb1ad33798afbbd899089acad1180005d0637c4/gas-usage)) | 123064 ([TX](https://mumbai.polygonscan.com/tx/0xca2e41e24c6206011fe0d932f27a2786c7d9486c93f63d96c131c5007e2b275e)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xca2e41e24c6206011fe0d932f27a2786c7d9486c93f63d96c131c5007e2b275e/gas-usage)) | 459014 ([TX](https://mumbai.polygonscan.com/tx/0xbd4c79d876ae928bbc721501029b01dbc5fc94d91d6299f548f19289f7c1c271)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xbd4c79d876ae928bbc721501029b01dbc5fc94d91d6299f548f19289f7c1c271/gas-usage)) | 126461 ([TX](https://mumbai.polygonscan.com/tx/0xd2b130bc2f26cfe43041f7102601425674e2cd22a6b74672b907b28e70686496)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xd2b130bc2f26cfe43041f7102601425674e2cd22a6b74672b907b28e70686496/gas-usage)) | 488186 ([TX](https://mumbai.polygonscan.com/tx/0x454a3a5a39432f7b01a70fcddfef948d20c70d2d719aea30d402d693447fa535)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0x454a3a5a39432f7b01a70fcddfef948d20c70d2d719aea30d402d693447fa535/gas-usage)) | 155645 ([TX](https://mumbai.polygonscan.com/tx/0xa148a4938de9883b2fbcd512e3c7161e78ca695843b6e535fdb5054b88872652)) ([Gas](https://dashboard.tenderly.co/tx/mumbai/0xa148a4938de9883b2fbcd512e3c7161e78ca695843b6e535fdb5054b88872652/gas-usage)) | +| **Alchemy (ETH from Safe)** | Yes | 417074 ([TX](https://sepolia.etherscan.io/tx/0x03c507f5dc14c6b6af04c5ad722f0650d86925837d9889e4972cb087e34d7b88)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x03c507f5dc14c6b6af04c5ad722f0650d86925837d9889e4972cb087e34d7b88/gas-usage)) | 424505 ([TX](https://sepolia.etherscan.io/tx/0x0263331d8d4568c08d4a700385c08062ee0342fe6f65b2c7eb1a194ddec23ec2)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x0263331d8d4568c08d4a700385c08062ee0342fe6f65b2c7eb1a194ddec23ec2/gas-usage)) | 107057 ([TX](https://sepolia.etherscan.io/tx/0xf4e38d9f3535dcb9519ca3527734a5ea611a0d1bafb632051736537853eb502b)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xf4e38d9f3535dcb9519ca3527734a5ea611a0d1bafb632051736537853eb502b/gas-usage)) | 427599 ([TX](https://sepolia.etherscan.io/tx/0x794b02531f14b6c432c0dcf08d1cb76a8693dd75b35c5dde0d4547754d208143)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x794b02531f14b6c432c0dcf08d1cb76a8693dd75b35c5dde0d4547754d208143/gas-usage)) | 110174 ([TX](https://sepolia.etherscan.io/tx/0xb56985ee07b1e7931aedc387698620d890c99992c4c688b8b3a150f355089e5d)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xb56985ee07b1e7931aedc387698620d890c99992c4c688b8b3a150f355089e5d/gas-usage)) | 456870 ([TX](https://sepolia.etherscan.io/tx/0x2d2a0c8215821f0aa9cf8f88175aa8256cdca1a2928f2aa667916e5127f5dcb6)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x2d2a0c8215821f0aa9cf8f88175aa8256cdca1a2928f2aa667916e5127f5dcb6/gas-usage)) | 139420 ([TX](https://sepolia.etherscan.io/tx/0x178d2c16a261dcb49e810bf39ce35cf96cbab8c7d3235709c7164ba6193c716e)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x178d2c16a261dcb49e810bf39ce35cf96cbab8c7d3235709c7164ba6193c716e/gas-usage)) | +| **Alchemy (ETH - Gas Policy)** | Yes | 411372 ([TX](https://sepolia.etherscan.io/tx/0xcbb2c3c49b9d72d9ecf692308d69a8ad797ab5b1c6603f4fad989f966d692af1)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xcbb2c3c49b9d72d9ecf692308d69a8ad797ab5b1c6603f4fad989f966d692af1/gas-usage)) | 418779 ([TX](https://sepolia.etherscan.io/tx/0x49fbedf833cfecf9db7de56c61d4227292723115520600dbc3711da5e6a85672)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x49fbedf833cfecf9db7de56c61d4227292723115520600dbc3711da5e6a85672/gas-usage)) | 130202 ([TX](https://sepolia.etherscan.io/tx/0x35f1e5b04d988e4614a17609190b3e21b0a9892f78da9f400248cfb3b5afde9a)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x35f1e5b04d988e4614a17609190b3e21b0a9892f78da9f400248cfb3b5afde9a/gas-usage)) | 421926 ([TX](https://sepolia.etherscan.io/tx/0x7dda913ae986d49c4322f414102ae374441a40adb4b33727e568ba140904d52a)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0x7dda913ae986d49c4322f414102ae374441a40adb4b33727e568ba140904d52a/gas-usage)) | 133394 ([TX](https://sepolia.etherscan.io/tx/0xe34902ebd5377cac04c47d142f6ca2de558df63a7e0c6541f704df651b7cfcb1)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xe34902ebd5377cac04c47d142f6ca2de558df63a7e0c6541f704df651b7cfcb1/gas-usage)) | 451200 ([TX](https://sepolia.etherscan.io/tx/0xb1253508bc4ca5ce41222b15b0e7bf03b2273bcb09d93e1d6d6a5ea39b43ee84)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xb1253508bc4ca5ce41222b15b0e7bf03b2273bcb09d93e1d6d6a5ea39b43ee84/gas-usage)) | 162654 ([TX](https://sepolia.etherscan.io/tx/0xd13fb70626a26aaa02e0389cd9347c1c0d8d8ed9ee794a61c5d3eea4b36e239a)) ([Gas](https://dashboard.tenderly.co/tx/sepolia/0xd13fb70626a26aaa02e0389cd9347c1c0d8d8ed9ee794a61c5d3eea4b36e239a/gas-usage)) | + +## Detailed Individual Gas Usage Results + +### Gelato (4337 Compatible - 1Balance) + +| Type | Actual Gas | SponsoredCall | handleOps | \_createSenderIfNeeded | Safe Proxy Factory | ValidateUserOp (Safe) | \_executeUserOp | executeUserOp (Safe) | execTransactionFromModule | +| ---------------------------------- | ---------- | ------------- | --------- | ---------------------- | ------------------ | --------------------- | --------------- | -------------------- | ------------------------- | +| Account Creation | 397421 | 365997 | 351544 | 283735 | 267761 | 14931 | 14893 | 7015 | 4411 | +| Account Creation + Native Transfer | 404828 | 373368 | 358915 | 283735 | 267761 | 14931 | 22264 | 14386 | 11782 | +| Native Transfer | 114692 | 88636 | 76339 | NA | NA | 20512 | 24264 | 15795 | 13782 | +| Account Creation + ERC20 Transfer | 408160 | 380804 | 366101 | 283754 | 267761 | 14977 | 29339 | 21415 | 18550 | +| ERC20 Transfer | 118033 | 96069 | 83524 | NA | NA | 20558 | 31339 | 23415 | 20550 | +| Account Creation + ERC721 Minting | 437372 | 405404 | 390784 | 283748 | 267761 | 14962 | 54058 | 46150 | 43373 | +| ERC721 Minting | 147232 | 120668 | 108206 | NA | NA | 20542 | 56058 | 48150 | 45373 | + +### Pimlico (USDC Paymaster) + +| Type | Actual Gas | handleOps | \_createSenderIfNeeded | Safe Proxy Factory | ValidateUserOp (Safe) | \_executeUserOp | executeUserOp (Safe) | execTransactionFromModule | +| ---------------------------------- | ---------- | --------- | ---------------------- | ------------------ | --------------------- | --------------- | -------------------- | ------------------------- | +| Account Creation | 506573 | 475433 | 321191 | 307459 | 15039 | 65040 | 7015 | 4411 | +| Account Creation + Native Transfer | 511055 | 479867 | 321191 | 307459 | 15039 | 69474 | 14386 | 11782 | +| Native Transfer | 199262 | 175066 | NA | NA | 20527 | 71487 | 16386 | 13782 | +| Account Creation + ERC20 Transfer | 514156 | 487060 | 321210 | 307459 | 15085 | 76548 | 21415 | 18550 | +| ERC20 Transfer | 202387 | 182259 | NA | NA | 20573 | 78561 | 23415 | 20550 | +| Account Creation + ERC721 Minting | 543411 | 511739 | 321204 | 307459 | 15069 | 101268 | 46150 | 43373 | +| ERC721 Minting | 231619 | 206939 | NA | NA | 20558 | 103281 | 48150 | 45373 | + +### Pimlico (MATIC - Gas Policy) + +| Type | Actual Gas | handleOps | \_createSenderIfNeeded | Safe Proxy Factory | ValidateUserOp (Safe) | \_executeUserOp | executeUserOp (Safe) | execTransactionFromModule | +| ---------------------------------- | ---------- | --------- | ---------------------- | ------------------ | --------------------- | --------------- | -------------------- | ------------------------- | +| Account Creation | 448172 | 415716 | 321217 | 307459 | 15100 | 14932 | 7015 | 4411 | +| Account Creation + Native Transfer | 455615 | 423087 | 321217 | 307459 | 15100 | 22303 | 14386 | 11782 | +| Native Transfer | 123064 | 97540 | NA | NA | 20588 | 24306 | 16386 | 13782 | +| Account Creation + ERC20 Transfer | 459014 | 430566 | 321236 | 307459 | 15146 | 29378 | 21415 | 18550 | +| ERC20 Transfer | 126461 | 105017 | NA | NA | 20633 | 31382 | 23415 | 20550 | +| Account Creation + ERC721 Minting | 488186 | 455150 | 321229 | 307459 | 15130 | 54097 | 46150 | 43373 | +| ERC721 Minting | 155645 | 129601 | NA | NA | 20618 | 56100 | 48150 | 45373 | + +### Alchemy (ETH from Safe) + +| Type | Actual Gas | handleOps | \_createSenderIfNeeded | Safe Proxy Factory | ValidateUserOp (Safe) | \_executeUserOp | executeUserOp (Safe) | execTransactionFromModule | +| ---------------------------------- | ---------- | --------- | ---------------------- | ------------------ | --------------------- | --------------- | -------------------- | ------------------------- | +| Account Creation | 417074 | 407850 | 283735 | 267761 | 47159 | 34826 | 7015 | 4411 | +| Account Creation + Native Transfer | 424505 | 415221 | 283735 | 267761 | 47159 | 42197 | 14386 | 13795 | +| Native Transfer | 107057 | 83301 | NA | NA | 20512 | 24297 | 16386 | 15795 | +| Account Creation + ERC20 Transfer | 427599 | 422407 | 283754 | 267761 | 47205 | 49272 | 21415 | 20806 | +| ERC20 Transfer | 110174 | 90486 | NA | NA | 20558 | 31372 | 23415 | 20550 | +| Account Creation + ERC721 Minting | 456870 | 447090 | 283748 | 267761 | 47190 | 73991 | 46150 | 43373 | +| ERC721 Minting | 139420 | 115168 | NA | NA | 20542 | 56091 | 48150 | 45373 | + +### Alchemy (ETH - Gas Policy) + +| Type | Actual Gas | handleOps | \_createSenderIfNeeded | Safe Proxy Factory | ValidateUserOp (Safe) | \_executeUserOp | executeUserOp (Safe) | execTransactionFromModule | +| ---------------------------------- | ---------- | --------- | ---------------------- | ------------------ | --------------------- | --------------- | -------------------- | ------------------------- | +| Account Creation | 411372 | 380632 | 281451 | 267761 | 14992 | 14932 | 7015 | 4411 | +| Account Creation + Native Transfer | 418779 | 388003 | 281451 | 14992 | 22303 | 14386 | 11782 | | +| Native Transfer | 130202 | 104878 | NA | NA | 15320 | 24310 | 16386 | 15795 | +| Account Creation + ERC20 Transfer | 421926 | 395254 | 281471 | 15038 | 29378 | 21415 | 20806 | 18550 | +| ERC20 Transfer | 133394 | 112126 | NA | NA | 20618 | 31385 | 23415 | 20550 | +| Account Creation + ERC721 Minting | 451200 | 419916 | 281464 | 15023 | 54097 | 46150 | | 43373 | +| ERC721 Minting | 162654 | 136786 | NA | NA | 20603 | 56103 | 48150 | 45373 | diff --git a/4337-gas-metering/alchemy/README.md b/4337-gas-metering/alchemy/README.md new file mode 100644 index 00000000..7036e75f --- /dev/null +++ b/4337-gas-metering/alchemy/README.md @@ -0,0 +1,571 @@ +# Alchemy + +## Safe Deployment with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:account + +> @safe-global/4337-gas-metering@1.0.0 alchemy:account +> tsx ./alchemy/alchemy.ts account + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xbf2AE89D8565bc948772097082f2e000FF72CBAB +Address Link: https://sepolia.etherscan.io/address/0xbf2AE89D8565bc948772097082f2e000FF72CBAB + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.02 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xa27fdf3167c094f36a312c14f9c9f46dc6ef205aaece8fb395132d5204c2e85f +UserOp Link: https://jiffyscan.xyz/userOpHash/0xa27fdf3167c094f36a312c14f9c9f46dc6ef205aaece8fb395132d5204c2e85f?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x03c507f5dc14c6b6af04c5ad722f0650d86925837d9889e4972cb087e34d7b88 + +Gas Used (Account or Paymaster): 417476 +Gas Used (Transaction): 417074 +``` + +## Safe Deployment + Native Transfer with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:native-transfer + +> @safe-global/4337-gas-metering@1.0.0 alchemy:native-transfer +> tsx ./alchemy/alchemy.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x0cc883d620a0E313971bd713D1556Ec4dc6821f1 +Address Link: https://sepolia.etherscan.io/address/0x0cc883d620a0E313971bd713D1556Ec4dc6821f1 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.02 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x19b5f1f34fd95466e5865cf10647baf68eed16e815a15a3665c2301c19a07337 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x19b5f1f34fd95466e5865cf10647baf68eed16e815a15a3665c2301c19a07337?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x0263331d8d4568c08d4a700385c08062ee0342fe6f65b2c7eb1a194ddec23ec2 + +Gas Used (Account or Paymaster): 424919 +Gas Used (Transaction): 424505 +``` + +## Native Transfer with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:native-transfer + +> @safe-global/4337-gas-metering@1.0.0 alchemy:native-transfer +> tsx ./alchemy/alchemy.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x0cc883d620a0E313971bd713D1556Ec4dc6821f1 +Address Link: https://sepolia.etherscan.io/address/0x0cc883d620a0E313971bd713D1556Ec4dc6821f1 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.01127255359589216 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xd5b3d3a2f80eba8abd56309a30f2f4eb61c7de6ca3f0c00cc446cb2b570d1c23 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xd5b3d3a2f80eba8abd56309a30f2f4eb61c7de6ca3f0c00cc446cb2b570d1c23?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xf4e38d9f3535dcb9519ca3527734a5ea611a0d1bafb632051736537853eb502b + +Gas Used (Account or Paymaster): 107379 +Gas Used (Transaction): 107057 +``` + +## Safe Deployment + ERC20 Transaction with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:erc20 + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc20 +> tsx ./alchemy/alchemy.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x395E28c040ADe3470B60eE4024a37FeA85ec3Df1 +Address Link: https://sepolia.etherscan.io/address/0x395E28c040ADe3470B60eE4024a37FeA85ec3Df1 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.02 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x7a588948e8fd155e40ad848a5bb08cb8f903af59639f645655748376150cb082 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x7a588948e8fd155e40ad848a5bb08cb8f903af59639f645655748376150cb082?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x794b02531f14b6c432c0dcf08d1cb76a8693dd75b35c5dde0d4547754d208143 + +Gas Used (Account or Paymaster): 432797 +Gas Used (Transaction): 427599 +``` + +## ERC20 Transaction with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:erc20 + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc20 +> tsx ./alchemy/alchemy.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x395E28c040ADe3470B60eE4024a37FeA85ec3Df1 +Address Link: https://sepolia.etherscan.io/address/0x395E28c040ADe3470B60eE4024a37FeA85ec3Df1 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.014251001650899964 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xe8c6de35003a3b7a3164b5bad8ec9008a9f034459084cea05d45bcfa8b00a8b6 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xe8c6de35003a3b7a3164b5bad8ec9008a9f034459084cea05d45bcfa8b00a8b6?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xb56985ee07b1e7931aedc387698620d890c99992c4c688b8b3a150f355089e5d + +Gas Used (Account or Paymaster): 115268 +Gas Used (Transaction): 110174 +``` + +## Safe Deployment + ERC721 Transaction with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:erc721 + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc721 +> tsx ./alchemy/alchemy.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x998CFAdDe39b44c7f94eD4694FD134F764956eC1 +Address Link: https://sepolia.etherscan.io/address/0x998CFAdDe39b44c7f94eD4694FD134F764956eC1 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.02 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x516e6526c5617165d5570999494b428f7be3e864dc28b7094829be3e42f87714 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x516e6526c5617165d5570999494b428f7be3e864dc28b7094829be3e42f87714?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x2d2a0c8215821f0aa9cf8f88175aa8256cdca1a2928f2aa667916e5127f5dcb6 + +Gas Used (Account or Paymaster): 457274 +Gas Used (Transaction): 456870 +``` + +## ERC721 Transaction with Alchemy Paymaster (Own Sponsorship) + +``` +npm run alchemy:erc721 + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc721 +> tsx ./alchemy/alchemy.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x998CFAdDe39b44c7f94eD4694FD134F764956eC1 +Address Link: https://sepolia.etherscan.io/address/0x998CFAdDe39b44c7f94eD4694FD134F764956eC1 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Fee Data from Alchemy. + +Received Gas Data from Alchemy. + +Transferring 0.013259139013776724 ETH to Safe for transaction. + +Transferred required ETH for the transaction. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x236f50d14082d4d6df8b9b3a68d837579c80253d7aebd7ed9f224a42f9914e4f +UserOp Link: https://jiffyscan.xyz/userOpHash/0x236f50d14082d4d6df8b9b3a68d837579c80253d7aebd7ed9f224a42f9914e4f?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x178d2c16a261dcb49e810bf39ce35cf96cbab8c7d3235709c7164ba6193c716e + +Gas Used (Account or Paymaster): 139744 +Gas Used (Transaction): 139420 +``` + +## Safe Deployment with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:account:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:account:paymaster +> tsx ./alchemy/alchemy.ts account paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xb00f9dEda8C1822Ab7d5b721518054F3C4437ed3 +Address Link: https://sepolia.etherscan.io/address/0xb00f9dEda8C1822Ab7d5b721518054F3C4437ed3 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x53f77a84f9f1662bc729e1afe39038ba1b7d6a8118bf4f91d7889cbcf7fb04ca +UserOp Link: https://jiffyscan.xyz/userOpHash/0x53f77a84f9f1662bc729e1afe39038ba1b7d6a8118bf4f91d7889cbcf7fb04ca?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xcbb2c3c49b9d72d9ecf692308d69a8ad797ab5b1c6603f4fad989f966d692af1 + +Gas Used (Account or Paymaster): 411685 +Gas Used (Transaction): 411372 +``` + +## Safe Deployment + Native Transfer with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:native-transfer:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:native-transfer:paymaster +> tsx ./alchemy/alchemy.ts native-transfer paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xA6317eA7527e846cf0d47F66f1A29f20e9Fe27cB +Address Link: https://sepolia.etherscan.io/address/0xA6317eA7527e846cf0d47F66f1A29f20e9Fe27cB + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xac1f808deffef50dcadf59c2ed689e6c56f13a210e8631f098fb2df4f99b6ac9 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xac1f808deffef50dcadf59c2ed689e6c56f13a210e8631f098fb2df4f99b6ac9?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x49fbedf833cfecf9db7de56c61d4227292723115520600dbc3711da5e6a85672 + +Gas Used (Account or Paymaster): 419104 +Gas Used (Transaction): 418779 +``` + +## Native Transfer with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:native-transfer:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:native-transfer:paymaster +> tsx ./alchemy/alchemy.ts native-transfer paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xA6317eA7527e846cf0d47F66f1A29f20e9Fe27cB +Address Link: https://sepolia.etherscan.io/address/0xA6317eA7527e846cf0d47F66f1A29f20e9Fe27cB + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x27978ad0d669dde49ea8b35a3db25e12360de6b2843b49b392f3da5946aa66ed +UserOp Link: https://jiffyscan.xyz/userOpHash/0x27978ad0d669dde49ea8b35a3db25e12360de6b2843b49b392f3da5946aa66ed?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x35f1e5b04d988e4614a17609190b3e21b0a9892f78da9f400248cfb3b5afde9a + +Gas Used (Account or Paymaster): 130440 +Gas Used (Transaction): 130202 +``` + +## Safe Deployment + ERC20 Transaction with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:erc20:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc20:paymaster +> tsx ./alchemy/alchemy.ts erc20 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xbF41EE996c17E6cC237C4186ABBcd9DCe197286B +Address Link: https://sepolia.etherscan.io/address/0xbF41EE996c17E6cC237C4186ABBcd9DCe197286B + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x34a820e77e07b698bc13c81d7f4872889d2330beeffd7c862f18e6073e19d7af +UserOp Link: https://jiffyscan.xyz/userOpHash/0x34a820e77e07b698bc13c81d7f4872889d2330beeffd7c862f18e6073e19d7af?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x7dda913ae986d49c4322f414102ae374441a40adb4b33727e568ba140904d52a + +Gas Used (Account or Paymaster): 427047 +Gas Used (Transaction): 421926 +``` + +## ERC20 Transaction with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:erc20:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc20:paymaster +> tsx ./alchemy/alchemy.ts erc20 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xbF41EE996c17E6cC237C4186ABBcd9DCe197286B +Address Link: https://sepolia.etherscan.io/address/0xbF41EE996c17E6cC237C4186ABBcd9DCe197286B + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xab669bd1093de63befc2a9c453d366a35beba1275b5d5c7e2413cc7a779b6280 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xab669bd1093de63befc2a9c453d366a35beba1275b5d5c7e2413cc7a779b6280?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xe34902ebd5377cac04c47d142f6ca2de558df63a7e0c6541f704df651b7cfcb1 + +Gas Used (Account or Paymaster): 138404 +Gas Used (Transaction): 133394 +``` + +## Safe Deployment + ERC721 Transaction with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:erc721:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc721:paymaster +> tsx ./alchemy/alchemy.ts erc721 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x6Bea0dbCcD353b648F2e5C09106B36C2351208c4 +Address Link: https://sepolia.etherscan.io/address/0x6Bea0dbCcD353b648F2e5C09106B36C2351208c4 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0xcb415603dee2458f4b2d0f69a83264758c8fded6c3a91ec6ac513153bccdf15b +UserOp Link: https://jiffyscan.xyz/userOpHash/0xcb415603dee2458f4b2d0f69a83264758c8fded6c3a91ec6ac513153bccdf15b?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xb1253508bc4ca5ce41222b15b0e7bf03b2273bcb09d93e1d6d6a5ea39b43ee84 + +Gas Used (Account or Paymaster): 451515 +Gas Used (Transaction): 451200 +``` + +## ERC721 Transaction with Alchemy Paymaster (Gas Policy) + +``` +npm run alchemy:erc721:paymaster + +> @safe-global/4337-gas-metering@1.0.0 alchemy:erc721:paymaster +> tsx ./alchemy/alchemy.ts erc721 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x6Bea0dbCcD353b648F2e5C09106B36C2351208c4 +Address Link: https://sepolia.etherscan.io/address/0x6Bea0dbCcD353b648F2e5C09106B36C2351208c4 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Paymaster Data Creation from Alchemy. + +Received Paymaster Data from Alchemy. + +Signed Real Data including Paymaster Data Created by Alchemy. + +UserOperation submitted. Hash: 0x4404a43075c523e59f909acc7379c323eb1427b781bc48b5002007d167e63c83 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x4404a43075c523e59f909acc7379c323eb1427b781bc48b5002007d167e63c83?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xd13fb70626a26aaa02e0389cd9347c1c0d8d8ed9ee794a61c5d3eea4b36e239a + +Gas Used (Account or Paymaster): 162859 +Gas Used (Transaction): 162654 +``` diff --git a/4337-gas-metering/alchemy/alchemy.ts b/4337-gas-metering/alchemy/alchemy.ts new file mode 100644 index 00000000..55a58566 --- /dev/null +++ b/4337-gas-metering/alchemy/alchemy.ts @@ -0,0 +1,290 @@ +import dotenv from "dotenv"; +import { getAccountNonce } from "permissionless"; +import { Network, Alchemy } from "alchemy-sdk"; +import { setTimeout } from "timers/promises"; +import { + Client, + Hash, + createPublicClient, + formatEther, + http, + parseEther, + zeroAddress, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { goerli, sepolia } from "viem/chains"; +import { + SAFE_ADDRESSES_MAP, + getAccountAddress, + getAccountInitCode, +} from "../utils/safe"; +import { + UserOperation, + signUserOperation, + txTypes, + getGasValuesFromAlchemyPaymaster, + getFeeValuesFromAlchemy, + getMaxFeePerGas, + getGasValuesFromAlchemy, + submitUserOperationAlchemy, + createCallData, +} from "../utils/userOps"; +import { transferETH } from "../utils/nativeTransfer"; + +dotenv.config(); +const paymaster = "alchemy"; + +const privateKey = process.env.PRIVATE_KEY; + +const entryPointAddress = process.env + .ALCHEMY_ENTRYPOINT_ADDRESS as `0x${string}`; +const multiSendAddress = process.env.ALCHEMY_MULTISEND_ADDRESS as `0x${string}`; + +const saltNonce = BigInt(process.env.ALCHEMY_NONCE as string); + +const chain = process.env.ALCHEMY_CHAIN; +const chainID = Number(process.env.ALCHEMY_CHAIN_ID); + +const safeVersion = process.env.SAFE_VERSION as string; + +const rpcURL = process.env.ALCHEMY_RPC_URL; +const policyID = process.env.ALCHEMY_GAS_POLICY; +const apiKey = process.env.ALCHEMY_API_KEY; + +const erc20TokenAddress = process.env + .ALCHEMY_ERC20_TOKEN_CONTRACT as `0x${string}`; +const erc721TokenAddress = process.env + .ALCHEMY_ERC721_TOKEN_CONTRACT as `0x${string}`; + +const argv = process.argv.slice(2); +let usePaymaster!: boolean; +if (argv.length < 1 || argv.length > 2) { + throw new Error("TX Type Argument not passed."); +} else if (argv.length == 2 && argv[1] == "paymaster=true") { + if (policyID) { + usePaymaster = true; + } else { + throw new Error("Paymaster requires policyID to be set."); + } +} + +const txType: string = argv[0]; +if (!txTypes.includes(txType)) { + throw new Error("TX Type Argument Invalid"); +} + +const safeAddresses = ( + SAFE_ADDRESSES_MAP as Record> +)[safeVersion]; +let chainAddresses; +if (safeAddresses) { + chainAddresses = safeAddresses[chainID]; +} + +if (apiKey === undefined) { + throw new Error( + "Please replace the `apiKey` env variable with your Alchemy API key", + ); +} + +if (!privateKey) { + throw new Error( + "Please populate .env file with demo Private Key. Recommended to not use your personal private key.", + ); +} + +const signer = privateKeyToAccount(privateKey as Hash); +console.log("Signer Extracted from Private Key."); + +let publicClient; +let settings; +if (chain == "sepolia") { + publicClient = createPublicClient({ + transport: http(rpcURL), + chain: sepolia, + }); + settings = { + apiKey: apiKey, + network: Network.ETH_SEPOLIA, + }; +} else if (chain == "goerli") { + publicClient = createPublicClient({ + transport: http(rpcURL), + chain: goerli, + }); + settings = { + apiKey: apiKey, + network: Network.ETH_GOERLI, + }; +} else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); +} + +const alchemy = new Alchemy(settings); + +const initCode = await getAccountInitCode({ + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: zeroAddress, + paymasterAddress: zeroAddress, +}); +console.log("\nInit Code Created."); + +const senderAddress = await getAccountAddress({ + client: publicClient, + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: zeroAddress, + paymasterAddress: zeroAddress, +}); +console.log("\nCounterfactual Sender Address Created:", senderAddress); +console.log( + "Address Link: https://" + chain + ".etherscan.io/address/" + senderAddress, +); + +const contractCode = await publicClient.getBytecode({ address: senderAddress }); + +if (contractCode) { + console.log("\nThe Safe is already deployed."); + if (txType == "account") { + process.exit(0); + } +} else { + console.log( + "\nDeploying a new Safe and executing calldata passed with it (if any).", + ); +} + +const newNonce = await getAccountNonce(publicClient as Client, { + entryPoint: entryPointAddress, + sender: senderAddress, +}); +console.log("\nNonce for the sender received from EntryPoint."); + +const txCallData: `0x${string}` = await createCallData( + chain, + publicClient, + signer, + txType, + senderAddress, + erc20TokenAddress, + erc721TokenAddress, + paymaster, +); + +const sponsoredUserOperation: UserOperation = { + sender: senderAddress, + nonce: newNonce, + initCode: contractCode ? "0x" : initCode, + callData: txCallData, + callGasLimit: 1n, // All Gas Values will be filled by Estimation Response Data. + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: "0x", + signature: "0x", +}; + +sponsoredUserOperation.signature = await signUserOperation( + sponsoredUserOperation, + signer, + chainID, + entryPointAddress, + chainAddresses.SAFE_4337_MODULE_ADDRESS, +); +console.log("\nSigned Dummy Data for Paymaster Data Creation from Alchemy."); + +if (usePaymaster) { + const rvGas = await getGasValuesFromAlchemyPaymaster( + policyID, + entryPointAddress, + sponsoredUserOperation, + chain, + apiKey, + ); + + sponsoredUserOperation.preVerificationGas = rvGas?.preVerificationGas; + sponsoredUserOperation.callGasLimit = rvGas?.callGasLimit; + sponsoredUserOperation.verificationGasLimit = rvGas?.verificationGasLimit; + sponsoredUserOperation.paymasterAndData = rvGas?.paymasterAndData; + sponsoredUserOperation.maxFeePerGas = rvGas?.maxFeePerGas; + sponsoredUserOperation.maxPriorityFeePerGas = rvGas?.maxPriorityFeePerGas; +} else { + sponsoredUserOperation.maxPriorityFeePerGas = await getFeeValuesFromAlchemy( + chain, + apiKey, + ); + sponsoredUserOperation.maxFeePerGas = await getMaxFeePerGas( + alchemy, + sponsoredUserOperation.maxPriorityFeePerGas, + ); + + const rvGas = await getGasValuesFromAlchemy( + entryPointAddress, + sponsoredUserOperation, + chain, + apiKey, + ); + + sponsoredUserOperation.preVerificationGas = rvGas?.preVerificationGas; + sponsoredUserOperation.callGasLimit = rvGas?.callGasLimit; + sponsoredUserOperation.verificationGasLimit = rvGas?.verificationGasLimit; + + const weiToSend = parseEther("0.02"); + let safeETHBalance = await publicClient.getBalance({ + address: senderAddress, + }); + if (safeETHBalance < weiToSend) { + console.log( + "\nTransferring", + formatEther(weiToSend - safeETHBalance), + "ETH to Safe for transaction.", + ); + await transferETH( + publicClient, + signer, + senderAddress, + weiToSend - safeETHBalance, + chain, + paymaster, + ); + while (safeETHBalance < weiToSend) { + await setTimeout(30000); // Sometimes it takes time to index. + safeETHBalance = await publicClient.getBalance({ + address: senderAddress, + }); + } + console.log("\nTransferred required ETH for the transaction."); + } +} + +sponsoredUserOperation.signature = await signUserOperation( + sponsoredUserOperation, + signer, + chainID, + entryPointAddress, + chainAddresses.SAFE_4337_MODULE_ADDRESS, +); +console.log( + "\nSigned Real Data including Paymaster Data Created by Alchemy.\n", +); + +await submitUserOperationAlchemy( + entryPointAddress, + sponsoredUserOperation, + chain, + apiKey, +); diff --git a/4337-gas-metering/gelato/README.md b/4337-gas-metering/gelato/README.md new file mode 100644 index 00000000..3d4517f3 --- /dev/null +++ b/4337-gas-metering/gelato/README.md @@ -0,0 +1,308 @@ +# Gelato + +## Safe Deployment with Gelato + +``` +npm run gelato:account:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:account:1balance +> tsx ./gelato/gelato.ts account + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8Af37a939fBEd9ac9AdB04270EF28DC844256CB3 +Address Link: https://sepolia.etherscan.io/address/0x8Af37a939fBEd9ac9AdB04270EF28DC844256CB3 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0x55def6ec01815152dfbf4f943f21e164559d79a974789e1a647aa7394fa80984 +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0x55def6ec01815152dfbf4f943f21e164559d79a974789e1a647aa7394fa80984 + +User OP Hash: 0xf46f8a12a949bf36e9584c8e40772162c3530ce4f72542e9da3b672ccce9019a +UserOp Link: https://jiffyscan.xyz/userOpHash/0xf46f8a12a949bf36e9584c8e40772162c3530ce4f72542e9da3b672ccce9019a?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x21205216b55d0f48aa09ff4289ae982c3b16e7a9905494815cabd1fb01a0d505 + +Gas Used (Account or Paymaster): 397421 +Gas Used (Transaction): 397421 +``` + +## Safe Deployment + Native Transfer with Gelato + +``` +npm run gelato:native-transfer:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:native-transfer:1balance +> tsx ./gelato/gelato.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8652734F7404E97FEe69cf617286C5423593Bad0 +Address Link: https://sepolia.etherscan.io/address/0x8652734F7404E97FEe69cf617286C5423593Bad0 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Transferring 0.000001 ETH to Safe for native transfer. + +Transferred required ETH for the native transfer. + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0x64080f3741400714cc10a6ca486e22467a2a879935c19268d6f1fe709e7f6ad8 +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0x64080f3741400714cc10a6ca486e22467a2a879935c19268d6f1fe709e7f6ad8 + +User OP Hash: 0x2126e36afaeae4190df1e540613aa972f8d73b37d9268ee089f4440db23ae74b +UserOp Link: https://jiffyscan.xyz/userOpHash/0x2126e36afaeae4190df1e540613aa972f8d73b37d9268ee089f4440db23ae74b?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x7bb36c93d21c911a2c1bdc7e50f55030cc7f006a1f7e2e651251dca9651383e0 + +Gas Used (Account or Paymaster): 404828 +Gas Used (Transaction): 404828 +``` + +## Native Transfer with Gelato + +``` +npm run gelato:native-transfer:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:native-transfer:1balance +> tsx ./gelato/gelato.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8652734F7404E97FEe69cf617286C5423593Bad0 +Address Link: https://sepolia.etherscan.io/address/0x8652734F7404E97FEe69cf617286C5423593Bad0 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Transferring 0.000001 ETH to Safe for native transfer. + +Transferred required ETH for the native transfer. + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0xa2d246b5deabbcc4378f3fd3f1fe9bf1ddae3a6b4e286d7f759c27e558562754 +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0xa2d246b5deabbcc4378f3fd3f1fe9bf1ddae3a6b4e286d7f759c27e558562754 + +User OP Hash: 0xc9c86bcbe24adf9e67fbe199dd2cb9abe7cff12634f76df68aa98ee28193ac48 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xc9c86bcbe24adf9e67fbe199dd2cb9abe7cff12634f76df68aa98ee28193ac48?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xefa122224466e9f1d09d42683aaec2b37f9871f7f5569099f0cc066961b39f15 + +Gas Used (Account or Paymaster): 114692 +Gas Used (Transaction): 114692 +``` + +## Safe Deployment + ERC20 Transaction with Gelato + +``` +npm run gelato:erc20:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:erc20:1balance +> tsx ./gelato/gelato.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xe86D78002637143c34d0687890c1b102D054a614 +Address Link: https://sepolia.etherscan.io/address/0xe86D78002637143c34d0687890c1b102D054a614 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0x02da9332b8528ce750fe2db6f245c90854fa5daa339911bc3b12691728bafb1b +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0x02da9332b8528ce750fe2db6f245c90854fa5daa339911bc3b12691728bafb1b + +User OP Hash: 0xf6d06a5723198af02f26a2daa0a6eec019ab539cfb97277a6aa5251e40863aa2 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xf6d06a5723198af02f26a2daa0a6eec019ab539cfb97277a6aa5251e40863aa2?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x4f55488ecc542be4effc2d7a4743345db6790ef80e7ca94f3e939a290738fa2d + +Gas Used (Account or Paymaster): 408160 +Gas Used (Transaction): 408160 +``` + +## ERC20 Transaction with Gelato + +``` +npm run gelato:erc20:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:erc20:1balance +> tsx ./gelato/gelato.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xe86D78002637143c34d0687890c1b102D054a614 +Address Link: https://sepolia.etherscan.io/address/0xe86D78002637143c34d0687890c1b102D054a614 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0x5f2e04dae7ec76037e22d250bcca19600b7c0cad4dcddc8015e629c69c22c2b3 +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0x5f2e04dae7ec76037e22d250bcca19600b7c0cad4dcddc8015e629c69c22c2b3 + +User OP Hash: 0x129341b16c3d7ffdafe17eb3bcae112eebc087ca4fef61ba503b7e460f3f12c4 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x129341b16c3d7ffdafe17eb3bcae112eebc087ca4fef61ba503b7e460f3f12c4?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x152c78871b6940215ba37cac5f5231fa2bd4bcf40742ebcf741903ce64adc405 + +Gas Used (Account or Paymaster): 118033 +Gas Used (Transaction): 118033 +``` + +## Safe Deployment + ERC721 Transaction with Gelato + +``` +npm run gelato:erc721:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:erc721:1balance +> tsx ./gelato/gelato.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xC8D65452DC04F13E2915916699f5B61fF647C163 +Address Link: https://sepolia.etherscan.io/address/0xC8D65452DC04F13E2915916699f5B61fF647C163 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0xe06c299ab9deac8ee76e40960af3b56b219dabd97488a67093a752376271fe3a +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0xe06c299ab9deac8ee76e40960af3b56b219dabd97488a67093a752376271fe3a + +User OP Hash: 0xae2b1d74f3e3e921f47db23c28f7c3f100bcaf8fe164d16ddd6b562b22519afb +UserOp Link: https://jiffyscan.xyz/userOpHash/0xae2b1d74f3e3e921f47db23c28f7c3f100bcaf8fe164d16ddd6b562b22519afb?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0x4aa37845d5c9fc0ad0713caefbbc9931263040d1502f076a98c993282257e51d + +Gas Used (Account or Paymaster): 437372 +Gas Used (Transaction): 437372 +``` + +## ERC721 Transaction with Gelato + +``` +npm run gelato:erc721:1balance + +> @safe-global/4337-gas-metering@1.0.0 gelato:erc721:1balance +> tsx ./gelato/gelato.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0xC8D65452DC04F13E2915916699f5B61fF647C163 +Address Link: https://sepolia.etherscan.io/address/0xC8D65452DC04F13E2915916699f5B61fF647C163 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Signed Dummy Data for Gelato. + +Received Gas Data from Gelato. + +Signed Real Data for Gelato. + +UserOperation submitted. + +Gelato Relay Task ID: 0xe201bbab015baeeaeab68f2e3a2c6e1cfe7af6704df0106b9fd3c9587c6ef61e +Gelato Relay Task Link: https://api.gelato.digital/tasks/status/0xe201bbab015baeeaeab68f2e3a2c6e1cfe7af6704df0106b9fd3c9587c6ef61e + +User OP Hash: 0x00d0e383c622a7e3b5c010ce915b300bf182988c6914a10b06efa1b933fd7d21 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x00d0e383c622a7e3b5c010ce915b300bf182988c6914a10b06efa1b933fd7d21?network=sepolia + +Transaction Link: https://sepolia.etherscan.io/tx/0xfac73b16d0932ba97a93f12ddc230024b102e581a37a53625dfe8108ca581bb5 + +Gas Used (Account or Paymaster): 147232 +Gas Used (Transaction): 147232 +``` diff --git a/4337-gas-metering/gelato/gelato.ts b/4337-gas-metering/gelato/gelato.ts new file mode 100644 index 00000000..aac197f5 --- /dev/null +++ b/4337-gas-metering/gelato/gelato.ts @@ -0,0 +1,214 @@ +import dotenv from "dotenv"; +import { getAccountNonce } from "permissionless"; +import { Client, Hash, createPublicClient, http, zeroAddress } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { sepolia } from "viem/chains"; +import { + SAFE_ADDRESSES_MAP, + getAccountAddress, + getAccountInitCode, +} from "../utils/safe"; +import { + UserOperation, + signUserOperation, + txTypes, + getGasValuesFromGelato, + submitUserOperationGelato, + createCallData, +} from "../utils/userOps"; + +dotenv.config(); +const paymaster = "gelato"; + +const privateKey = process.env.PRIVATE_KEY; + +const entryPointAddress = process.env + .GELATO_ENTRYPOINT_ADDRESS as `0x${string}`; +const multiSendAddress = process.env.GELATO_MULTISEND_ADDRESS as `0x${string}`; + +const saltNonce = BigInt(process.env.GELATO_NONCE as string); + +const chain = process.env.GELATO_CHAIN; +const chainID = Number(process.env.GELATO_CHAIN_ID); + +const safeVersion = process.env.SAFE_VERSION as string; + +const rpcURL = process.env.GELATO_RPC_URL; +const policyID = process.env.GELATO_GAS_POLICY; +const apiKey = process.env.GELATO_API_KEY; + +const erc20TokenAddress = process.env + .GELATO_ERC20_TOKEN_CONTRACT as `0x${string}`; +const erc721TokenAddress = process.env + .GELATO_ERC721_TOKEN_CONTRACT as `0x${string}`; + +const argv = process.argv.slice(2); +let usePaymaster!: boolean; +if (argv.length < 1 || argv.length > 2) { + throw new Error("TX Type Argument not passed."); +} else if (argv.length == 2 && argv[1] == "paymaster=true") { + if (policyID) { + usePaymaster = true; + } else { + throw new Error("Paymaster requires policyID to be set."); + } +} + +const txType: string = argv[0]; +if (!txTypes.includes(txType)) { + throw new Error("TX Type Argument Invalid"); +} + +const safeAddresses = ( + SAFE_ADDRESSES_MAP as Record> +)[safeVersion]; +let chainAddresses; +if (safeAddresses) { + chainAddresses = safeAddresses[chainID]; +} + +if (apiKey === undefined) { + throw new Error( + "Please replace the `apiKey` env variable with your Alchemy API key", + ); +} + +if (!privateKey) { + throw new Error( + "Please populate .env file with demo Private Key. Recommended to not use your personal private key.", + ); +} + +const signer = privateKeyToAccount(privateKey as Hash); +console.log("Signer Extracted from Private Key."); + +let publicClient; +if (chain == "sepolia") { + publicClient = createPublicClient({ + transport: http(rpcURL), + chain: sepolia, + }); +} else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); +} + +const initCode = await getAccountInitCode({ + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: zeroAddress, + paymasterAddress: zeroAddress, +}); +console.log("\nInit Code Created."); + +const senderAddress = await getAccountAddress({ + client: publicClient, + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: zeroAddress, + paymasterAddress: zeroAddress, +}); +console.log("\nCounterfactual Sender Address Created:", senderAddress); +console.log( + "Address Link: https://" + chain + ".etherscan.io/address/" + senderAddress, +); + +const contractCode = await publicClient.getBytecode({ address: senderAddress }); + +if (contractCode) { + console.log("\nThe Safe is already deployed."); + if (txType == "account") { + process.exit(0); + } +} else { + console.log( + "\nDeploying a new Safe and executing calldata passed with it (if any).", + ); +} + +const newNonce = await getAccountNonce(publicClient as Client, { + entryPoint: entryPointAddress, + sender: senderAddress, +}); +console.log("\nNonce for the sender received from EntryPoint."); + +const txCallData: `0x${string}` = await createCallData( + chain, + publicClient, + signer, + txType, + senderAddress, + erc20TokenAddress, + erc721TokenAddress, + paymaster, +); + +const sponsoredUserOperation: UserOperation = { + sender: senderAddress, + nonce: newNonce, + initCode: contractCode ? "0x" : initCode, + callData: txCallData, + callGasLimit: 1n, // All Gas Values will be filled by Estimation Response Data. + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: "0x", + signature: "0x", +}; + +sponsoredUserOperation.signature = await signUserOperation( + sponsoredUserOperation, + signer, + chainID, + entryPointAddress, + chainAddresses.SAFE_4337_MODULE_ADDRESS, +); +console.log("\nSigned Dummy Data for Gelato."); + +if (usePaymaster) { + throw new Error("Currently paymaster is not supported for Gelato."); +} else { + sponsoredUserOperation.maxPriorityFeePerGas = 0n; // Gelato prefers to keep it to zero. + sponsoredUserOperation.maxFeePerGas = 0n; + + const rvGas = await getGasValuesFromGelato( + entryPointAddress, + sponsoredUserOperation, + chainID, + apiKey, + ); + + sponsoredUserOperation.preVerificationGas = rvGas?.preVerificationGas; + sponsoredUserOperation.callGasLimit = rvGas?.callGasLimit; + // sponsoredUserOperation.callGasLimit = "0x186a0" as any; + sponsoredUserOperation.verificationGasLimit = rvGas?.verificationGasLimit; +} + +sponsoredUserOperation.signature = await signUserOperation( + sponsoredUserOperation, + signer, + chainID, + entryPointAddress, + chainAddresses.SAFE_4337_MODULE_ADDRESS, +); +console.log("\nSigned Real Data for Gelato."); + +await submitUserOperationGelato( + entryPointAddress, + sponsoredUserOperation, + chain, + chainID, + apiKey, +); diff --git a/4337-gas-metering/package-lock.json b/4337-gas-metering/package-lock.json new file mode 100644 index 00000000..469641ea --- /dev/null +++ b/4337-gas-metering/package-lock.json @@ -0,0 +1,5208 @@ +{ + "name": "@safe-global/4337-gas-metering", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@safe-global/4337-gas-metering", + "version": "1.0.0", + "license": "GPL-3.0", + "dependencies": { + "@alchemy/aa-accounts": "1.2.0", + "@alchemy/aa-alchemy": "1.2.0", + "@alchemy/aa-core": "1.2.0", + "alchemy-sdk": "3.1.0", + "dotenv": "16.3.1", + "permissionless": "0.0.16", + "viem": "1.19.11" + }, + "devDependencies": { + "@types/node": "20.10.2", + "@typescript-eslint/eslint-plugin": "^6.15.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.5.0", + "eslint-plugin-prettier": "^5.1.0", + "eslint-plugin-promise": "^6.1.1", + "prettier": "3.1.0", + "tsx": "3.13.0", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, + "node_modules/@alchemy/aa-accounts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@alchemy/aa-accounts/-/aa-accounts-1.2.0.tgz", + "integrity": "sha512-H0iBnFZh1XGC/fGDwlmXiB0aHGJlGg0+o/KhIrgfqxvoIz6A5FnW8uaJ+ciI/MBb8ZzoK9Mo0BgNE3spYppxRQ==", + "dependencies": { + "@alchemy/aa-core": "^1.2.0", + "viem": "^1.16.2" + } + }, + "node_modules/@alchemy/aa-alchemy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@alchemy/aa-alchemy/-/aa-alchemy-1.2.0.tgz", + "integrity": "sha512-KqSWARu1DqYHE0UJas1O/WngoT0uqztGCUy5eO1yWQXz40I5hFMuWDYsV2kyo8UmTsn8qsEtQpts8HhAX4LWIw==", + "dependencies": { + "@alchemy/aa-core": "^1.2.0", + "viem": "^1.16.2" + }, + "optionalDependencies": { + "alchemy-sdk": "^3.0.0" + } + }, + "node_modules/@alchemy/aa-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@alchemy/aa-core/-/aa-core-1.2.0.tgz", + "integrity": "sha512-R56LwXKlR3CkvINH/OoZTFdJNPqzngBToj5DjTxipOlz/BFFPi0siG5nmbpCHm0683E6Go+az/hNUxX0PrkopA==", + "dependencies": { + "abitype": "^0.8.3", + "eventemitter3": "^5.0.1", + "viem": "^1.16.2", + "zod": "^3.22.4" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@scure/base": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", + "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.2.tgz", + "integrity": "sha512-37MXfxkb0vuIlRKHNxwCkb60PNBpR94u4efQuN4JgIAm66zfCDXGSAFCef9XUWFovX2R1ok6Z7MHhtdVXXkkIw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", + "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/type-utils": "6.15.0", + "@typescript-eslint/utils": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", + "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", + "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", + "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/utils": "6.15.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", + "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", + "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", + "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", + "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abitype": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.8.11.tgz", + "integrity": "sha512-bM4v2dKvX08sZ9IU38IN5BKmN+ZkOSd2oI4a9f0ejHYZQYV6cDr7j+d95ga0z2XHG36Y4jzoG5Z7qDqxp7fi/A==", + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alchemy-sdk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/alchemy-sdk/-/alchemy-sdk-3.1.0.tgz", + "integrity": "sha512-KMzBo0Dq+cEqXegn4fh2sP74dhAngr9twIv2pBTyPq3/ZJs+aiXXlFzVrVUYaa6x9L7iQtqhz3YKFCuN5uvpAg==", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@ethersproject/units": "^5.7.0", + "@ethersproject/wallet": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "axios": "^0.26.1", + "sturdy-websocket": "^0.2.1", + "websocket": "^1.0.34" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "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" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", + "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", + "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.1.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.5.0.tgz", + "integrity": "sha512-Hw02Bj1QrZIlKyj471Tb1jSReTl4ghIMHGuBGiMVmw+s0jOPbI4CBuYpGbZr+tdQ+VAvSK6FDSta3J4ib/SKHQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.0.tgz", + "integrity": "sha512-hQc+2zbnMeXcIkg+pKZtVa+3Yqx4WY7SMkn1PLZ4VbBEU7jJIpVn9347P8BBhTbz6ne85aXvQf30kvexcqBeWw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isows": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", + "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/node-gyp-build": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/permissionless": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/permissionless/-/permissionless-0.0.16.tgz", + "integrity": "sha512-tuGMf1l6X+WD8X2/WZAaF0Rqj7ysWX++eiUNgG/A+Qe1vP+e4Oik57jVUOW7Yo/VXQ9zU3luEC2GmwrFeo4b8w==", + "peerDependencies": { + "viem": "^1.19.4" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sturdy-websocket": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz", + "integrity": "sha512-NnzSOEKyv4I83qbuKw9ROtJrrT6Z/Xt7I0HiP/e6H6GnpeTDvzwGIGeJ8slai+VwODSHQDooW2CAilJwT9SpRg==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz", + "integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsx": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.13.0.tgz", + "integrity": "sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==", + "dev": true, + "dependencies": { + "esbuild": "~0.18.20", + "get-tsconfig": "^4.7.2", + "source-map-support": "^0.5.21" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/viem": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/viem/-/viem-1.19.11.tgz", + "integrity": "sha512-dbsXEWDBZkByuzJXAs/e01j7dpUJ5ICF5WcyntFwf8Y97n5vnC/91lAleSa6DA5V4WJvYZbhDpYeTctsMAQnhA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@scure/bip32": "1.3.2", + "@scure/bip39": "1.2.1", + "abitype": "0.9.8", + "isows": "1.0.3", + "ws": "8.13.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/abitype": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.9.8.tgz", + "integrity": "sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/4337-gas-metering/package.json b/4337-gas-metering/package.json new file mode 100644 index 00000000..c3119523 --- /dev/null +++ b/4337-gas-metering/package.json @@ -0,0 +1,73 @@ +{ + "name": "@safe-global/4337-gas-metering", + "version": "1.0.0", + "description": "Gas Metering Analysis for Safe with 4337 Module", + "homepage": "https://github.com/safe-global/safe-modules/4337-gas-metering", + "license": "GPL-3.0", + "type": "module", + "private": true, + "scripts": { + "alchemy:account": "tsx ./alchemy/alchemy.ts account", + "alchemy:account:paymaster": "tsx ./alchemy/alchemy.ts account paymaster=true", + "alchemy:native-transfer": "tsx ./alchemy/alchemy.ts native-transfer", + "alchemy:native-transfer:paymaster": "tsx ./alchemy/alchemy.ts native-transfer paymaster=true", + "alchemy:erc20": "tsx ./alchemy/alchemy.ts erc20", + "alchemy:erc20:paymaster": "tsx ./alchemy/alchemy.ts erc20 paymaster=true", + "alchemy:erc721": "tsx ./alchemy/alchemy.ts erc721", + "alchemy:erc721:paymaster": "tsx ./alchemy/alchemy.ts erc721 paymaster=true", + "alchemy": "tsx ./alchemy/alchemy.ts", + "build": "npx rimraf dist && tsc", + "fmt": "prettier --ignore-path .gitignore --write .", + "fmt:check": "prettier --check .", + "lint": "eslint ./alchemy && eslint ./gelato && eslint ./pimlico && eslint ./utils", + "gelato:account:1balance": "tsx ./gelato/gelato.ts account", + "gelato:native-transfer:1balance": "tsx ./gelato/gelato.ts native-transfer", + "gelato:erc20:1balance": "tsx ./gelato/gelato.ts erc20", + "gelato:erc721:1balance": "tsx ./gelato/gelato.ts erc721", + "gelato": "tsx ./gelato/gelato.ts", + "pimlico:account": "tsx ./pimlico/pimlico.ts account", + "pimlico:account:paymaster": "tsx ./pimlico/pimlico.ts account paymaster=true", + "pimlico:native-transfer": "tsx ./pimlico/pimlico.ts native-transfer", + "pimlico:native-transfer:paymaster": "tsx ./pimlico/pimlico.ts native-transfer paymaster=true", + "pimlico:erc20": "tsx ./pimlico/pimlico.ts erc20", + "pimlico:erc20:paymaster": "tsx ./pimlico/pimlico.ts erc20 paymaster=true", + "pimlico:erc721": "tsx ./pimlico/pimlico.ts erc721", + "pimlico:erc721:paymaster": "tsx ./pimlico/pimlico.ts erc721 paymaster=true", + "pimlico": "tsx ./pimlico/pimlico.ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/safe-global/safe-modules.git" + }, + "keywords": [ + "Ethereum", + "Wallet", + "Safe" + ], + "author": "@safe-global", + "bugs": { + "url": "https://github.com/safe-global/safe-modules/issues" + }, + "dependencies": { + "@alchemy/aa-accounts": "1.2.0", + "@alchemy/aa-alchemy": "1.2.0", + "@alchemy/aa-core": "1.2.0", + "alchemy-sdk": "3.1.0", + "dotenv": "16.3.1", + "permissionless": "0.0.16", + "viem": "1.19.11" + }, + "devDependencies": { + "@types/node": "20.10.2", + "@typescript-eslint/eslint-plugin": "^6.15.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.5.0", + "eslint-plugin-prettier": "^5.1.0", + "eslint-plugin-promise": "^6.1.1", + "prettier": "3.1.0", + "tsx": "3.13.0", + "typescript": "^5.3.3" + } +} diff --git a/4337-gas-metering/pimlico/README.md b/4337-gas-metering/pimlico/README.md new file mode 100644 index 00000000..87f6018a --- /dev/null +++ b/4337-gas-metering/pimlico/README.md @@ -0,0 +1,515 @@ +# Pimlico + +## Safe Deployment with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:account + +> @safe-global/4337-gas-metering@1.0.0 pimlico:account +> tsx ./pimlico/pimlico.ts account + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x49Bcc15610B8bb5C35392E9bcCa19af516BeF408 +Address Link: https://mumbai.polygonscan.com/address/0x49Bcc15610B8bb5C35392E9bcCa19af516BeF408 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x81ba032e86c9169c9f295a1d435458ba7d7c0cab95a575b84081539e6266e461 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x81ba032e86c9169c9f295a1d435458ba7d7c0cab95a575b84081539e6266e461?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0x3c6284e4df1686d699d2bc4cca04a25ecc76d68a73665ca53d466e6bd6bedf28 +Transaction Link: https://mumbai.polygonscan.com/tx/0x3c6284e4df1686d699d2bc4cca04a25ecc76d68a73665ca53d466e6bd6bedf28 + +Gas Used (Account or Paymaster): 504830 +Gas Used (Transaction): 506573 +``` + +## Safe Deployment + Native Transfer with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:native-transfer + +> @safe-global/4337-gas-metering@1.0.0 pimlico:native-transfer +> tsx ./pimlico/pimlico.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x32965E785bC35EaD9C837dd4e602E260B84f2594 +Address Link: https://mumbai.polygonscan.com/address/0x32965E785bC35EaD9C837dd4e602E260B84f2594 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x33550a7468bb4949c8cdc0b333cc9aa0e289f7e080e85274275644ef8d8786c9 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x33550a7468bb4949c8cdc0b333cc9aa0e289f7e080e85274275644ef8d8786c9?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0x8bc4e42b076d22e0fc3418eba40c65caab6e3a10c1fbb10cbeee4a7fbfa8b4b3 +Transaction Link: https://mumbai.polygonscan.com/tx/0x8bc4e42b076d22e0fc3418eba40c65caab6e3a10c1fbb10cbeee4a7fbfa8b4b3 + +Gas Used (Account or Paymaster): 509312 +Gas Used (Transaction): 511055 +``` + +## Native Transfer with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:native-transfer + +> @safe-global/4337-gas-metering@1.0.0 pimlico:native-transfer +> tsx ./pimlico/pimlico.ts native-transfer + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x32965E785bC35EaD9C837dd4e602E260B84f2594 +Address Link: https://mumbai.polygonscan.com/address/0x32965E785bC35EaD9C837dd4e602E260B84f2594 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x1da0059783a7bd4d752bcc7b1a44c06f01236ba38687d502e9a69d7c84b1230e +UserOp Link: https://jiffyscan.xyz/userOpHash/0x1da0059783a7bd4d752bcc7b1a44c06f01236ba38687d502e9a69d7c84b1230e?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0x46cdfc14649087609f69411fc41f5feb4dc23a6ea9255928b932841858e5f186 +Transaction Link: https://mumbai.polygonscan.com/tx/0x46cdfc14649087609f69411fc41f5feb4dc23a6ea9255928b932841858e5f186 + +Gas Used (Account or Paymaster): 197382 +Gas Used (Transaction): 199262 +``` + +## Safe Deployment + ERC20 Transaction with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:erc20 + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc20 +> tsx ./pimlico/pimlico.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x70E545d18b53413c47051a56b063AdE487a209Ff +Address Link: https://mumbai.polygonscan.com/address/0x70E545d18b53413c47051a56b063AdE487a209Ff + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x4d175a2c4e151b7745e03ce9936e06fbc3115118d06e5b8bef5211add2151821 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x4d175a2c4e151b7745e03ce9936e06fbc3115118d06e5b8bef5211add2151821?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xa5cf461800341c2e9934608ff55aeda26d1a3e7da4f5bc9f3cce3fd185409623 +Transaction Link: https://mumbai.polygonscan.com/tx/0xa5cf461800341c2e9934608ff55aeda26d1a3e7da4f5bc9f3cce3fd185409623 + +Gas Used (Account or Paymaster): 517210 +Gas Used (Transaction): 514156 +``` + +## ERC20 Transaction with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:erc20 + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc20 +> tsx ./pimlico/pimlico.ts erc20 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x70E545d18b53413c47051a56b063AdE487a209Ff +Address Link: https://mumbai.polygonscan.com/address/0x70E545d18b53413c47051a56b063AdE487a209Ff + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0xd4475cf9151629cb44236b4da541996b103c4e89a075c2cb9375f533421da1e1 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xd4475cf9151629cb44236b4da541996b103c4e89a075c2cb9375f533421da1e1?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xdc21ae13dc92eb48851fa62f57c74f3a0085acf81343d9aaaa14fcc3c6911f91 +Transaction Link: https://mumbai.polygonscan.com/tx/0xdc21ae13dc92eb48851fa62f57c74f3a0085acf81343d9aaaa14fcc3c6911f91 + +Gas Used (Account or Paymaster): 205268 +Gas Used (Transaction): 202387 +``` + +## Safe Deployment + ERC721 Transaction with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:erc721 + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc721 +> tsx ./pimlico/pimlico.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x6306048538368FD9009102B10EdB6B38Fa6d48a7 +Address Link: https://mumbai.polygonscan.com/address/0x6306048538368FD9009102B10EdB6B38Fa6d48a7 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x24a391b1114c2ed44caea101d4a01011bb765c1f85e4d3f69fb16aae79ab2fac +UserOp Link: https://jiffyscan.xyz/userOpHash/0x24a391b1114c2ed44caea101d4a01011bb765c1f85e4d3f69fb16aae79ab2fac?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xcd6c137474be4f002822498e032ad9b78b0505bd4db495ee65fc602ec1a7f006 +Transaction Link: https://mumbai.polygonscan.com/tx/0xcd6c137474be4f002822498e032ad9b78b0505bd4db495ee65fc602ec1a7f006 + +Gas Used (Account or Paymaster): 541670 +Gas Used (Transaction): 543411 +``` + +## ERC721 Transaction with Pimlico Paymaster (Own Sponsorship) + +``` +npm run pimlico:erc721 + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc721 +> tsx ./pimlico/pimlico.ts erc721 + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x6306048538368FD9009102B10EdB6B38Fa6d48a7 +Address Link: https://mumbai.polygonscan.com/address/0x6306048538368FD9009102B10EdB6B38Fa6d48a7 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. + +Safe Wallet USDC Balance: 0 + +Transferring 1 USDC Token for paying the Paymaster from Sender to Safe. + +Updated Safe Wallet USDC Balance: 1 +UserOperation submitted. Hash: 0x05499bd6a9c3b6ecf4bd2ec5be00ae3f1e5597daca258b2a27c58b330a90cb28 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x05499bd6a9c3b6ecf4bd2ec5be00ae3f1e5597daca258b2a27c58b330a90cb28?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0x31732175d3f3b35c9c2a38e841bcd485085edf79e7f3c532ec7997c4993c0192 +Transaction Link: https://mumbai.polygonscan.com/tx/0x31732175d3f3b35c9c2a38e841bcd485085edf79e7f3c532ec7997c4993c0192 + +Gas Used (Account or Paymaster): 229741 +Gas Used (Transaction): 231619 +``` + +## Safe Deployment with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:account:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:account:paymaster +> tsx ./pimlico/pimlico.ts account paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8ac55DeB4a707BDD62f63D98570B82736b3FBC64 +Address Link: https://mumbai.polygonscan.com/address/0x8ac55DeB4a707BDD62f63D98570B82736b3FBC64 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. +UserOperation submitted. Hash: 0xbf6edac0683e35c855467cb9822eb6d151d7826ee15404ddb618d906800092dc +UserOp Link: https://jiffyscan.xyz/userOpHash/0xbf6edac0683e35c855467cb9822eb6d151d7826ee15404ddb618d906800092dc?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xd51d026ecfa6dbafa8aac8a138badc6e3b397683117878e360bae9051a3b733a +Transaction Link: https://mumbai.polygonscan.com/tx/0xd51d026ecfa6dbafa8aac8a138badc6e3b397683117878e360bae9051a3b733a + +Gas Used (Account or Paymaster): 446245 +Gas Used (Transaction): 448172 +``` + +## Safe Deployment + Native Transfer with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:native-transfer:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:native-transfer:paymaster +> tsx ./pimlico/pimlico.ts native-transfer paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8fE158E24Aa2330F002aabB967815a817FE4F478 +Address Link: https://mumbai.polygonscan.com/address/0x8fE158E24Aa2330F002aabB967815a817FE4F478 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. +UserOperation submitted. Hash: 0x7d2ca6fba592799c4c9aa5e38ea24bd31d4516a53030b09b263341912bf819bc +UserOp Link: https://jiffyscan.xyz/userOpHash/0x7d2ca6fba592799c4c9aa5e38ea24bd31d4516a53030b09b263341912bf819bc?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xdd966b95b6625be33ae37f6c5bb1ad33798afbbd899089acad1180005d0637c4 +Transaction Link: https://mumbai.polygonscan.com/tx/0xdd966b95b6625be33ae37f6c5bb1ad33798afbbd899089acad1180005d0637c4 + +Gas Used (Account or Paymaster): 453652 +Gas Used (Transaction): 455615 +``` + +## Native Transfer with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:native-transfer:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:native-transfer:paymaster +> tsx ./pimlico/pimlico.ts native-transfer paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8fE158E24Aa2330F002aabB967815a817FE4F478 +Address Link: https://mumbai.polygonscan.com/address/0x8fE158E24Aa2330F002aabB967815a817FE4F478 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. +UserOperation submitted. Hash: 0x38fd87397a93359b50265f3bf388b0a03a5f1845b977a0a7b1fb6ac053241eb9 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x38fd87397a93359b50265f3bf388b0a03a5f1845b977a0a7b1fb6ac053241eb9?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xca2e41e24c6206011fe0d932f27a2786c7d9486c93f63d96c131c5007e2b275e +Transaction Link: https://mumbai.polygonscan.com/tx/0xca2e41e24c6206011fe0d932f27a2786c7d9486c93f63d96c131c5007e2b275e + +Gas Used (Account or Paymaster): 120998 +Gas Used (Transaction): 123064 +``` + +## Safe Deployment + ERC20 Transaction with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:erc20:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc20:paymaster +> tsx ./pimlico/pimlico.ts erc20 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8aaADBe50a15e1aFfe7D4363D4e00540E8e0db7D +Address Link: https://mumbai.polygonscan.com/address/0x8aaADBe50a15e1aFfe7D4363D4e00540E8e0db7D + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. +UserOperation submitted. Hash: 0xaa85e8c6f94695fb829541e55eda8b5b5f23a8cca4541f4a53d62b1280861736 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xaa85e8c6f94695fb829541e55eda8b5b5f23a8cca4541f4a53d62b1280861736?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xbd4c79d876ae928bbc721501029b01dbc5fc94d91d6299f548f19289f7c1c271 +Transaction Link: https://mumbai.polygonscan.com/tx/0xbd4c79d876ae928bbc721501029b01dbc5fc94d91d6299f548f19289f7c1c271 + +Gas Used (Account or Paymaster): 461859 +Gas Used (Transaction): 459014 +``` + +## ERC20 Transaction with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:erc20:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc20:paymaster +> tsx ./pimlico/pimlico.ts erc20 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x8aaADBe50a15e1aFfe7D4363D4e00540E8e0db7D +Address Link: https://mumbai.polygonscan.com/address/0x8aaADBe50a15e1aFfe7D4363D4e00540E8e0db7D + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Safe Wallet ERC20 Balance: 0 + +Minting ERC20 Tokens to Safe Wallet. + +Updated Safe Wallet ERC20 Balance: 1 + +Appropriate calldata created. +UserOperation submitted. Hash: 0xbc1283f136edac0ea47d140c2ab11568a33584021cd0530eec3a2a5515136822 +UserOp Link: https://jiffyscan.xyz/userOpHash/0xbc1283f136edac0ea47d140c2ab11568a33584021cd0530eec3a2a5515136822?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xd2b130bc2f26cfe43041f7102601425674e2cd22a6b74672b907b28e70686496 +Transaction Link: https://mumbai.polygonscan.com/tx/0xd2b130bc2f26cfe43041f7102601425674e2cd22a6b74672b907b28e70686496 + +Gas Used (Account or Paymaster): 129190 +Gas Used (Transaction): 126461 +``` + +## Safe Deployment + ERC721 Transaction with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:erc721:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc721:paymaster +> tsx ./pimlico/pimlico.ts erc721 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x07a49F28A360B7799AeEBC9907bE605daFc13a30 +Address Link: https://mumbai.polygonscan.com/address/0x07a49F28A360B7799AeEBC9907bE605daFc13a30 + +Deploying a new Safe and executing calldata passed with it (if any). + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. +UserOperation submitted. Hash: 0x9b917c637eed529c8ae13eeb00ed8fdf3aac711dea7efdac7c048ba16bd9c8e3 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x9b917c637eed529c8ae13eeb00ed8fdf3aac711dea7efdac7c048ba16bd9c8e3?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0x454a3a5a39432f7b01a70fcddfef948d20c70d2d719aea30d402d693447fa535 +Transaction Link: https://mumbai.polygonscan.com/tx/0x454a3a5a39432f7b01a70fcddfef948d20c70d2d719aea30d402d693447fa535 + +Gas Used (Account or Paymaster): 486237 +Gas Used (Transaction): 488186 +``` + +## ERC721 Transaction with Pimlico Paymaster (Gas Policy) + +``` +npm run pimlico:erc721:paymaster + +> @safe-global/4337-gas-metering@1.0.0 pimlico:erc721:paymaster +> tsx ./pimlico/pimlico.ts erc721 paymaster=true + +Signer Extracted from Private Key. + +Init Code Created. + +Counterfactual Sender Address Created: 0x07a49F28A360B7799AeEBC9907bE605daFc13a30 +Address Link: https://mumbai.polygonscan.com/address/0x07a49F28A360B7799AeEBC9907bE605daFc13a30 + +The Safe is already deployed. + +Nonce for the sender received from EntryPoint. + +Appropriate calldata created. +UserOperation submitted. Hash: 0x8a82cfc2396e1031f09e8f9725e276ded8e2a741a70264d6b23aaafc314e7105 +UserOp Link: https://jiffyscan.xyz/userOpHash/0x8a82cfc2396e1031f09e8f9725e276ded8e2a741a70264d6b23aaafc314e7105?network=mumbai + +Querying for receipts... +Receipt found! +Transaction hash: 0xa148a4938de9883b2fbcd512e3c7161e78ca695843b6e535fdb5054b88872652 +Transaction Link: https://mumbai.polygonscan.com/tx/0xa148a4938de9883b2fbcd512e3c7161e78ca695843b6e535fdb5054b88872652 + +Gas Used (Account or Paymaster): 153569 +Gas Used (Transaction): 155645 +``` diff --git a/4337-gas-metering/pimlico/pimlico.ts b/4337-gas-metering/pimlico/pimlico.ts new file mode 100644 index 00000000..f3af25ef --- /dev/null +++ b/4337-gas-metering/pimlico/pimlico.ts @@ -0,0 +1,301 @@ +import dotenv from "dotenv"; +import { getAccountNonce, bundlerActions } from "permissionless"; +import { + pimlicoBundlerActions, + pimlicoPaymasterActions, +} from "permissionless/actions/pimlico"; +import { setTimeout } from "timers/promises"; +import { Client, Hash, createClient, createPublicClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { goerli, polygonMumbai } from "viem/chains"; +import { + SAFE_ADDRESSES_MAP, + getAccountAddress, + getAccountInitCode, +} from "../utils/safe"; +import { + UserOperation, + submitUserOperationPimlico, + signUserOperation, + txTypes, + createCallData, +} from "../utils/userOps"; +import { + getERC20Decimals, + getERC20Balance, + transferERC20Token, +} from "../utils/erc20"; + +dotenv.config(); +const paymaster = "pimlico"; + +const privateKey = process.env.PRIVATE_KEY; + +const entryPointAddress = process.env + .PIMLICO_ENTRYPOINT_ADDRESS as `0x${string}`; +const multiSendAddress = process.env.PIMLICO_MULTISEND_ADDRESS as `0x${string}`; + +const saltNonce = BigInt(process.env.PIMLICO_NONCE as string); + +const chain = process.env.PIMLICO_CHAIN; +const chainID = Number(process.env.PIMLICO_CHAIN_ID); + +const safeVersion = process.env.SAFE_VERSION as string; + +const rpcURL = process.env.PIMLICO_RPC_URL; +const policyID = process.env.PIMLICO_GAS_POLICY; +const apiKey = process.env.PIMLICO_API_KEY; + +const erc20PaymasterAddress = process.env + .PIMLICO_ERC20_PAYMASTER_ADDRESS as `0x${string}`; +const usdcTokenAddress = process.env + .PIMLICO_USDC_TOKEN_ADDRESS as `0x${string}`; +const erc20TokenAddress = process.env + .PIMLICO_ERC20_TOKEN_CONTRACT as `0x${string}`; +const erc721TokenAddress = process.env + .PIMLICO_ERC721_TOKEN_CONTRACT as `0x${string}`; + +const argv = process.argv.slice(2); +let usePaymaster!: boolean; +if (argv.length < 1 || argv.length > 2) { + throw new Error("TX Type Argument not passed."); +} else if (argv.length == 2 && argv[1] == "paymaster=true") { + if (policyID) { + usePaymaster = true; + } else { + throw new Error("Paymaster requires policyID to be set."); + } +} + +const txType: string = argv[0]; +if (!txTypes.includes(txType)) { + throw new Error("TX Type Argument Invalid"); +} + +const safeAddresses = ( + SAFE_ADDRESSES_MAP as Record> +)[safeVersion]; +let chainAddresses; +if (safeAddresses) { + chainAddresses = safeAddresses[chainID]; +} + +if (apiKey === undefined) { + throw new Error( + "Please replace the `apiKey` env variable with your Pimlico API key", + ); +} + +if (!privateKey) { + throw new Error( + "Please populate .env file with demo Private Key. Recommended to not use your personal private key.", + ); +} + +const signer = privateKeyToAccount(privateKey as Hash); +console.log("Signer Extracted from Private Key."); + +let bundlerClient; +let publicClient; +let pimlicoPaymasterClient; +if (chain == "goerli") { + bundlerClient = createClient({ + transport: http(`https://api.pimlico.io/v1/${chain}/rpc?apikey=${apiKey}`), + chain: goerli, + }) + .extend(bundlerActions) + .extend(pimlicoBundlerActions); + + publicClient = createPublicClient({ + transport: http(rpcURL), + chain: goerli, + }); + + pimlicoPaymasterClient = createClient({ + transport: http(`https://api.pimlico.io/v2/${chain}/rpc?apikey=${apiKey}`), + chain: goerli, + }).extend(pimlicoPaymasterActions); +} else if (chain == "mumbai") { + bundlerClient = createClient({ + transport: http(`https://api.pimlico.io/v1/${chain}/rpc?apikey=${apiKey}`), + chain: polygonMumbai, + }) + .extend(bundlerActions) + .extend(pimlicoBundlerActions); + + publicClient = createPublicClient({ + transport: http(rpcURL), + chain: polygonMumbai, + }); + + pimlicoPaymasterClient = createClient({ + transport: http(`https://api.pimlico.io/v2/${chain}/rpc?apikey=${apiKey}`), + chain: polygonMumbai, + }).extend(pimlicoPaymasterActions); +} else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); +} + +const initCode = await getAccountInitCode({ + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: usdcTokenAddress, + paymasterAddress: erc20PaymasterAddress, +}); +console.log("\nInit Code Created."); + +const senderAddress = await getAccountAddress({ + client: publicClient, + owner: signer.address, + addModuleLibAddress: chainAddresses.ADD_MODULES_LIB_ADDRESS, + safe4337ModuleAddress: chainAddresses.SAFE_4337_MODULE_ADDRESS, + safeProxyFactoryAddress: chainAddresses.SAFE_PROXY_FACTORY_ADDRESS, + safeSingletonAddress: chainAddresses.SAFE_SINGLETON_ADDRESS, + saltNonce: saltNonce, + multiSendAddress: multiSendAddress, + erc20TokenAddress: usdcTokenAddress, + paymasterAddress: erc20PaymasterAddress, +}); +console.log("\nCounterfactual Sender Address Created:", senderAddress); +if (chain == "mumbai") { + console.log( + "Address Link: https://mumbai.polygonscan.com/address/" + senderAddress, + ); +} else { + console.log( + "Address Link: https://" + chain + ".etherscan.io/address/" + senderAddress, + ); +} + +const contractCode = await publicClient.getBytecode({ address: senderAddress }); + +if (contractCode) { + console.log("\nThe Safe is already deployed."); + if (txType == "account") { + process.exit(0); + } +} else { + console.log( + "\nDeploying a new Safe and executing calldata passed with it (if any).", + ); +} + +const newNonce = await getAccountNonce(publicClient as Client, { + entryPoint: entryPointAddress, + sender: senderAddress, +}); +console.log("\nNonce for the sender received from EntryPoint."); + +const txCallData: `0x${string}` = await createCallData( + chain, + publicClient, + signer, + txType, + senderAddress, + erc20TokenAddress, + erc721TokenAddress, + paymaster, +); + +const sponsoredUserOperation: UserOperation = { + sender: senderAddress, + nonce: newNonce, + initCode: contractCode ? "0x" : initCode, + callData: txCallData, + callGasLimit: 1n, // All Gas Values will be filled by Estimation Response Data. + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: erc20PaymasterAddress, + signature: "0x", +}; + +const gasEstimate = await bundlerClient.estimateUserOperationGas({ + userOperation: sponsoredUserOperation, + entryPoint: entryPointAddress, +}); +const maxGasPriceResult = await bundlerClient.getUserOperationGasPrice(); + +sponsoredUserOperation.callGasLimit = gasEstimate.callGasLimit; +sponsoredUserOperation.verificationGasLimit = gasEstimate.verificationGasLimit; +sponsoredUserOperation.preVerificationGas = gasEstimate.preVerificationGas; +sponsoredUserOperation.maxFeePerGas = maxGasPriceResult.fast.maxFeePerGas; +sponsoredUserOperation.maxPriorityFeePerGas = + maxGasPriceResult.fast.maxPriorityFeePerGas; + +if (usePaymaster) { + const sponsorResult = await pimlicoPaymasterClient.sponsorUserOperation({ + userOperation: sponsoredUserOperation, + entryPoint: entryPointAddress, + sponsorshipPolicyId: policyID, + }); + + sponsoredUserOperation.callGasLimit = sponsorResult.callGasLimit; + sponsoredUserOperation.verificationGasLimit = + sponsorResult.verificationGasLimit; + sponsoredUserOperation.preVerificationGas = sponsorResult.preVerificationGas; + sponsoredUserOperation.paymasterAndData = sponsorResult.paymasterAndData; +} else { + // Fetch USDC balance of sender + const usdcDecimals = await getERC20Decimals(usdcTokenAddress, publicClient); + const usdcAmount = BigInt(10 ** usdcDecimals); + let senderUSDCBalance = await getERC20Balance( + usdcTokenAddress, + publicClient, + senderAddress, + ); + console.log( + "\nSafe Wallet USDC Balance:", + Number(senderUSDCBalance / usdcAmount), + ); + + if (senderUSDCBalance < BigInt(1) * usdcAmount) { + console.log( + "\nTransferring 1 USDC Token for paying the Paymaster from Sender to Safe.", + ); + await transferERC20Token( + usdcTokenAddress, + publicClient, + signer, + senderAddress, + BigInt(1) * usdcAmount, + chain, + paymaster, + ); + while (senderUSDCBalance < BigInt(1) * usdcAmount) { + await setTimeout(15000); + senderUSDCBalance = await getERC20Balance( + usdcTokenAddress, + publicClient, + senderAddress, + ); + } + console.log( + "\nUpdated Safe Wallet USDC Balance:", + Number(senderUSDCBalance / usdcAmount), + ); + } +} + +sponsoredUserOperation.signature = await signUserOperation( + sponsoredUserOperation, + signer, + chainID, + entryPointAddress, + chainAddresses.SAFE_4337_MODULE_ADDRESS, +); + +await submitUserOperationPimlico( + sponsoredUserOperation, + bundlerClient, + entryPointAddress, + chain, +); diff --git a/4337-gas-metering/tsconfig.json b/4337-gas-metering/tsconfig.json new file mode 100644 index 00000000..05e145c1 --- /dev/null +++ b/4337-gas-metering/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + /* Language and Environment */ + "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + /* Modules */ + "module": "ES2022" /* Specify what module code is generated. */, + "moduleResolution": "Node" /* Specify how TypeScript looks up a file from a given module specifier. */, + /* Interop Constraints */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + /* Completeness */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */, + "outDir": "./dist" /* Redirect output structure to the directory. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */ + }, + "exclude": ["node_modules"] +} diff --git a/4337-gas-metering/utils/erc20.ts b/4337-gas-metering/utils/erc20.ts new file mode 100644 index 00000000..2ba7f2a5 --- /dev/null +++ b/4337-gas-metering/utils/erc20.ts @@ -0,0 +1,284 @@ +import dotenv from "dotenv"; +import { + http, + Address, + encodeFunctionData, + createWalletClient, + PrivateKeyAccount, +} from "viem"; +import { goerli, polygonMumbai, sepolia } from "viem/chains"; + +dotenv.config(); +const pimlicoRPCURL = process.env.PIMLICO_RPC_URL; +const alchemyRPCURL = process.env.ALCHEMY_RPC_URL; +const gelatoRPCURL = process.env.GELATO_RPC_URL; + +export const generateApproveCallData = (paymasterAddress: Address) => { + const approveData = encodeFunctionData({ + abi: [ + { + inputs: [ + { name: "_spender", type: "address" }, + { name: "_value", type: "uint256" }, + ], + name: "approve", + outputs: [{ name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + ], + args: [ + paymasterAddress, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, + ], + }); + + return approveData; +}; + +export const generateTransferCallData = (to: Address, value: bigint) => { + const transferData = encodeFunctionData({ + abi: [ + { + inputs: [ + { name: "_to", type: "address" }, + { name: "_value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + ], + args: [to, value], + }); + + return transferData; +}; + +export const getERC20Decimals = async ( + erc20TokenAddress: string, + publicClient: any, +) => { + const erc20Decimals = await publicClient.readContract({ + abi: [ + { + inputs: [], + name: "decimals", + outputs: [{ type: "uint8" }], + type: "function", + stateMutability: "view", + }, + ], + address: erc20TokenAddress, + functionName: "decimals", + }); + + return erc20Decimals; +}; + +export const getERC20Balance = async ( + erc20TokenAddress: string, + publicClient: any, + owner: string, +) => { + const senderERC20Balance = await publicClient.readContract({ + abi: [ + { + inputs: [{ name: "_owner", type: "address" }], + name: "balanceOf", + outputs: [{ name: "balance", type: "uint256" }], + type: "function", + stateMutability: "view", + }, + ], + address: erc20TokenAddress, + functionName: "balanceOf", + args: [owner], + }); + + return senderERC20Balance; +}; + +export const mintERC20Token = async ( + erc20TokenAddress: string, + publicClient: any, + signer: PrivateKeyAccount, + to: string, + amount: bigint, + chain: string, + paymaster: string, +) => { + let walletClient; + if (paymaster == "pimlico") { + if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(pimlicoRPCURL), + }); + } else if (chain == "mumbai") { + walletClient = createWalletClient({ + account: signer, + chain: polygonMumbai, + transport: http(pimlicoRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "alchemy") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(alchemyRPCURL), + }); + } else if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(alchemyRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "gelato") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(gelatoRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else { + throw new Error( + "Current code only support Pimlico and Alchemy. Please make required changes if you want to use a different Paymaster.", + ); + } + const { request } = await publicClient.simulateContract({ + address: erc20TokenAddress, + abi: [ + { + inputs: [ + { name: "to", type: "address" }, + { name: "amount", type: "uint256" }, + ], + name: "mint", + outputs: [], + type: "function", + stateMutability: "public", + }, + ], + functionName: "mint", + args: [to, amount], + account: signer, + }); + await walletClient.writeContract(request); +}; + +export const transferERC20Token = async ( + erc20TokenAddress: string, + publicClient: any, + signer: PrivateKeyAccount, + to: string, + amount: bigint, + chain: string, + paymaster: string, +) => { + let walletClient; + if (paymaster == "pimlico") { + if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(pimlicoRPCURL), + }); + } else if (chain == "mumbai") { + walletClient = createWalletClient({ + account: signer, + chain: polygonMumbai, + transport: http(pimlicoRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "alchemy") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(alchemyRPCURL), + }); + } else if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(alchemyRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "gelato") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(gelatoRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else { + throw new Error( + "Current code only support Pimlico and Alchemy. Please make required changes if you want to use a different Paymaster.", + ); + } + + const signerERC20Bal = await getERC20Balance( + erc20TokenAddress, + publicClient, + signer.address, + ); + if (signerERC20Bal < amount) { + console.log( + "Signer does not have enough Tokens to transfer. Please transfer required funds.", + ); + process.exit(0); + } + + const { request } = await publicClient.simulateContract({ + address: erc20TokenAddress, + abi: [ + { + inputs: [ + { name: "recipient", type: "address" }, + { name: "amount", type: "uint256" }, + ], + name: "transfer", + outputs: [{ name: "", type: "bool" }], + type: "function", + stateMutability: "public", + }, + ], + functionName: "transfer", + args: [to, amount], + account: signer, + }); + await walletClient.writeContract(request); +}; diff --git a/4337-gas-metering/utils/erc721.ts b/4337-gas-metering/utils/erc721.ts new file mode 100644 index 00000000..297fd911 --- /dev/null +++ b/4337-gas-metering/utils/erc721.ts @@ -0,0 +1,19 @@ +import { Address, encodeFunctionData } from "viem"; + +export const generateMintingCallData = (to: Address) => { + const transferData = encodeFunctionData({ + abi: [ + { + inputs: [{ name: "_to", type: "address" }], + name: "safeMint", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + ], + args: [to], + }); + + return transferData; +}; diff --git a/4337-gas-metering/utils/multisend.ts b/4337-gas-metering/utils/multisend.ts new file mode 100644 index 00000000..bb8f6c63 --- /dev/null +++ b/4337-gas-metering/utils/multisend.ts @@ -0,0 +1,44 @@ +import { encodePacked, encodeFunctionData } from "viem"; + +export type InternalTx = { + to: `0x${string}`; + data: `0x${string}`; + value: bigint; + operation: 0 | 1; +}; + +const encodeInternalTransaction = (tx: InternalTx): string => { + const encoded = encodePacked( + ["uint8", "address", "uint256", "uint256", "bytes"], + [ + tx.operation, + tx.to, + tx.value, + BigInt(tx.data.slice(2).length / 2), + tx.data, + ], + ); + return encoded.slice(2); +}; + +export const encodeMultiSend = (txs: InternalTx[]): `0x${string}` => { + const data: `0x${string}` = `0x${txs + .map((tx) => encodeInternalTransaction(tx)) + .join("")}`; + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { internalType: "bytes", name: "transactions", type: "bytes" }, + ], + name: "multiSend", + outputs: [], + stateMutability: "payable", + type: "function", + }, + ], + functionName: "multiSend", + args: [data], + }); +}; diff --git a/4337-gas-metering/utils/nativeTransfer.ts b/4337-gas-metering/utils/nativeTransfer.ts new file mode 100644 index 00000000..af20285b --- /dev/null +++ b/4337-gas-metering/utils/nativeTransfer.ts @@ -0,0 +1,99 @@ +import dotenv from "dotenv"; +import { http, createWalletClient, PrivateKeyAccount } from "viem"; +import { goerli, polygonMumbai, sepolia } from "viem/chains"; +import { setTimeout } from "timers/promises"; + +dotenv.config(); +const pimlicoRPCURL = process.env.PIMLICO_RPC_URL; +const alchemyRPCURL = process.env.ALCHEMY_RPC_URL; +const gelatoRPCURL = process.env.GELATO_RPC_URL; + +export const transferETH = async ( + publicClient: any, + signer: PrivateKeyAccount, + receiver: `0x${string}`, + amount: bigint, + chain: string, + paymaster: string, +) => { + let walletClient; + if (paymaster == "pimlico") { + if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(pimlicoRPCURL), + }); + } else if (chain == "mumbai") { + walletClient = createWalletClient({ + account: signer, + chain: polygonMumbai, + transport: http(pimlicoRPCURL), + }); + } else { + throw new Error( + "For Pimlico, current code only support using Goerli. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "alchemy") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(alchemyRPCURL), + }); + } else if (chain == "goerli") { + walletClient = createWalletClient({ + account: signer, + chain: goerli, + transport: http(alchemyRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else if (paymaster == "gelato") { + if (chain == "sepolia") { + walletClient = createWalletClient({ + account: signer, + chain: sepolia, + transport: http(gelatoRPCURL), + }); + } else { + throw new Error( + "Current code only support limited networks. Please make required changes if you want to use custom network.", + ); + } + } else { + throw new Error( + "Current code only support Pimlico, Alchemy and Gelato. Please make required changes if you want to use a different Paymaster.", + ); + } + + let userETHBalance = await publicClient.getBalance({ + address: signer.address, + }); + + if (userETHBalance < amount) { + console.log( + "\nSigner does not have enough balance to deposit to Safe. Deposit atleast", + amount, + "wei.", + ); + while (userETHBalance < amount) { + await setTimeout(15000); + userETHBalance = await publicClient.getBalance({ + address: signer.address, + }); + } + console.log( + "\nSigner now have enough balance for depositing ETH to Safe Transfer.", + ); + } + + await walletClient.sendTransaction({ + to: receiver, + value: amount, + }); +}; diff --git a/4337-gas-metering/utils/safe.ts b/4337-gas-metering/utils/safe.ts new file mode 100644 index 00000000..b71b1845 --- /dev/null +++ b/4337-gas-metering/utils/safe.ts @@ -0,0 +1,360 @@ +import { + Address, + Hex, + PublicClient, + concatHex, + encodeFunctionData, + encodePacked, + getContractAddress, + hexToBigInt, + keccak256, + zeroAddress, +} from "viem"; +import { InternalTx, encodeMultiSend } from "./multisend"; +import { generateApproveCallData } from "./erc20"; + +export const SAFE_ADDRESSES_MAP = { + "1.4.1": { + "11155111": { + ADD_MODULES_LIB_ADDRESS: "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb", + SAFE_4337_MODULE_ADDRESS: "0xa581c4A4DB7175302464fF3C06380BC3270b4037", + SAFE_PROXY_FACTORY_ADDRESS: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", + SAFE_SINGLETON_ADDRESS: "0x41675C099F32341bf84BFc5382aF534df5C7461a", + }, + "5": { + ADD_MODULES_LIB_ADDRESS: "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb", + SAFE_4337_MODULE_ADDRESS: "0xa581c4A4DB7175302464fF3C06380BC3270b4037", + SAFE_PROXY_FACTORY_ADDRESS: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", + SAFE_SINGLETON_ADDRESS: "0x41675C099F32341bf84BFc5382aF534df5C7461a", + }, + "80001": { + ADD_MODULES_LIB_ADDRESS: "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb", + SAFE_4337_MODULE_ADDRESS: "0xa581c4A4DB7175302464fF3C06380BC3270b4037", + SAFE_PROXY_FACTORY_ADDRESS: "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67", + SAFE_SINGLETON_ADDRESS: "0x41675C099F32341bf84BFc5382aF534df5C7461a", + }, + }, +} as const; + +const getInitializerCode = async ({ + owner, + addModuleLibAddress, + safe4337ModuleAddress, + multiSendAddress, + erc20TokenAddress, + paymasterAddress, +}: { + owner: Address; + addModuleLibAddress: Address; + safe4337ModuleAddress: Address; + multiSendAddress: Address; + erc20TokenAddress: Address; + paymasterAddress: Address; +}) => { + const setupTxs: InternalTx[] = [ + { + to: addModuleLibAddress, + data: enableModuleCallData(safe4337ModuleAddress), + value: 0n, + operation: 1, // 1 = DelegateCall required for enabling the module + }, + ]; + + if (erc20TokenAddress != zeroAddress && paymasterAddress != zeroAddress) { + setupTxs.push({ + to: erc20TokenAddress, + data: generateApproveCallData(paymasterAddress), + value: 0n, + operation: 0, // 0 = Call + }); + } + + const multiSendCallData = encodeMultiSend(setupTxs); + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address[]", + name: "_owners", + type: "address[]", + }, + { + internalType: "uint256", + name: "_threshold", + type: "uint256", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "bytes", + name: "data", + type: "bytes", + }, + { + internalType: "address", + name: "fallbackHandler", + type: "address", + }, + { + internalType: "address", + name: "paymentToken", + type: "address", + }, + { + internalType: "uint256", + name: "payment", + type: "uint256", + }, + { + internalType: "address payable", + name: "paymentReceiver", + type: "address", + }, + ], + name: "setup", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "setup", + args: [ + [owner], + 1n, + multiSendAddress, + multiSendCallData, + safe4337ModuleAddress, + zeroAddress, + 0n, + zeroAddress, + ], + }); +}; + +export const enableModuleCallData = (safe4337ModuleAddress: `0x${string}`) => { + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address[]", + name: "modules", + type: "address[]", + }, + ], + name: "enableModules", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "enableModules", + args: [[safe4337ModuleAddress]], + }); +}; + +export const getAccountInitCode = async ({ + owner, + addModuleLibAddress, + safe4337ModuleAddress, + safeProxyFactoryAddress, + safeSingletonAddress, + saltNonce = 0n, + multiSendAddress, + erc20TokenAddress, + paymasterAddress, +}: { + owner: Address; + addModuleLibAddress: Address; + safe4337ModuleAddress: Address; + safeProxyFactoryAddress: Address; + safeSingletonAddress: Address; + saltNonce?: bigint; + multiSendAddress: Address; + erc20TokenAddress: Address; + paymasterAddress: Address; +}): Promise => { + if (!owner) throw new Error("Owner account not found"); + const initializer = await getInitializerCode({ + owner, + addModuleLibAddress, + safe4337ModuleAddress, + multiSendAddress, + erc20TokenAddress, + paymasterAddress, + }); + + const initCodeCallData = encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "_singleton", + type: "address", + }, + { + internalType: "bytes", + name: "initializer", + type: "bytes", + }, + { + internalType: "uint256", + name: "saltNonce", + type: "uint256", + }, + ], + name: "createProxyWithNonce", + outputs: [ + { + internalType: "contract SafeProxy", + name: "proxy", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "createProxyWithNonce", + args: [safeSingletonAddress, initializer, saltNonce], + }); + + return concatHex([safeProxyFactoryAddress, initCodeCallData]); +}; + +export const EIP712_SAFE_OPERATION_TYPE = { + SafeOp: [ + { type: "address", name: "safe" }, + { type: "uint256", name: "nonce" }, + { type: "bytes", name: "initCode" }, + { type: "bytes", name: "callData" }, + { type: "uint256", name: "callGasLimit" }, + { type: "uint256", name: "verificationGasLimit" }, + { type: "uint256", name: "preVerificationGas" }, + { type: "uint256", name: "maxFeePerGas" }, + { type: "uint256", name: "maxPriorityFeePerGas" }, + { type: "bytes", name: "paymasterAndData" }, + { type: "uint48", name: "validAfter" }, + { type: "uint48", name: "validUntil" }, + { type: "address", name: "entryPoint" }, + ], +}; + +export const encodeCallData = (params: { + to: Address; + value: bigint; + data: `0x${string}`; +}) => { + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "bytes", + name: "data", + type: "bytes", + }, + { + internalType: "uint8", + name: "operation", + type: "uint8", + }, + ], + name: "executeUserOp", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "executeUserOp", + args: [params.to, params.value, params.data, 0], + }); +}; + +export const getAccountAddress = async ({ + client, + owner, + addModuleLibAddress, + safe4337ModuleAddress, + safeProxyFactoryAddress, + safeSingletonAddress, + saltNonce = 0n, + multiSendAddress, + erc20TokenAddress, + paymasterAddress, +}: { + client: PublicClient; + owner: Address; + addModuleLibAddress: Address; + safe4337ModuleAddress: Address; + safeProxyFactoryAddress: Address; + safeSingletonAddress: Address; + saltNonce?: bigint; + multiSendAddress: Address; + erc20TokenAddress: Address; + paymasterAddress: Address; +}): Promise
=> { + const proxyCreationCode = await client.readContract({ + abi: [ + { + inputs: [], + name: "proxyCreationCode", + outputs: [ + { + internalType: "bytes", + name: "", + type: "bytes", + }, + ], + stateMutability: "pure", + type: "function", + }, + ], + address: safeProxyFactoryAddress, + functionName: "proxyCreationCode", + }); + + const deploymentCode = encodePacked( + ["bytes", "uint256"], + [proxyCreationCode, hexToBigInt(safeSingletonAddress)], + ); + + const initializer = await getInitializerCode({ + owner, + addModuleLibAddress, + safe4337ModuleAddress, + multiSendAddress, + erc20TokenAddress, + paymasterAddress, + }); + + const salt = keccak256( + encodePacked( + ["bytes32", "uint256"], + [keccak256(encodePacked(["bytes"], [initializer])), saltNonce], + ), + ); + + return getContractAddress({ + from: safeProxyFactoryAddress, + salt, + bytecode: deploymentCode, + opcode: "CREATE2", + }); +}; diff --git a/4337-gas-metering/utils/userOps.ts b/4337-gas-metering/utils/userOps.ts new file mode 100644 index 00000000..786b69d2 --- /dev/null +++ b/4337-gas-metering/utils/userOps.ts @@ -0,0 +1,685 @@ +import dotenv from "dotenv"; +import type { Address } from "abitype"; +import { + fromHex, + parseEther, + type Hex, + type PrivateKeyAccount, + formatEther, +} from "viem"; +import { EIP712_SAFE_OPERATION_TYPE, encodeCallData } from "./safe"; +import { Alchemy } from "alchemy-sdk"; +import { setTimeout } from "timers/promises"; +import { + generateTransferCallData, + getERC20Balance, + getERC20Decimals, + mintERC20Token, +} from "./erc20"; +import { generateMintingCallData } from "./erc721"; +import { transferETH } from "./nativeTransfer"; + +dotenv.config(); + +export const txTypes = ["account", "erc20", "erc721", "native-transfer"]; + +export type UserOperation = { + sender: Address; + nonce: bigint; + initCode: Hex; + callData: Hex; + callGasLimit: bigint; + verificationGasLimit: bigint; + preVerificationGas: bigint; + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + paymasterAndData: Hex; + signature: Hex; +}; + +// Sponsored User Operation Data +export type suoData = { + preVerificationGas: any; + callGasLimit: any; + verificationGasLimit: any; + paymasterAndData: any; + maxFeePerGas: any; + maxPriorityFeePerGas: any; +}; + +export type gasData = { + preVerificationGas: any; + callGasLimit: any; + verificationGasLimit: any; +}; + +export const submitUserOperationPimlico = async ( + userOperation: UserOperation, + bundlerClient: any, + entryPointAddress: any, + chain: string, +) => { + const userOperationHash = await bundlerClient.sendUserOperation({ + userOperation, + entryPoint: entryPointAddress, + }); + console.log(`UserOperation submitted. Hash: ${userOperationHash}`); + console.log( + `UserOp Link: https://jiffyscan.xyz/userOpHash/${userOperationHash}?network=` + + chain + + "\n", + ); + + console.log("Querying for receipts..."); + const receipt = await bundlerClient.waitForUserOperationReceipt({ + hash: userOperationHash, + }); + console.log( + `Receipt found!\nTransaction hash: ${receipt.receipt.transactionHash}`, + ); + if (chain == "mumbai") { + console.log( + `Transaction Link: https://mumbai.polygonscan.com/tx/${receipt.receipt.transactionHash}`, + ); + } else { + console.log( + `Transaction Link: https://` + + chain + + `.etherscan.io/tx/${receipt.receipt.transactionHash}`, + ); + } + console.log(`\nGas Used (Account or Paymaster): ${receipt.actualGasUsed}`); + console.log(`Gas Used (Transaction): ${receipt.receipt.gasUsed}\n`); +}; + +export const signUserOperation = async ( + userOperation: UserOperation, + signer: PrivateKeyAccount, + chainID: any, + entryPointAddress: any, + safe4337ModuleAddress: any, +) => { + const signatures = [ + { + signer: signer.address, + data: await signer.signTypedData({ + domain: { + chainId: chainID, + verifyingContract: safe4337ModuleAddress, + }, + types: EIP712_SAFE_OPERATION_TYPE, + primaryType: "SafeOp", + message: { + safe: userOperation.sender, + nonce: userOperation.nonce, + initCode: userOperation.initCode, + callData: userOperation.callData, + callGasLimit: userOperation.callGasLimit, + verificationGasLimit: userOperation.verificationGasLimit, + preVerificationGas: userOperation.preVerificationGas, + maxFeePerGas: userOperation.maxFeePerGas, + maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas, + paymasterAndData: userOperation.paymasterAndData, + validAfter: "0x000000000000", + validUntil: "0x000000000000", + entryPoint: entryPointAddress, + }, + }), + }, + ]; + + signatures.sort((left, right) => + left.signer.toLowerCase().localeCompare(right.signer.toLowerCase()), + ); + + let signatureBytes: Address = "0x000000000000000000000000"; + for (const sig of signatures) { + signatureBytes += sig.data.slice(2); + } + + return signatureBytes; +}; + +export const getGasValuesFromAlchemyPaymaster = async ( + policyID: string | undefined, + entryPointAddress: `0x${string}`, + sponsoredUserOperation: UserOperation, + chain: string, + apiKey: string, +) => { + const gasOptions = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 1, + jsonrpc: "2.0", + method: "alchemy_requestGasAndPaymasterAndData", + params: [ + { + policyId: policyID, + entryPoint: entryPointAddress, + dummySignature: sponsoredUserOperation.signature, + userOperation: { + sender: sponsoredUserOperation.sender, + nonce: "0x" + sponsoredUserOperation.nonce.toString(16), + initCode: sponsoredUserOperation.initCode, + callData: sponsoredUserOperation.callData, + }, + }, + ], + }), + }; + + let rv; + let responseValues; + await fetch( + "https://eth-" + chain + ".g.alchemy.com/v2/" + apiKey, + gasOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + console.log("\nReceived Paymaster Data from Alchemy."); + + if (responseValues && responseValues["result"]) { + rv = responseValues["result"] as suoData; + } + return rv; +}; + +export const getFeeValuesFromAlchemy = async ( + chain: string, + apiKey: string, +) => { + const feeOptions = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 1, + jsonrpc: "2.0", + method: "rundler_maxPriorityFeePerGas", + }), + }; + + let responseValues; + await fetch( + "https://eth-" + chain + ".g.alchemy.com/v2/" + apiKey, + feeOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + console.log("\nReceived Fee Data from Alchemy."); + + let rvFee; + if (responseValues && responseValues["result"]) { + rvFee = responseValues["result"] as bigint; + } + return rvFee as bigint; +}; + +export const getMaxFeePerGas = async ( + alchemy: Alchemy, + maxPriorityFeePerGas: bigint, +) => { + let maxFeePerGas; + + // Get the latest Block Number + const latestBlockNum = await alchemy.core.getBlockNumber(); + + // Get latest Block Details + const rvBlock = await alchemy.core.getBlock(latestBlockNum); + if (rvBlock && rvBlock.baseFeePerGas) { + maxFeePerGas = + ((BigInt(rvBlock.baseFeePerGas._hex) + BigInt(maxPriorityFeePerGas)) * + 15n) / + 10n; // Adding a buffer. Recommended is atleast 50%. + // https://docs.alchemy.com/reference/bundler-api-fee-logic + } + + return ("0x" + maxFeePerGas?.toString(16)) as any; +}; + +export const getGasValuesFromAlchemy = async ( + entryPointAddress: `0x${string}`, + sponsoredUserOperation: UserOperation, + chain: string, + apiKey: string, +) => { + const gasOptions = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 1, + jsonrpc: "2.0", + method: "eth_estimateUserOperationGas", + params: [ + { + sender: sponsoredUserOperation.sender, + nonce: "0x" + sponsoredUserOperation.nonce.toString(16), + initCode: sponsoredUserOperation.initCode, + callData: sponsoredUserOperation.callData, + callGasLimit: "0x1", + verificationGasLimit: "0x1", + preVerificationGas: "0x1", + maxFeePerGas: sponsoredUserOperation.maxFeePerGas.toString(16), + maxPriorityFeePerGas: + sponsoredUserOperation.maxPriorityFeePerGas.toString(16), + signature: sponsoredUserOperation.signature, + paymasterAndData: "0x", + }, + entryPointAddress, + ], + }), + }; + + let responseValues; + await fetch( + "https://eth-" + chain + ".g.alchemy.com/v2/" + apiKey, + gasOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + console.log("\nReceived Gas Data from Alchemy."); + + let rvGas; + if (responseValues && responseValues["result"]) { + rvGas = responseValues["result"] as gasData; + } + + return rvGas; +}; + +export const submitUserOperationAlchemy = async ( + entryPointAddress: `0x${string}`, + sponsoredUserOperation: UserOperation, + chain: string, + apiKey: string, +) => { + const options = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 1, + jsonrpc: "2.0", + method: "eth_sendUserOperation", + params: [ + { + sender: sponsoredUserOperation.sender, + nonce: "0x" + sponsoredUserOperation.nonce.toString(16), + initCode: sponsoredUserOperation.initCode, + callData: sponsoredUserOperation.callData, + callGasLimit: sponsoredUserOperation.callGasLimit.toString(16), + verificationGasLimit: + sponsoredUserOperation.verificationGasLimit.toString(16), + preVerificationGas: + sponsoredUserOperation.preVerificationGas.toString(16), + maxFeePerGas: sponsoredUserOperation.maxFeePerGas.toString(16), + maxPriorityFeePerGas: + sponsoredUserOperation.maxPriorityFeePerGas.toString(16), + signature: sponsoredUserOperation.signature, + paymasterAndData: sponsoredUserOperation.paymasterAndData, + }, + entryPointAddress, + ], + }), + }; + + let responseValues; + await fetch("https://eth-" + chain + ".g.alchemy.com/v2/" + apiKey, options) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + + if (responseValues && responseValues["result"]) { + console.log("UserOperation submitted. Hash:", responseValues["result"]); + console.log( + "UserOp Link: https://jiffyscan.xyz/userOpHash/" + + responseValues["result"] + + "?network=" + + chain, + ); + + const hashOptions = { + method: "POST", + headers: { + accept: "application/json", + "content-type": "application/json", + }, + body: JSON.stringify({ + id: 1, + jsonrpc: "2.0", + method: "eth_getUserOperationReceipt", + params: [responseValues["result"]], + entryPoint: entryPointAddress, + }), + }; + let runOnce = true; + + while (responseValues["result"] == null || runOnce) { + await setTimeout(25000); + await fetch( + "https://eth-" + chain + ".g.alchemy.com/v2/" + apiKey, + hashOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + runOnce = false; + } + + if ( + responseValues["result"] && + responseValues["result"]["receipt"]["transactionHash"] + ) { + console.log( + "\nTransaction Link: https://" + + chain + + ".etherscan.io/tx/" + + responseValues["result"]["receipt"]["transactionHash"], + ); + const actualGasUsed = fromHex( + responseValues["result"]["actualGasUsed"], + "number", + ); + const gasUsed = fromHex( + responseValues["result"]["receipt"]["gasUsed"], + "number", + ); + console.log(`\nGas Used (Account or Paymaster): ${actualGasUsed}`); + console.log(`Gas Used (Transaction): ${gasUsed}\n`); + } else { + console.log("\n" + responseValues["error"]); + } + } else { + if (responseValues && responseValues["error"]["message"]) { + console.log("\n" + responseValues["error"]["message"]); + } + } +}; + +export const createCallData = async ( + chain: string, + publicClient: any, + signer: PrivateKeyAccount, + txType: string, + senderAddress: `0x${string}`, + erc20TokenAddress: `0x${string}`, + erc721TokenAddress: `0x${string}`, + paymaster: string, +) => { + let txCallData!: `0x${string}`; + if (txType == "account") { + txCallData = encodeCallData({ + to: senderAddress, + data: "0x", + value: 0n, + }); + } else if (txType == "erc20") { + // Token Configurations + const erc20Decimals = await getERC20Decimals( + erc20TokenAddress, + publicClient, + ); + const erc20Amount = BigInt(10 ** erc20Decimals); + let senderERC20Balance = await getERC20Balance( + erc20TokenAddress, + publicClient, + senderAddress, + ); + console.log( + "\nSafe Wallet ERC20 Balance:", + Number(senderERC20Balance / erc20Amount), + ); + + // Trying to mint tokens (Make sure ERC20 Token Contract is mintable by anyone). + if (senderERC20Balance < erc20Amount) { + console.log("\nMinting ERC20 Tokens to Safe Wallet."); + await mintERC20Token( + erc20TokenAddress, + publicClient, + signer, + senderAddress, + erc20Amount, + chain, + paymaster, + ); + + while (senderERC20Balance < erc20Amount) { + await setTimeout(15000); + senderERC20Balance = await getERC20Balance( + erc20TokenAddress, + publicClient, + senderAddress, + ); + } + console.log( + "\nUpdated Safe Wallet ERC20 Balance:", + Number(senderERC20Balance / erc20Amount), + ); + } + + txCallData = encodeCallData({ + to: erc20TokenAddress, + data: generateTransferCallData(signer.address, erc20Amount), // transfer() function call with corresponding data. + value: 0n, + }); + } else if (txType == "erc721") { + txCallData = encodeCallData({ + to: erc721TokenAddress, + data: generateMintingCallData(signer.address), // safeMint() function call with corresponding data. + value: 0n, + }); + } else if (txType == "native-transfer") { + const weiToSend = parseEther("0.000001"); + let safeETHBalance = await publicClient.getBalance({ + address: senderAddress, + }); + if (safeETHBalance < weiToSend) { + console.log( + "\nTransferring", + formatEther(weiToSend - safeETHBalance), + "ETH to Safe for native transfer.", + ); + await transferETH( + publicClient, + signer, + senderAddress, + weiToSend - safeETHBalance, + chain, + paymaster, + ); + while (safeETHBalance < weiToSend) { + await setTimeout(30000); // Sometimes it takes time to index. + safeETHBalance = await publicClient.getBalance({ + address: senderAddress, + }); + } + console.log("\nTransferred required ETH for the native transfer."); + } + + txCallData = encodeCallData({ + to: signer.address, + data: "0x", + value: weiToSend, + }); + } + console.log("\nAppropriate calldata created."); + return txCallData; +}; + +export const getGasValuesFromGelato = async ( + entryPointAddress: `0x${string}`, + sponsoredUserOperation: UserOperation, + chainID: number, + apiKey: string, +) => { + const gasOptions = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 0, + jsonrpc: "2.0", + method: "eth_estimateUserOperationGas", + params: [ + { + sender: sponsoredUserOperation.sender, + nonce: "0x" + sponsoredUserOperation.nonce.toString(16), + initCode: sponsoredUserOperation.initCode, + callData: sponsoredUserOperation.callData, + signature: sponsoredUserOperation.signature, + paymasterAndData: "0x", + }, + entryPointAddress, + ], + }), + }; + + let responseValues; + await fetch( + `https://api.gelato.digital//bundlers/${chainID}/rpc?sponsorApiKey=${apiKey}`, + gasOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + console.log("\nReceived Gas Data from Gelato."); + + let rvGas; + if (responseValues && responseValues["result"]) { + rvGas = responseValues["result"] as gasData; + } + + return rvGas; +}; + +export const submitUserOperationGelato = async ( + entryPointAddress: `0x${string}`, + sponsoredUserOperation: UserOperation, + chain: string, + chainID: number, + apiKey: string, +) => { + const options = { + method: "POST", + headers: { accept: "application/json", "content-type": "application/json" }, + body: JSON.stringify({ + id: 0, + jsonrpc: "2.0", + method: "eth_sendUserOperation", + params: [ + { + sender: sponsoredUserOperation.sender, + nonce: "0x" + sponsoredUserOperation.nonce.toString(16), + initCode: sponsoredUserOperation.initCode, + callData: sponsoredUserOperation.callData, + signature: sponsoredUserOperation.signature, + paymasterAndData: sponsoredUserOperation.paymasterAndData, + callGasLimit: sponsoredUserOperation.callGasLimit, + verificationGasLimit: sponsoredUserOperation.verificationGasLimit, + preVerificationGas: sponsoredUserOperation.preVerificationGas, + maxFeePerGas: "0x" + sponsoredUserOperation.maxFeePerGas.toString(16), + maxPriorityFeePerGas: + "0x" + sponsoredUserOperation.maxPriorityFeePerGas.toString(16), + }, + entryPointAddress, + ], + }), + }; + + let responseValues: any; + await fetch( + `https://api.gelato.digital//bundlers/${chainID}/rpc?sponsorApiKey=${apiKey}`, + options, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + + if (responseValues && responseValues["result"]) { + console.log( + "\nUserOperation submitted.\n\nGelato Relay Task ID:", + responseValues["result"], + ); + console.log( + "Gelato Relay Task Link: https://api.gelato.digital/tasks/status/" + + responseValues["result"], + ); + + const hashOptions = { + method: "POST", + headers: { + accept: "application/json", + "content-type": "application/json", + }, + body: JSON.stringify({ + id: 0, + jsonrpc: "2.0", + method: "eth_getUserOperationReceipt", + params: [responseValues["result"]], + }), + }; + + let runOnce = true; + + while (responseValues["result"] == null || runOnce) { + await setTimeout(25000); + await fetch( + `https://api.gelato.digital//bundlers/${chainID}/rpc?sponsorApiKey=${apiKey}`, + hashOptions, + ) + .then((response) => response.json()) + .then((response) => (responseValues = response)) + .catch((err) => console.error(err)); + runOnce = false; + } + + if ( + responseValues["result"] && + responseValues["result"]["receipt"]["transactionHash"] + ) { + const rvEntryPoint = + responseValues["result"]["logs"][ + responseValues["result"]["logs"].length - 2 + ]["address"]; + + if (rvEntryPoint == entryPointAddress) { + const userOpHash = + responseValues["result"]["logs"][ + responseValues["result"]["logs"].length - 2 + ]["topics"][1]; + console.log( + "\nUser OP Hash: " + + userOpHash + + "\nUserOp Link: https://jiffyscan.xyz/userOpHash/" + + userOpHash + + "?network=" + + chain, + ); + } + console.log( + "\nTransaction Link: https://" + + chain + + ".etherscan.io/tx/" + + responseValues["result"]["receipt"]["transactionHash"], + ); + const actualGasUsed = fromHex( + responseValues["result"]["actualGasUsed"], + "number", + ); + const gasUsed = fromHex( + responseValues["result"]["receipt"]["gasUsed"], + "number", + ); + console.log(`\nGas Used (Account or Paymaster): ${actualGasUsed}`); + console.log(`Gas Used (Transaction): ${gasUsed}\n`); + } else { + console.log("\n" + responseValues["error"]); + } + } else { + if (responseValues && responseValues["message"]) { + console.log("\n" + responseValues["message"]); + } + } +}; diff --git a/4337/contracts/test/TestERC721Token.sol b/4337/contracts/test/TestERC721Token.sol new file mode 100644 index 00000000..19b2d0b1 --- /dev/null +++ b/4337/contracts/test/TestERC721Token.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract XanderBlazeNFT is ERC721 { + constructor() ERC721("XanderBlazeNFT", "XBN") {} + + // @dev This can be called by anyone. + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId, ""); + } +} diff --git a/4337/src/deploy/token.ts b/4337/src/deploy/token.ts index ffe53277..3d0d6eb3 100644 --- a/4337/src/deploy/token.ts +++ b/4337/src/deploy/token.ts @@ -14,6 +14,13 @@ const deploy: DeployFunction = async ({ deployments, getNamedAccounts, network } log: true, deterministicDeployment: true, }) + + await deploy('XanderBlazeNFT', { + from: deployer, + args: [], + log: true, + deterministicDeployment: true, + }) } export default deploy diff --git a/4337/test/gas/Gas.spec.ts b/4337/test/gas/Gas.spec.ts new file mode 100644 index 00000000..62970bc7 --- /dev/null +++ b/4337/test/gas/Gas.spec.ts @@ -0,0 +1,359 @@ +import { expect } from 'chai' +import { deployments, ethers } from 'hardhat' +import { getSafe4337Module, getEntryPoint, getFactory, getAddModulesLib, getSafeL2Singleton } from '../utils/setup' +import { buildSignatureBytes, logGas } from '../../src/utils/execution' +import { buildUserOperationFromSafeUserOperation, buildSafeUserOpTransaction, signSafeOp } from '../../src/utils/userOp' +import { chainId } from '../utils/encoding' +import { Safe4337 } from '../../src/utils/safe' + +describe('Gas Metering', () => { + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture() + const { HariWillibaldToken, XanderBlazeNFT } = await deployments.run() + + const [user] = await ethers.getSigners() + const entryPoint = await getEntryPoint() + const module = await getSafe4337Module() + const proxyFactory = await getFactory() + const proxyCreationCode = await proxyFactory.proxyCreationCode() + const addModulesLib = await getAddModulesLib() + const singleton = await getSafeL2Singleton() + const safe = await Safe4337.withSigner(user.address, { + safeSingleton: await singleton.getAddress(), + entryPoint: await entryPoint.getAddress(), + erc4337module: await module.getAddress(), + proxyFactory: await proxyFactory.getAddress(), + addModulesLib: await addModulesLib.getAddress(), + proxyCreationCode, + chainId: Number(await chainId()), + }) + const erc20Token = await ethers.getContractAt('HariWillibaldToken', HariWillibaldToken.address) + const erc721Token = await ethers.getContractAt('XanderBlazeNFT', XanderBlazeNFT.address) + + return { + user, + entryPoint, + validator: module, + safe, + erc20Token, + erc721Token, + } + }) + + describe('Safe Deployment + Enabling 4337 Module', () => { + it('Safe with 4337 Module Deployment', async () => { + const { user, entryPoint, validator, safe } = await setupTests() + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + const safeOp = buildSafeUserOpTransaction( + safe.address, + safe.address, // No functions are called. + 0, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + + const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + + const userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await logGas('Safe with 4337 Module Deployment', entryPoint.executeUserOp(userOp, 0)) + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + }) + }) + + describe('Safe Deployment + Enabling 4337 Module + Native Transfers', () => { + it('Safe with 4337 Module Deployment + Native Transfer', async () => { + const { user, entryPoint, validator, safe } = await setupTests() + const amount = ethers.parseEther('0.00001') + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + await user.sendTransaction({ + to: safe.address, + value: amount, + }) + const safeBalBefore = await ethers.provider.getBalance(safe.address) + expect(safeBalBefore).to.equal(amount) + + const safeOp = buildSafeUserOpTransaction( + safe.address, + user.address, + amount, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + + const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + + const userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await logGas('Safe with 4337 Module Deployment + Native Transfer', entryPoint.executeUserOp(userOp, 0)) + + const safeBalAfter = await ethers.provider.getBalance(safe.address) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + expect(safeBalAfter).to.equal(0) + }) + + it('Safe with 4337 Module Native Transfer', async () => { + const { user, entryPoint, validator, safe } = await setupTests() + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + let safeOp = buildSafeUserOpTransaction( + safe.address, + safe.address, + 0, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + let signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + let userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await entryPoint.executeUserOp(userOp, 0) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + + // Now Native Transfer + const amount = ethers.parseEther('0.00001') + expect(await ethers.provider.getBalance(safe.address)).to.equal(0) + await user.sendTransaction({ + to: safe.address, + value: amount, + }) + expect(await ethers.provider.getBalance(safe.address)).to.equal(amount) + + safeOp = buildSafeUserOpTransaction( + safe.address, + user.address, + amount, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await logGas('Safe with 4337 Module Native Transfer', entryPoint.executeUserOp(userOp, 0)) + + expect(await ethers.provider.getBalance(safe.address)).to.equal(0) + }) + }) + + describe('Safe Deployment + Enabling 4337 Module + Token Operations', () => { + it('Safe with 4337 Module Deployment + ERC20 Token Transfer', async () => { + const { user, entryPoint, validator, safe, erc20Token } = await setupTests() + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + expect(await erc20Token.balanceOf(safe.address)).to.equal(0) + await erc20Token.transfer(safe.address, ethers.parseUnits('4.2', 18)).then((tx) => tx.wait()) + expect(await erc20Token.balanceOf(safe.address)).to.equal(ethers.parseUnits('4.2', 18)) + + const safeOp = buildSafeUserOpTransaction( + safe.address, + await erc20Token.getAddress(), + 0, + erc20Token.interface.encodeFunctionData('transfer', [user.address, await erc20Token.balanceOf(safe.address)]), + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + + const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + + const userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await logGas('Safe with 4337 Module Deployment + ERC20 Transfer', entryPoint.executeUserOp(userOp, 0)) + expect(await erc20Token.balanceOf(safe.address)).to.equal(0) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + }) + + it('Safe with 4337 Module Deployment + ERC721 Token Minting', async () => { + const { user, entryPoint, validator, safe, erc721Token } = await setupTests() + const tokenID = 1 + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + const safeOp = buildSafeUserOpTransaction( + safe.address, + await erc721Token.getAddress(), + 0, + erc721Token.interface.encodeFunctionData('safeMint', [safe.address, tokenID]), + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + const userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + expect(await erc721Token.balanceOf(safe.address)).to.equal(0) + await logGas('Safe with 4337 Module Deployment + ERC721 Transfer', entryPoint.executeUserOp(userOp, 0)) + expect(await erc721Token.balanceOf(safe.address)).to.equal(1) + expect(await erc721Token.ownerOf(tokenID)).to.equal(safe.address) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + }) + }) + + describe('Token Operations Only', () => { + it('Safe with 4337 Module ERC20 Token Transfer', async () => { + const { user, entryPoint, validator, safe, erc20Token } = await setupTests() + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + let safeOp = buildSafeUserOpTransaction( + safe.address, + safe.address, // No functions are called. + 0, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + let signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + let userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await entryPoint.executeUserOp(userOp, 0) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + + // Now Token Transfer + expect(await erc20Token.balanceOf(safe.address)).to.equal(0) + await erc20Token.transfer(safe.address, ethers.parseUnits('4.2', 18)).then((tx) => tx.wait()) + expect(await erc20Token.balanceOf(safe.address)).to.equal(ethers.parseUnits('4.2', 18)) + + safeOp = buildSafeUserOpTransaction( + safe.address, + await erc20Token.getAddress(), + 0, + erc20Token.interface.encodeFunctionData('transfer', [user.address, await erc20Token.balanceOf(safe.address)]), + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await logGas('Safe with 4337 Module ERC20 Transfer', entryPoint.executeUserOp(userOp, 0)) + + expect(await erc20Token.balanceOf(safe.address)).to.equal(0) + }) + + it('Safe with 4337 Module ERC721 Token Minting', async () => { + const { user, entryPoint, validator, safe, erc721Token } = await setupTests() + + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0) + + let safeOp = buildSafeUserOpTransaction( + safe.address, + safe.address, // No functions are called. + 0, + '0x', + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + let signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + let userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + await entryPoint.executeUserOp(userOp, 0) + expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.not.equal(0) + + // Now ERC721 Token Transfer + const tokenID = 1 + + safeOp = buildSafeUserOpTransaction( + safe.address, + await erc721Token.getAddress(), + 0, + erc721Token.interface.encodeFunctionData('safeMint', [safe.address, tokenID]), + await entryPoint.getNonce(safe.address, 0), + await entryPoint.getAddress(), + false, + false, + { + initCode: safe.getInitCode(), + }, + ) + signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())]) + userOp = buildUserOperationFromSafeUserOperation({ + safeOp, + signature, + }) + + expect(await erc721Token.balanceOf(safe.address)).to.equal(0) + await logGas('Safe with 4337 Module ERC721 Transfer', entryPoint.executeUserOp(userOp, 0)) + expect(await erc721Token.balanceOf(safe.address)).to.equal(1) + expect(await erc721Token.ownerOf(tokenID)).to.equal(safe.address) + }) + }) +})