diff --git a/contracts/SafeProtocolManager.sol b/contracts/SafeProtocolManager.sol index 398c26e7..d58f8c50 100644 --- a/contracts/SafeProtocolManager.sol +++ b/contracts/SafeProtocolManager.sol @@ -26,7 +26,7 @@ contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksMana * @notice Mapping of a mapping what stores information about plugins that are enabled per account. * address (module address) => address (account address) => EnabledPluginInfo */ - mapping(address => mapping(address => PluginAccessInfo)) public enabledPlugins; + mapping(address => mapping(address => PluginAccessInfo)) internal enabledPlugins; struct PluginAccessInfo { uint8 permissions; address nextPluginPointer; @@ -464,4 +464,13 @@ contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksMana revert MissingPluginPermission(msg.sender, requiresPermissions, permission, givenPermissions); } } + + function transferPrefund( + address account, + address payable entrypoint, + uint256 missingAccountFunds + ) external override onlyEnabledPlugin(account) { + require(functionHandlers[hex"3a871cdd"][account] == msg.sender); + IAccount(account).execTransactionFromModule(entrypoint, missingAccountFunds, "", 0); + } } diff --git a/contracts/base/FunctionHandlerManager.sol b/contracts/base/FunctionHandlerManager.sol index 2083c5e6..9ac657e7 100644 --- a/contracts/base/FunctionHandlerManager.sol +++ b/contracts/base/FunctionHandlerManager.sol @@ -16,7 +16,7 @@ abstract contract FunctionHandlerManager is RegistryManager { // Storage /** @dev Mapping that stores information about an account, function selector, and address of the account. */ - mapping(bytes4 => mapping(address => address)) public functionHandlers; + mapping(bytes4 => mapping(address => address)) internal functionHandlers; // Events event FunctionHandlerChanged(address indexed account, bytes4 indexed selector, address indexed functionHandler); diff --git a/contracts/base/HooksManager.sol b/contracts/base/HooksManager.sol index 4f2620ed..079101a9 100644 --- a/contracts/base/HooksManager.sol +++ b/contracts/base/HooksManager.sol @@ -7,7 +7,7 @@ import {OnlyAccountCallable} from "./OnlyAccountCallable.sol"; import {MODULE_TYPE_HOOKS} from "../common/Constants.sol"; abstract contract HooksManager is RegistryManager { - mapping(address => address) public enabledHooks; + mapping(address => address) internal enabledHooks; struct TempHooksInfo { address hooksAddress; diff --git a/contracts/interfaces/Manager.sol b/contracts/interfaces/Manager.sol index 90ad13fc..16ffdba2 100644 --- a/contracts/interfaces/Manager.sol +++ b/contracts/interfaces/Manager.sol @@ -29,6 +29,8 @@ interface ISafeProtocolManager { * in case of succcessful execution. Empty if the call failed. */ function executeRootAccess(address account, SafeRootAccess calldata rootAccess) external returns (bytes memory data); + + function transferPrefund(address account, address payable entrypoint, uint256 missingAccountFunds) external; } interface ISafeProtocolSignatureValidatorManager { diff --git a/contracts/modules/erc4337/SafeProtocol4337Module.sol b/contracts/modules/erc4337/SafeProtocol4337Module.sol new file mode 100644 index 00000000..5bbea043 --- /dev/null +++ b/contracts/modules/erc4337/SafeProtocol4337Module.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.18; + +import {SafeProtocolAction, SafeTransaction} from "../../DataTypes.sol"; +import {PLUGIN_PERMISSION_EXECUTE_CALL} from "../../common/Constants.sol"; +import {IAccount} from "../../interfaces/Accounts.sol"; +import {ISafeProtocolManager} from "../../interfaces/Manager.sol"; +import {IERC165, ISafeProtocolFunctionHandler, ISafeProtocolPlugin} from "../../interfaces/Modules.sol"; +import {UserOperation} from "./interfaces/IERC4337.sol"; +import {ISafeProtocol4337Handler} from "./interfaces/ISafeProtocol4337Handler.sol"; + +contract SafeProtocol4337Module is ISafeProtocolFunctionHandler, ISafeProtocolPlugin { + uint256 private constant VALIDATION_SIG_SUCCESS = 0; + uint256 private constant VALIDATION_SIG_FAILURE = 0; + + address payable public immutable entrypoint; + + constructor(address payable _entrypoint) { + require(_entrypoint != address(0), "invalid entrypoint address"); + entrypoint = _entrypoint; + } + + /** + * @inheritdoc IERC165 + */ + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return + interfaceId == type(IERC165).interfaceId || + interfaceId == type(ISafeProtocolFunctionHandler).interfaceId || + interfaceId == type(ISafeProtocolPlugin).interfaceId; + } + + /** + * @inheritdoc ISafeProtocolFunctionHandler + */ + function handle(address account, address sender, uint256 value, bytes calldata data) external override returns (bytes memory result) { + require(sender == entrypoint, "unsupported entrypoint"); + require(value == 0, "not payable"); + + ISafeProtocolManager manager = ISafeProtocolManager(msg.sender); + bytes4 selector = bytes4(data[:4]); + if (selector == ISafeProtocol4337Handler(account).validateUserOp.selector) { + (UserOperation memory userOp, bytes32 userOpHash, uint256 missingAccountFunds) = abi.decode( + data[4:], + (UserOperation, bytes32, uint256) + ); + uint256 validationData = _validateUserOp(manager, account, userOp, userOpHash, missingAccountFunds); + result = abi.encode(validationData); + } else if (selector == ISafeProtocol4337Handler(account).executeUserOp.selector) { + (address to, uint256 opValue, bytes memory opData) = abi.decode(data[4:], (address, uint256, bytes)); + _executeUserOp(manager, account, to, opValue, opData); + } + } + + /** + * Validate account operation. + * @param manager the protocol manager. + * @param account the account. + * @param userOp the operation that is about to be executed. + * @param missingAccountFunds missing funds on the account's deposit in the entrypoint. + * @return validationData packaged validation data. + */ + function _validateUserOp( + ISafeProtocolManager manager, + address account, + UserOperation memory userOp, + bytes32 userOpHash, + uint256 missingAccountFunds + ) internal returns (uint256 validationData) { + require(bytes4(userOp.callData) == ISafeProtocol4337Handler(account).executeUserOp.selector, "unsupported execution"); + + if (missingAccountFunds > 0) { + manager.transferPrefund(account, entrypoint, missingAccountFunds); + } + + try IAccount(account).checkSignatures(userOpHash, "", userOp.signature) { + validationData = VALIDATION_SIG_SUCCESS; + } catch { + validationData = VALIDATION_SIG_FAILURE; + } + } + + /** + * Executes a account operation. + * @param manager the protocol manager. + * @param account the account. + * @param to target of the operation. + * @param value value of the operation. + * @param data calldata for the operation. + */ + function _executeUserOp(ISafeProtocolManager manager, address account, address to, uint256 value, bytes memory data) internal { + SafeTransaction memory transaction; + { + transaction.actions = new SafeProtocolAction[](1); + transaction.actions[0].to = payable(to); + transaction.actions[0].value = value; + transaction.actions[0].data = data; + } + manager.executeTransaction(account, transaction); + } + + /** + * @inheritdoc ISafeProtocolPlugin + */ + function metadataProvider() + external + view + override(ISafeProtocolFunctionHandler, ISafeProtocolPlugin) + returns (uint256 providerType, bytes memory location) + {} + + /** + * @inheritdoc ISafeProtocolPlugin + */ + function name() external pure override returns (string memory) { + return "Safe Protocol ERC-4337 Plugin"; + } + + /** + * @inheritdoc ISafeProtocolPlugin + */ + function version() external pure override returns (string memory) { + return "1"; + } + + /** + * @inheritdoc ISafeProtocolPlugin + */ + function requiresPermissions() external pure override returns (uint8 permissions) { + permissions = PLUGIN_PERMISSION_EXECUTE_CALL; + } +} diff --git a/contracts/modules/erc4337/interfaces/IERC4337.sol b/contracts/modules/erc4337/interfaces/IERC4337.sol new file mode 100644 index 00000000..bad8f453 --- /dev/null +++ b/contracts/modules/erc4337/interfaces/IERC4337.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.18; + +import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol"; +import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; +import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; diff --git a/contracts/modules/erc4337/interfaces/ISafeProtocol4337Handler.sol b/contracts/modules/erc4337/interfaces/ISafeProtocol4337Handler.sol new file mode 100644 index 00000000..a22152ee --- /dev/null +++ b/contracts/modules/erc4337/interfaces/ISafeProtocol4337Handler.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.18; + +import {IAccount} from "./IERC4337.sol"; + +interface ISafeProtocol4337Handler is IAccount { + function executeUserOp(address to, uint256 value, bytes calldata data) external; +} diff --git a/deploy/deploy_plugins.ts b/deploy/deploy_plugins.ts new file mode 100644 index 00000000..d851551d --- /dev/null +++ b/deploy/deploy_plugins.ts @@ -0,0 +1,37 @@ +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { MODULE_TYPE_FUNCTION_HANDLER, MODULE_TYPE_PLUGIN } from "../src/utils/constants"; + +const ENTRYPOINT = process.env.SAFE_PROTOCOL_ERC4337_ENTRYPOINT ?? "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; + +const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { ethers, deployments, getNamedAccounts } = hre; + const { deployer, owner } = await getNamedAccounts(); + const { deploy } = deployments; + + const registry = await deploy("SafeProtocolRegistry", { + from: deployer, + args: [owner], + log: true, + deterministicDeployment: true, + }); + + const module = await deploy("SafeProtocol4337Module", { + from: deployer, + args: [ENTRYPOINT], + log: true, + deterministicDeployment: true, + }); + await ethers.getContractAt("SafeProtocolRegistry", registry.address).then(async (registry) => { + const { listedAt } = await registry.check(module.address, ethers.toBeHex(ethers.MaxUint256)); + if (listedAt == 0n) { + await registry + .connect(await ethers.getSigner(owner)) + .addModule(module.address, MODULE_TYPE_PLUGIN | MODULE_TYPE_FUNCTION_HANDLER); + } + }); +}; + +deploy.tags = ["plugin"]; +export default deploy; diff --git a/package.json b/package.json index d8c36cea..7e8d3c2c 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "build:ts": "yarn rimraf dist && tsc -p tsconfig.prod.json" }, "devDependencies": { + "@account-abstraction/contracts": "^0.6.0", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", @@ -59,7 +60,7 @@ "eslint-config-prettier": "^8.8.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-prettier": "^4.2.1", - "ethers": "^6.4.0", + "ethers": "^6.8.1", "hardhat": "^2.15.0", "hardhat-deploy": "^0.11.34", "hardhat-gas-reporter": "^1.0.8", @@ -74,4 +75,4 @@ "typescript": "~5.0.0", "yargs": "^17.7.2" } -} \ No newline at end of file +} diff --git a/scripts/user_operation.ts b/scripts/user_operation.ts new file mode 100644 index 00000000..285fa63f --- /dev/null +++ b/scripts/user_operation.ts @@ -0,0 +1,92 @@ +/** + * Script to execute an ERC-4337 user operation for a test executor. + */ + +import { ethers, deployments } from "hardhat"; + +import { PLUGIN_PERMISSION_EXECUTE_CALL } from "../src/utils/constants"; +import { UserOperationStruct } from "../typechain-types/@account-abstraction/contracts/interfaces/IAccount"; + +const BUNDLER = process.env.SAFE_PROTOCOL_ERC4337_BUNDLER_URL ?? "http://localhost:3000/rpc"; +const ENTRYPOINT = process.env.SAFE_PROTOCOL_ERC4337_ENTRYPOINT ?? "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; + +async function main() { + const bundler = await bundlerRpc(BUNDLER); + + const manager = await deployments + .get("SafeProtocolManager") + .then(({ address }) => ethers.getContractAt("SafeProtocolManager", address)); + const module = await deployments + .get("SafeProtocol4337Module") + .then(({ address }) => ethers.getContractAt("SafeProtocol4337Module", address)); + const handler = await ethers.getContractAt("ISafeProtocol4337Handler", await module.getAddress()); + const entrypoint = await ethers.getContractAt("EntryPoint", ENTRYPOINT); + + const account = await ethers.deployContract("TestExecutor", [await manager.getAddress()]); + console.log(`using account ${await account.getAddress()}`); + + await fundAddress(await account.getAddress()); + await account.setModule(await manager.getAddress()); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("enablePlugin", [await module.getAddress(), PLUGIN_PERMISSION_EXECUTE_CALL]), + ); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("setFunctionHandler", [handler.validateUserOp.fragment.selector, await handler.getAddress()]), + ); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("setFunctionHandler", [handler.executeUserOp.fragment.selector, await handler.getAddress()]), + ); + + const { maxFeePerGas, maxPriorityFeePerGas } = await ethers.provider.getFeeData(); + const op = { + sender: await account.getAddress(), + nonce: ethers.toBeHex(await entrypoint.getNonce(await account.getAddress(), 0)), + initCode: "0x", + callData: handler.interface.encodeFunctionData("executeUserOp", [ethers.ZeroAddress, 0, "0x"]), + callGasLimit: ethers.toBeHex(100000), + verificationGasLimit: ethers.toBeHex(200000), + preVerificationGas: ethers.toBeHex(100000), + maxFeePerGas: ethers.toBeHex(maxFeePerGas ?? 0), + maxPriorityFeePerGas: ethers.toBeHex(maxPriorityFeePerGas ?? 0), + paymasterAndData: "0x", + signature: "0x", + }; + console.log("sending operation", op); + + const result = await bundler.sendUserOperation(op, await entrypoint.getAddress()); + console.log("sent user operation", result); +} + +async function bundlerRpc(url: string) { + const provider = new ethers.JsonRpcProvider(url, await ethers.provider.getNetwork()); + return { + sendUserOperation: async (op: UserOperationStruct, entrypoint: string) => { + return await provider._send({ + jsonrpc: "2.0", + method: "eth_sendUserOperation", + params: [op, entrypoint], + id: 4337, + }); + }, + }; +} + +async function fundAddress(to: string) { + const [signer] = await ethers.getSigners(); + const balance = await ethers.provider.getBalance(to); + const value = ethers.parseEther("1.0") - balance; + if (value > 0n) { + await signer.sendTransaction({ to, value }).then((tx) => tx.wait()); + } +} + +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); diff --git a/test/SafeProtocolManager.spec.ts b/test/SafeProtocolManager.spec.ts index 47475f7d..a59307d8 100644 --- a/test/SafeProtocolManager.spec.ts +++ b/test/SafeProtocolManager.spec.ts @@ -48,7 +48,7 @@ describe("SafeProtocolManager", async () => { expect(await safeProtocolManager.supportsInterface.staticCall("0x945b8148")).to.be.true; expect(await safeProtocolManager.supportsInterface.staticCall("0xe6d7a83a")).to.be.true; expect(await safeProtocolManager.supportsInterface.staticCall("0x01ffc9a7")).to.be.true; - expect(await safeProtocolManager.supportsInterface.staticCall("0x3f6c68ec")).to.be.true; + expect(await safeProtocolManager.supportsInterface.staticCall("0x3cd5a81c")).to.be.true; }); it("Should return false when non-supported interfaceId is passed as parameter", async () => { diff --git a/test/modules/plugins/erc4337/SafeProtocol4337Module.spec.ts b/test/modules/plugins/erc4337/SafeProtocol4337Module.spec.ts new file mode 100644 index 00000000..8ba50680 --- /dev/null +++ b/test/modules/plugins/erc4337/SafeProtocol4337Module.spec.ts @@ -0,0 +1,84 @@ +import EntryPoint from "@account-abstraction/contracts/artifacts/EntryPoint.json"; +import { Signer } from "ethers"; +import { ethers, deployments } from "hardhat"; + +import { MODULE_TYPE_PLUGIN, MODULE_TYPE_FUNCTION_HANDLER, PLUGIN_PERMISSION_EXECUTE_CALL } from "../../../../src/utils/constants"; + +describe("SafeProtocol4337Module", () => { + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture(); + const [deployer, owner, user, bundler] = await ethers.getSigners(); + + const registry = await ethers.deployContract("SafeProtocolRegistry", [owner.address]); + const manager = await ethers.deployContract("SafeProtocolManager", [owner.address, await registry.getAddress()]); + + const entrypoint = await deployEntryPoint(deployer); + const module = await ethers.deployContract("SafeProtocol4337Module", [await entrypoint.getAddress()]); + const handler = await ethers.getContractAt("ISafeProtocol4337Handler", await module.getAddress()); + + const account = await ethers.deployContract("TestExecutor", [await manager.getAddress()]); + await account.setModule(await manager.getAddress()); + + await registry.connect(owner).addModule(await module.getAddress(), MODULE_TYPE_PLUGIN | MODULE_TYPE_FUNCTION_HANDLER); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("enablePlugin", [await module.getAddress(), PLUGIN_PERMISSION_EXECUTE_CALL]), + ); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("setFunctionHandler", [ + handler.validateUserOp.fragment.selector, + await handler.getAddress(), + ]), + ); + await account.exec( + await account.getAddress(), + 0, + manager.interface.encodeFunctionData("setFunctionHandler", [ + handler.executeUserOp.fragment.selector, + await handler.getAddress(), + ]), + ); + + return { account, entrypoint, handler, user, bundler }; + }); + + describe("handleOps", () => { + it("should validate and execute user operations", async () => { + const { account, entrypoint, handler, user, bundler } = await setupTests(); + + await user.sendTransaction({ + to: await account.getAddress(), + value: ethers.parseEther("1.0"), + }); + + const { maxFeePerGas, maxPriorityFeePerGas } = await ethers.provider.getFeeData(); + await entrypoint.connect(bundler).handleOps( + [ + { + sender: await account.getAddress(), + nonce: await entrypoint.getNonce(await account.getAddress(), 0), + initCode: "0x", + callData: handler.interface.encodeFunctionData("executeUserOp", [ethers.ZeroAddress, 0, "0x"]), + callGasLimit: 100000, + verificationGasLimit: 200000, + preVerificationGas: 100000, + maxFeePerGas: maxFeePerGas ?? 0, + maxPriorityFeePerGas: maxPriorityFeePerGas ?? 0, + paymasterAndData: "0x", + signature: "0x", + }, + ], + bundler.address, + ); + }); + }); +}); + +async function deployEntryPoint(deployer: Signer) { + const { abi, bytecode } = EntryPoint; + const contract = await new ethers.ContractFactory(abi, bytecode, deployer).deploy(); + return await ethers.getContractAt("IEntryPoint", await contract.getAddress()); +} diff --git a/yarn.lock b/yarn.lock index 998ba3a1..7cb00611 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,15 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adraffy/ens-normalize@1.9.2": - version "1.9.2" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.9.2.tgz#60111a5d9db45b2e5cbb6231b0bb8d97e8659316" - integrity sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg== +"@account-abstraction/contracts@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@account-abstraction/contracts/-/contracts-0.6.0.tgz#7188a01839999226e6b2796328af338329543b76" + integrity sha512-8ooRJuR7XzohMDM4MV34I12Ci2bmxfE9+cixakRL7lA4BAwJKQ3ahvd8FbJa9kiwkUPCUNtj+/zxDQWYYalLMQ== + +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== "@babel/code-frame@^7.0.0": version "7.22.5" @@ -498,16 +503,23 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" -"@noble/hashes@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -2500,14 +2512,14 @@ ethers@^5.5.3, ethers@^5.7.1: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@^6.4.0: - version "6.6.2" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.6.2.tgz#0b6131b5fa291fec69b7ae379cb6bb2405c505a7" - integrity sha512-vyWfVAj2g7xeZIivOqlbpt7PbS2MzvJkKgsncgn4A/1xZr8Q3BznBmEBRQyPXKCgHmX4PzRQLpnYG7jl/yutMg== +ethers@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.8.1.tgz#ee2a1a39b5f62a13678f90ccd879175391d0a2b4" + integrity sha512-iEKm6zox5h1lDn6scuRWdIdFJUCGg3+/aQWu0F4K0GVyEZiktFkqrJbRjTn1FlYEPz7RKA707D6g5Kdk6j7Ljg== dependencies: - "@adraffy/ens-normalize" "1.9.2" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.7.1" + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" "@types/node" "18.15.13" aes-js "4.0.0-beta.5" tslib "2.4.0"