Update plugin contracts to use 4337 v0.7.0

This commit is contained in:
JohnGuilding
2024-03-28 12:43:23 +00:00
parent 10aaae67d6
commit 4345a1442c
37 changed files with 486 additions and 193 deletions

View File

@@ -1,7 +1,8 @@
{
"gasFactor": "1",
"port": "3000",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"network": "http://127.0.0.1:8545",
"entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"beneficiary": "0xd21934eD8eAf27a67f0A70042Af50A1D6d195E81",
"minBalance": "1",
"mnemonic": "./workdir/mnemonic.txt",

View File

@@ -4,6 +4,7 @@ out = "out"
libs = [
"lib",
]
solc_version = "0.8.23"
allow_paths = [
"../../primitives",

View File

@@ -16,7 +16,7 @@ function getRemappings() {
const config: HardhatUserConfig = {
solidity: {
version: "0.8.19",
version: "0.8.23",
settings: {
optimizer: {
enabled: true,

View File

@@ -13,7 +13,7 @@
"@getwax/circuits": "../zkp"
},
"devDependencies": {
"@account-abstraction/contracts": "^0.6.0",
"@account-abstraction/contracts": "0.7.0",
"@account-abstraction/utils": "^0.6.0",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
"@nomicfoundation/hardhat-ethers": "^3.0.0",

View File

@@ -3,7 +3,6 @@ import DeterministicDeployer from "../lib-ts/deterministic-deployer/Deterministi
import {
SimulateTxAccessor__factory,
SafeProxyFactory__factory,
TokenCallbackHandler__factory,
CompatibilityFallbackHandler__factory,
CreateCall__factory,
MultiSend__factory,
@@ -16,6 +15,8 @@ import {
BLSOpen__factory,
} from "../typechain-types";
import makeDevFaster from "../test/e2e/utils/makeDevFaster";
import { TokenCallbackHandler__factory } from "../typechain-types/factories/lib/safe-contracts/contracts/handler/TokenCallbackHandler__factory";
import bundlerConfig from "./../config/bundler.config.json";
async function deploy() {
const { NODE_URL, MNEMONIC } = process.env;
@@ -36,12 +37,6 @@ async function deploy() {
MultiSendCallOnly__factory,
SignMessageLib__factory,
BLSOpen__factory,
DeterministicDeployer.link(BLSSignatureAggregator__factory, [
{
"lib/account-abstraction/contracts/samples/bls/lib/BLSOpen.sol:BLSOpen":
deployer.calculateAddress(BLSOpen__factory, []),
},
]),
];
for (const contractFactory of contractFactories) {
@@ -51,6 +46,25 @@ async function deploy() {
console.log(`deployed ${contractName} to ${await contract.getAddress()}`);
}
const blsSignatureAggregatorFactory = DeterministicDeployer.link(
BLSSignatureAggregator__factory,
[
{
"lib/account-abstraction/contracts/samples/bls/lib/BLSOpen.sol:BLSOpen":
deployer.calculateAddress(BLSOpen__factory, []),
},
],
);
const blsSignatureAggregator = await deployer.connectOrDeploy(
blsSignatureAggregatorFactory,
[bundlerConfig.entryPoint],
);
console.log(
`deployed ${
BLSSignatureAggregator__factory.name.split("_")[0]
} to ${await blsSignatureAggregator.getAddress()}`,
);
const safeDeployer = await DeterministicDeployer.initSafeVersion(wallet);
const safeContractFactories = [

View File

@@ -21,7 +21,7 @@
DOCKER_NETWORK=packages-plugins-docker-network
GETH_IMAGE=ethereum/client-go:v1.13.5
BUNDLER_IMAGE=patched-bundler-727838b
BUNDLER_IMAGE=accountabstraction/bundler:0.7.0
GETH_CONTAINER=geth${RANDOM}
BUNDLER_CONTAINER=bundler${RANDOM}
@@ -78,13 +78,9 @@ docker exec ${GETH_CONTAINER} geth \
# Deploy common contracts
yarn hardhat run "${SCRIPT_DIR}/deploy_all.ts" --network localhost
if ! docker images | grep -q "${BUNDLER_IMAGE}"; then
echo "Building '${BUNDLER_IMAGE}'..."
# Build the Docker image from the Dockerfile
docker build -f "${SCRIPT_DIR}/${BUNDLER_IMAGE}.dockerfile" -t "${BUNDLER_IMAGE}" .
fi
# Start ERC-4337 bundler
docker pull ${BUNDLER_IMAGE}
docker run --rm -i --name ${BUNDLER_CONTAINER} -p 3000:3000 -v "$PWD"/config:/app/workdir:ro --network=${DOCKER_NETWORK} ${BUNDLER_IMAGE} \
--network http://${GETH_CONTAINER}:8545 \
&

View File

@@ -4,7 +4,7 @@ pragma abicoder v2;
import {HandlerContext} from "safe-contracts/contracts/handler/HandlerContext.sol";
import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {BLS} from "account-abstraction/contracts/samples/bls/lib/hubble-contracts/contracts/libs/BLS.sol";
import {IBLSAccount} from "account-abstraction/contracts/samples/bls/IBLSAccount.sol";
@@ -59,7 +59,7 @@ contract SafeBlsPlugin is Safe4337Base, IBLSAccount {
}
function _validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 /* userOpHash */
) internal view override returns (uint256) {
uint256 initCodeLen = userOp.initCode.length;

View File

@@ -4,11 +4,12 @@ pragma abicoder v2;
import {HandlerContext} from "safe-contracts/contracts/handler/HandlerContext.sol";
import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {BLS} from "account-abstraction/contracts/samples/bls/lib/hubble-contracts/contracts/libs/BLS.sol";
import {IBLSAccount} from "account-abstraction/contracts/samples/bls/IBLSAccount.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {Safe4337Base, ISafe} from "./utils/Safe4337Base.sol";
import {Safe4337Base, ISafe, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {WaxLib as W} from "../compression/WaxLib.sol";
import {IDecompressor} from "../compression/decompressors/IDecompressor.sol";
@@ -38,12 +39,10 @@ contract SafeCompressionPlugin is Safe4337Base, IBLSAccount {
_decompressor = decompressorParam;
}
function decompressAndPerform(
bytes calldata stream
) public {
function decompressAndPerform(bytes calldata stream) public {
_requireFromEntryPoint();
(W.Action[] memory actions,) = _decompressor.decompress(stream);
(W.Action[] memory actions, ) = _decompressor.decompress(stream);
ISafe safe = _currentSafe();
@@ -57,9 +56,7 @@ contract SafeCompressionPlugin is Safe4337Base, IBLSAccount {
}
}
function setDecompressor(
IDecompressor decompressorParam
) public {
function setDecompressor(IDecompressor decompressorParam) public {
_requireFromCurrentSafeOrEntryPoint();
_decompressor = decompressorParam;
}
@@ -77,14 +74,15 @@ contract SafeCompressionPlugin is Safe4337Base, IBLSAccount {
}
function _validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 /* userOpHash */
) internal view override returns (uint256) {
uint256 initCodeLen = userOp.initCode.length;
if (initCodeLen > 0) {
bytes32 claimedKeyHash =
keccak256(userOp.initCode[initCodeLen - 128:]);
bytes32 claimedKeyHash = keccak256(
userOp.initCode[initCodeLen - 128:]
);
// See appendKeyToInitCode.ts for a detailed explanation.
require(

View File

@@ -2,11 +2,12 @@
pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {Safe4337Base} from "./utils/Safe4337Base.sol";
import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
interface ISafe {
function enableModule(address module) external;
@@ -88,11 +89,11 @@ contract SafeECDSAPlugin is Safe4337Base {
}
function _validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal view override returns (uint256 validationData) {
address keyOwner = ecdsaOwnerStorage[msg.sender].owner;
bytes32 hash = userOpHash.toEthSignedMessageHash();
bytes32 hash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
if (keyOwner != hash.recover(userOp.signature)) {
return SIG_VALIDATION_FAILED;

View File

@@ -2,6 +2,7 @@
pragma solidity ^0.8.0;
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
contract Enum {
enum Operation {
@@ -114,7 +115,9 @@ contract SafeECDSARecoveryPlugin {
}
bytes32 currentOwnerHash = keccak256(abi.encodePacked(currentOwner));
bytes32 ethSignedHash = currentOwnerHash.toEthSignedMessageHash();
bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(
currentOwnerHash
);
if (newOwner != ethSignedHash.recover(newOwnerSignature))
revert INVALID_NEW_OWNER_SIGNATURE();

View File

@@ -3,10 +3,10 @@ pragma solidity >=0.8.0 <0.9.0;
import {HandlerContext} from "safe-contracts/contracts/handler/HandlerContext.sol";
import {BaseAccount} from "account-abstraction/contracts/core/BaseAccount.sol";
import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {WebAuthn} from "../primitives/WebAuthn.sol";
import {Safe4337Base} from "./utils/Safe4337Base.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
interface ISafe {
function enableModule(address module) external;
@@ -66,7 +66,7 @@ contract SafeWebAuthnPlugin is Safe4337Base, WebAuthn {
}
function _validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 /*userOpHash*/
) internal override returns (uint256 validationData) {
bytes calldata authenticatorData;

View File

@@ -4,8 +4,8 @@ pragma abicoder v2;
import {IGroth16Verifier} from "./interface/IGroth16Verifier.sol";
import {ISafe} from "./interface/ISafe.sol";
import {Safe4337Base} from "./utils/Safe4337Base.sol";
import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
struct ZKPPasswordOwnerStorage {
address owner;
@@ -76,7 +76,7 @@ contract SafeZKPPasswordPlugin is Safe4337Base {
}
function _validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal view override returns (uint256 validationData) {
// TODO (merge-ok) There is likely a more efficient way to encode this

View File

@@ -3,7 +3,7 @@ pragma solidity >=0.7.0 <0.9.0;
pragma abicoder v2;
import {HandlerContext} from "safe-contracts/contracts/handler/HandlerContext.sol";
import {SIG_VALIDATION_FAILED} from "account-abstraction/contracts/core/Helpers.sol";
import {BaseAccount} from "account-abstraction/contracts/core/BaseAccount.sol";
interface ISafe {

View File

@@ -7,13 +7,14 @@ import {
import { setupTests } from "./utils/setupTests";
import receiptOf from "./utils/receiptOf";
import {
generateInitCodeAndAddress,
generateFactoryParamsAndAddress,
createUserOperation,
} from "./utils/createUserOp";
import { getSigners } from "./utils/getSigners";
import getBlsUserOpHash from "./utils/getBlsUserOpHash";
import appendKeyToInitCode from "./utils/appendKeyToInitCode";
import setupBls from "./utils/setupBls";
import { packUserOp } from "./utils/userOpUtils";
const BLS_PRIVATE_KEY =
"0xdbe3d601b1b25c42c50015a87855fdce00ea9b3a7e33c92d31c69aeb70708e08";
@@ -58,41 +59,46 @@ describe("SafeBlsPlugin", () => {
[recipientAddress, transferAmount, "0x"],
);
let { initCode, deployedAddress } = await generateInitCodeAndAddress(
admin,
owner,
safeBlsPlugin,
safeSingleton,
safeProxyFactory,
);
let { factoryParams, deployedAddress } =
await generateFactoryParamsAndAddress(
admin,
owner,
safeBlsPlugin,
safeSingleton,
safeProxyFactory,
);
initCode = appendKeyToInitCode(initCode, blsSigner.pubkey);
factoryParams.factoryData = appendKeyToInitCode(
factoryParams.factoryData,
blsSigner.pubkey,
);
const unsignedUserOperation = await createUserOperation(
provider,
bundlerProvider,
deployedAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
"0x",
);
const packedUserOperation = packUserOp(unsignedUserOperation);
const blsUserOpHash = getBlsUserOpHash(
(await provider.getNetwork()).chainId,
await blsSignatureAggregator.getAddress(),
blsSigner.pubkey,
unsignedUserOperation,
packedUserOperation,
entryPointAddress,
);
const aggReportedUserOpHash = await blsSignatureAggregator.getUserOpHash(
unsignedUserOperation,
);
const aggReportedUserOpHash =
await blsSignatureAggregator.getUserOpHash(packedUserOperation);
expect(blsUserOpHash).to.equal(aggReportedUserOpHash);
const userOperation = {
...unsignedUserOperation,
...packedUserOperation,
signature: solidityPacked(
["uint256[2]"],
[blsSigner.sign(blsUserOpHash)],

View File

@@ -1,5 +1,5 @@
import { expect } from "chai";
import { AbiCoder, ethers, solidityPacked } from "ethers";
import { ethers, solidityPacked } from "ethers";
import {
AddressRegistry__factory,
EntryPoint__factory,
@@ -12,6 +12,7 @@ import { setupTests } from "./utils/setupTests";
import { createUserOperation } from "./utils/createUserOp";
import setupBls from "./utils/setupBls";
import getBlsUserOpHash from "./utils/getBlsUserOpHash";
import { packUserOp } from "./utils/userOpUtils";
const BLS_PRIVATE_KEY =
"0xdbe3d601b1b25c42c50015a87855fdce00ea9b3a7e33c92d31c69aeb70708e08";
@@ -41,7 +42,6 @@ describe("SafeCompressionPlugin", () => {
SafeCompressionFactory__factory,
[],
);
await safeCompressionFactory.waitForDeployment();
const addressRegistry = await deployer.connectOrDeploy(
AddressRegistry__factory,
@@ -92,11 +92,14 @@ describe("SafeCompressionPlugin", () => {
[compressedActions],
);
// Note: initCode is not used because we need to create both the safe
// Note: factoryParams is not used because we need to create both the safe
// proxy and the plugin, and 4337 currently only allows one contract
// creation in this step. Since we need an extra step anyway, it's simpler
// to do the whole create outside of 4337.
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
// Native tokens for the pre-fund
await receiptOf(
@@ -112,27 +115,28 @@ describe("SafeCompressionPlugin", () => {
provider,
bundlerProvider,
accountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
"0x",
);
const packedUserOperation = packUserOp(unsignedUserOperation);
const blsUserOpHash = getBlsUserOpHash(
(await provider.getNetwork()).chainId,
await blsSignatureAggregator.getAddress(),
blsSigner.pubkey,
unsignedUserOperation,
packedUserOperation,
entryPointAddress,
);
const aggReportedUserOpHash = await blsSignatureAggregator.getUserOpHash(
unsignedUserOperation,
);
const aggReportedUserOpHash =
await blsSignatureAggregator.getUserOpHash(packedUserOperation);
expect(blsUserOpHash).to.equal(aggReportedUserOpHash);
const userOperation = {
...unsignedUserOperation,
...packedUserOperation,
signature: solidityPacked(
["uint256[2]"],
[blsSigner.sign(blsUserOpHash)],

View File

@@ -64,11 +64,14 @@ describe("SafeECDSAPlugin", () => {
[recipient.address, transferAmount, "0x00"],
);
// Note: initCode is not used because we need to create both the safe
// Note: factoryParams is not used because we need to create both the safe
// proxy and the plugin, and 4337 currently only allows one contract
// creation in this step. Since we need an extra step anyway, it's simpler
// to do the whole create outside of 4337.
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
// Send userOp
await createAndSendUserOpWithEcdsaSig(
@@ -76,7 +79,7 @@ describe("SafeECDSAPlugin", () => {
bundlerProvider,
owner,
accountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,

View File

@@ -170,7 +170,10 @@ describe("SafeECDSARecoveryPlugin", () => {
[await recoveryPlugin.getAddress(), "0x00", addRecoveryAccountCalldata],
);
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const dummySignature = await owner.signMessage("dummy sig");
// Send userOp to add recovery account
@@ -179,7 +182,7 @@ describe("SafeECDSARecoveryPlugin", () => {
bundlerProvider,
owner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -236,7 +239,7 @@ describe("SafeECDSARecoveryPlugin", () => {
bundlerProvider,
newEcdsaPluginSigner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -317,7 +320,10 @@ describe("SafeECDSARecoveryPlugin", () => {
[await recoveryPlugin.getAddress(), "0x00", addRecoveryAccountCalldata],
);
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const dummySignature = await owner.signMessage("dummy sig");
// Send userOp to add recovery account
@@ -326,7 +332,7 @@ describe("SafeECDSARecoveryPlugin", () => {
bundlerProvider,
owner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -380,7 +386,7 @@ describe("SafeECDSARecoveryPlugin", () => {
bundlerProvider,
guardianSigner,
guardianSimpleAccountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -403,7 +409,7 @@ describe("SafeECDSARecoveryPlugin", () => {
bundlerProvider,
newEcdsaPluginSigner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,

View File

@@ -4,7 +4,7 @@ import sendUserOpAndWait from "./utils/sendUserOpAndWait";
import { setupTests } from "./utils/setupTests";
import { SafeWebAuthnPlugin__factory } from "../../typechain-types";
import {
generateInitCodeAndAddress,
generateFactoryParamsAndAddress,
createUserOperation,
} from "./utils/createUserOp";
import { getSigners } from "./utils/getSigners";
@@ -91,14 +91,14 @@ describe.skip("SafeWebAuthnPlugin", () => {
"execTransaction",
[recipientAddress, transferAmount, "0x00"],
);
const { initCode, deployedAddress } = await generateInitCodeAndAddress(
admin,
owner,
safeWebAuthnPlugin,
safeSingleton,
safeProxyFactory,
);
const { factoryParams, deployedAddress } =
await generateFactoryParamsAndAddress(
admin,
owner,
safeWebAuthnPlugin,
safeSingleton,
safeProxyFactory,
);
const recipientBalanceBefore = await provider.getBalance(recipientAddress);
@@ -106,7 +106,7 @@ describe.skip("SafeWebAuthnPlugin", () => {
provider,
bundlerProvider,
deployedAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
userOpSignature,

View File

@@ -1,4 +1,3 @@
import { getUserOpHash } from "@account-abstraction/utils";
import { ERC4337ZKPPasswordClient } from "@getwax/circuits";
import { expect } from "chai";
import { resolveProperties, ethers } from "ethers";
@@ -11,6 +10,7 @@ import {
} from "../../typechain-types";
import { setupTests } from "./utils/setupTests";
import { createUserOperation } from "./utils/createUserOp";
import { getUserOpHash } from "./utils/userOpUtils";
describe("SafeZKPPasswordPlugin", () => {
it("should pass the ERC4337 validation", async () => {
@@ -85,17 +85,20 @@ describe("SafeZKPPasswordPlugin", () => {
],
);
// Note: initCode is not used because we need to create both the safe
// Note: factoryParams is not used because we need to create both the safe
// proxy and the plugin, and 4337 currently only allows one contract
// creation in this step. Since we need an extra step anyway, it's simpler
// to do the whole create outside of 4337.
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const unsignedUserOperation = await createUserOperation(
provider,
bundlerProvider,
accountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,

View File

@@ -51,7 +51,6 @@ describe("SafeZkEmailRecoveryPlugin", () => {
SafeECDSAFactory__factory,
[],
);
await safeECDSAFactory.waitForDeployment();
const createArgs = [
safeSingleton,
@@ -94,7 +93,6 @@ describe("SafeZkEmailRecoveryPlugin", () => {
await defaultDkimRegistry.getAddress(),
],
);
await recoveryPlugin.waitForDeployment();
});
it("Should use recovery plugin via EOA and then send tx with new key.", async () => {
@@ -173,7 +171,10 @@ describe("SafeZkEmailRecoveryPlugin", () => {
[await recoveryPlugin.getAddress(), "0x00", configureRecoveryCalldata],
);
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const dummySignature = await owner.signMessage("dummy sig");
// Send userOp to add recovery account
@@ -182,7 +183,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
owner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -291,7 +292,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
newEcdsaPluginSigner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -393,7 +394,10 @@ describe("SafeZkEmailRecoveryPlugin", () => {
[await recoveryPlugin.getAddress(), "0x00", addRecoveryHashCalldata],
);
const initCode = "0x";
const factoryParams = {
factory: "0x",
factoryData: "0x",
};
const dummySignature = await owner.signMessage("dummy sig");
// Send userOp to add recovery account
@@ -402,7 +406,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
owner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -471,7 +475,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
otherAccount,
otherSimpleAccountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -508,7 +512,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
otherAccount,
otherSimpleAccountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
@@ -531,7 +535,7 @@ describe("SafeZkEmailRecoveryPlugin", () => {
bundlerProvider,
newEcdsaPluginSigner,
safeProxyAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,

View File

@@ -1,7 +1,5 @@
import { ethers, getBytes, NonceManager, Signer } from "ethers";
import { AddressZero } from "@ethersproject/constants";
import { UserOperationStruct } from "@account-abstraction/contracts";
import { getUserOpHash } from "@account-abstraction/utils";
import { SafeProxyFactory } from "../../../typechain-types/lib/safe-contracts/contracts/proxies/SafeProxyFactory";
import { Safe } from "../../../typechain-types/lib/safe-contracts/contracts/Safe";
@@ -14,10 +12,17 @@ import receiptOf from "./receiptOf";
import { calculateProxyAddress } from "./calculateProxyAddress";
import { getGasEstimates } from "./getGasEstimates";
import sendUserOpAndWait from "./sendUserOpAndWait";
import {
FactoryParams,
getUserOpHash,
PackedUserOperation,
packUserOp,
UserOperation,
} from "./userOpUtils";
type Plugin = SafeBlsPlugin | SafeWebAuthnPlugin;
export const generateInitCodeAndAddress = async (
export const generateFactoryParamsAndAddress = async (
admin: NonceManager,
owner: NonceManager,
plugin: Plugin,
@@ -58,25 +63,24 @@ export const generateInitCodeAndAddress = async (
}),
);
// The initCode contains 20 bytes of the factory address and the rest is the
// calldata to be forwarded
const initCode = ethers.concat([
factoryAddress,
safeProxyFactory.interface.encodeFunctionData("createProxyWithNonce", [
singletonAddress,
encodedInitializer,
73,
]),
]);
const factoryData = safeProxyFactory.interface.encodeFunctionData(
"createProxyWithNonce",
[singletonAddress, encodedInitializer, 73],
);
return { initCode, deployedAddress };
const factoryParams = {
factory: factoryAddress,
factoryData,
};
return { factoryParams, deployedAddress };
};
export const createUserOperation = async (
provider: ethers.JsonRpcProvider,
bundlerProvider: ethers.JsonRpcProvider,
accountAddress: string,
initCode: string,
factoryParams: FactoryParams,
userOpCallData: string,
entryPointAddress: string,
dummySignature: string,
@@ -88,16 +92,19 @@ export const createUserOperation = async (
const nonce = await entryPoint.getNonce(accountAddress, "0x00");
const nonceHex = "0x0" + nonce.toString();
const userOperationWithoutGasFields = {
let userOp: Partial<UserOperation> = {
sender: accountAddress,
nonce: nonceHex,
initCode,
callData: userOpCallData,
callGasLimit: "0x00",
paymasterAndData: "0x",
signature: dummySignature,
};
if (factoryParams.factory !== "0x") {
userOp.factory = factoryParams.factory;
userOp.factoryData = factoryParams.factoryData;
}
const {
callGasLimit,
verificationGasLimit,
@@ -107,23 +114,23 @@ export const createUserOperation = async (
} = await getGasEstimates(
provider,
bundlerProvider,
userOperationWithoutGasFields,
userOp,
entryPointAddress,
);
const unsignedUserOperation = {
sender: accountAddress,
nonce: nonceHex,
initCode,
factory: userOp.factory,
factoryData: userOp.factoryData,
callData: userOpCallData,
callGasLimit,
verificationGasLimit,
preVerificationGas,
maxFeePerGas,
maxPriorityFeePerGas,
paymasterAndData: "0x",
signature: dummySignature,
} satisfies UserOperationStruct;
} satisfies UserOperation;
return await ethers.resolveProperties(unsignedUserOperation);
};
@@ -133,7 +140,7 @@ export const createAndSendUserOpWithEcdsaSig = async (
bundlerProvider: ethers.JsonRpcProvider,
owner: Signer,
accountAddress: string,
initCode: string,
factoryParams: FactoryParams,
userOpCallData: string,
entryPointAddress: string,
dummySignature: string,
@@ -142,7 +149,7 @@ export const createAndSendUserOpWithEcdsaSig = async (
provider,
bundlerProvider,
accountAddress,
initCode,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,

View File

@@ -1,12 +1,13 @@
import { ethers, keccak256 } from "ethers";
import { ResolvedUserOp } from "./resolveUserOp";
import { solG2 } from "@thehubbleproject/bls/dist/mcl";
import { PackedUserOperation } from "./userOpUtils";
export default function getBlsUserOpHash(
chainId: bigint,
aggregatorAddress: string,
publicKey: solG2,
userOp: ResolvedUserOp,
userOp: PackedUserOperation,
entryPointAddress: string,
): string {
const abi = ethers.AbiCoder.defaultAbiCoder();
@@ -14,8 +15,14 @@ export default function getBlsUserOpHash(
return keccak256(
abi.encode(
["bytes32", "bytes32", "address", "uint256"],
[internalUserOpHash(userOp), publicKeyHash, aggregatorAddress, chainId],
["bytes32", "bytes32", "address", "uint256", "address"],
[
internalUserOpHash(userOp),
publicKeyHash,
aggregatorAddress,
chainId,
entryPointAddress,
],
),
);
}
@@ -26,7 +33,7 @@ export default function getBlsUserOpHash(
* (Also the same as UserOperationLib.hash, but *different* from EntryPoint's
* getUserOpHash and BLSSignatureAggregator's getUserOpHash.)
*/
function internalUserOpHash(userOp: ResolvedUserOp): string {
function internalUserOpHash(userOp: PackedUserOperation): string {
const abi = ethers.AbiCoder.defaultAbiCoder();
return keccak256(
@@ -36,11 +43,9 @@ function internalUserOpHash(userOp: ResolvedUserOp): string {
"uint256", // userOp.nonce,
"bytes32", // keccak256(userOp.initCode),
"bytes32", // keccak256(userOp.callData),
"uint256", // userOp.callGasLimit,
"uint256", // userOp.verificationGasLimit,
"bytes32", // userOp.accountGasLimits,
"uint256", // userOp.preVerificationGas,
"uint256", // userOp.maxFeePerGas,
"uint256", // userOp.maxPriorityFeePerGas,
"bytes32", // userOp.gasFees,
"bytes32", // keccak256(userOp.paymasterAndData),
],
[
@@ -48,11 +53,9 @@ function internalUserOpHash(userOp: ResolvedUserOp): string {
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.accountGasLimits,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas,
userOp.gasFees,
keccak256(userOp.paymasterAndData),
],
),

View File

@@ -1,14 +1,15 @@
import { ethers } from "ethers";
import { UserOperation } from "./userOpUtils";
export const getGasEstimates = async (
provider: ethers.JsonRpcProvider,
bundlerProvider: ethers.JsonRpcProvider,
userOperationWithoutGasFields: any,
partialUserOperation: Partial<UserOperation>,
entryPointAddress: string,
) => {
const gasEstimate = (await bundlerProvider.send(
"eth_estimateUserOperationGas",
[userOperationWithoutGasFields, entryPointAddress],
[partialUserOperation, entryPointAddress],
)) as {
verificationGasLimit: string;
preVerificationGas: string;

View File

@@ -1,23 +0,0 @@
import { UserOperationStruct } from "@account-abstraction/contracts";
export default async function resolveUserOp(
userOp: UserOperationStruct,
): Promise<ResolvedUserOp> {
return {
sender: await userOp.sender,
nonce: await userOp.nonce,
initCode: await userOp.initCode,
callData: await userOp.callData,
callGasLimit: await userOp.callGasLimit,
verificationGasLimit: await userOp.verificationGasLimit,
preVerificationGas: await userOp.preVerificationGas,
maxFeePerGas: await userOp.maxFeePerGas,
maxPriorityFeePerGas: await userOp.maxPriorityFeePerGas,
paymasterAndData: await userOp.paymasterAndData,
signature: await userOp.signature,
};
}
export type ResolvedUserOp = {
[K in keyof UserOperationStruct]: Awaited<UserOperationStruct[K]>;
};

View File

@@ -1,9 +1,9 @@
import { UserOperationStruct } from "@account-abstraction/contracts";
import { UserOperation } from "./userOpUtils";
import { ethers } from "ethers";
import sleep from "./sleep";
export default async function sendUserOpAndWait(
userOp: UserOperationStruct,
userOp: UserOperation,
entryPoint: string,
bundlerProvider: ethers.JsonRpcProvider,
pollingDelay = 100,

View File

@@ -22,11 +22,11 @@ export default async function setupBls(
await blsOpen.getAddress(),
},
]),
[],
[entryPointAddress],
);
await receiptOf(
blsSignatureAggregator.addStake(entryPointAddress, 100n * 86_400n, {
blsSignatureAggregator.addStake(100n * 86_400n, {
value: parseEther("1"),
}),
);

View File

@@ -0,0 +1,214 @@
import {
AbiCoder,
BigNumberish,
BytesLike,
concat,
hexlify,
isHexString,
keccak256,
} from "ethers";
import { PackedUserOperationStruct } from "../../../typechain-types/lib/account-abstraction/contracts/core/EntryPoint";
/**
* @notice these utils have been largely copied from ERC4337Utils.ts in eth-infinitism's
* bundler repo, which form part of @account-abstraction/utils. This is because
* @account-abstraction/utils v0.7.0 has not been published on npm yet. These utility
* function can be swapped out once v0.7.0 has been published.
*
* The only changes were to update ethers functionality from v5 to v6
*/
export type PackedUserOperation = PackedUserOperationStruct;
export type UserOperation = {
sender: string;
nonce: BigNumberish;
factory?: string;
factoryData?: BytesLike;
callData: BytesLike;
callGasLimit: BigNumberish;
verificationGasLimit: BigNumberish;
preVerificationGas: BigNumberish;
maxFeePerGas: BigNumberish;
maxPriorityFeePerGas: BigNumberish;
paymaster?: string;
paymasterVerificationGasLimit?: BigNumberish;
paymasterPostOpGasLimit?: BigNumberish;
paymasterData?: BytesLike;
signature: BytesLike;
};
export type FactoryParams = {
factory: string;
factoryData?: BytesLike;
};
/**
* calculate the userOpHash of a given userOperation.
* The userOpHash is a hash of all UserOperation fields, except the "signature" field.
* The entryPoint uses this value in the emitted UserOperationEvent.
* A wallet may use this value as the hash to sign (the SampleWallet uses this method)
* @param op
* @param entryPoint
* @param chainId
*/
export function getUserOpHash(
op: UserOperation,
entryPoint: string,
chainId: number,
): string {
const userOpHash = keccak256(encodeUserOp(op, true));
const defaultAbiCoder = AbiCoder.defaultAbiCoder();
const enc = defaultAbiCoder.encode(
["bytes32", "address", "uint256"],
[userOpHash, entryPoint, chainId],
);
return keccak256(enc);
}
/**
* abi-encode the userOperation
* @param op a PackedUserOp
* @param forSignature "true" if the hash is needed to calculate the getUserOpHash()
* "false" to pack entire UserOp, for calculating the calldata cost of putting it on-chain.
*/
export function encodeUserOp(
op1: PackedUserOperation | UserOperation,
forSignature = true,
): string {
// if "op" is unpacked UserOperation, then pack it first, before we ABI-encode it.
let op: PackedUserOperation;
if ("callGasLimit" in op1) {
op = packUserOp(op1);
} else {
op = op1;
}
const defaultAbiCoder = AbiCoder.defaultAbiCoder();
if (forSignature) {
return defaultAbiCoder.encode(
[
"address",
"uint256",
"bytes32",
"bytes32",
"bytes32",
"uint256",
"bytes32",
"bytes32",
],
[
op.sender,
op.nonce,
keccak256(op.initCode),
keccak256(op.callData),
op.accountGasLimits,
op.preVerificationGas,
op.gasFees,
keccak256(op.paymasterAndData),
],
);
} else {
// for the purpose of calculating gas cost encode also signature (and no keccak of bytes)
return defaultAbiCoder.encode(
[
"address",
"uint256",
"bytes",
"bytes",
"bytes32",
"uint256",
"bytes32",
"bytes",
"bytes",
],
[
op.sender,
op.nonce,
op.initCode,
op.callData,
op.accountGasLimits,
op.preVerificationGas,
op.gasFees,
op.paymasterAndData,
op.signature,
],
);
}
}
export function packUserOp(op: UserOperation): PackedUserOperation {
let paymasterAndData: BytesLike;
if (op.paymaster == null) {
paymasterAndData = "0x";
} else {
if (
op.paymasterVerificationGasLimit == null ||
op.paymasterPostOpGasLimit == null
) {
throw new Error("paymaster with no gas limits");
}
paymasterAndData = packPaymasterData(
op.paymaster,
op.paymasterVerificationGasLimit,
op.paymasterPostOpGasLimit,
op.paymasterData,
);
}
return {
sender: op.sender,
nonce: "0x" + BigInt(op.nonce).toString(16),
initCode:
op.factory == null ? "0x" : concat([op.factory, op.factoryData ?? ""]),
callData: op.callData,
accountGasLimits: packUint(op.verificationGasLimit, op.callGasLimit),
preVerificationGas: "0x" + BigInt(op.preVerificationGas).toString(16),
gasFees: packUint(op.maxPriorityFeePerGas, op.maxFeePerGas),
paymasterAndData,
signature: op.signature,
};
}
export function packPaymasterData(
paymaster: string,
paymasterVerificationGasLimit: BigNumberish,
postOpGasLimit: BigNumberish,
paymasterData?: BytesLike,
): BytesLike {
return concat([
paymaster,
packUint(paymasterVerificationGasLimit, postOpGasLimit),
paymasterData ?? "0x",
]);
}
export function packUint(high128: BigNumberish, low128: BigNumberish): string {
high128 = BigInt(high128);
low128 = BigInt(low128);
const packed = "0x" + ((high128 << 128n) + low128).toString(16);
return hexZeroPad(packed, 32);
}
/**
* @notice Copied from ethers v5.7 as the ethers v6 equivalent behaves
* in a way the bundler doesn't expect and an error is thrown
*/
export function hexZeroPad(value: BytesLike, length: number): string {
if (typeof value !== "string") {
value = hexlify(value);
} else if (!isHexString(value)) {
console.log("invalid hex string", "value", value);
}
if (value.length > 2 * length + 2) {
console.log("value out of range", "value", arguments[1]);
}
while (value.length < 2 * length + 2) {
value = "0x0" + value.substring(2);
}
return value;
}

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
pragma solidity ^0.8.19;
import {KernelFactory} from "kernel/src/factory/KernelFactory.sol";
import {Kernel, UserOperation, ECDSA} from "kernel/src/Kernel.sol";

View File

@@ -7,7 +7,6 @@ import {TestHelper} from "../utils/TestHelper.sol";
import {SafeBlsPluginHarness} from "../utils/SafeBlsPluginHarness.sol";
import {SafeBlsPlugin} from "../../../src/safe/SafeBlsPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
import {UserOperation, UserOperationLib} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {BLSSignatureAggregator} from "account-abstraction/contracts/samples/bls/BLSSignatureAggregator.sol";
/* solhint-disable func-name-mixedcase */
@@ -21,7 +20,7 @@ contract SafeBlsPluginTest is TestHelper {
function setUp() public {
uint256[4] memory blsPublicKey = getBlsPublicKey();
blsSignatureAggregator = new BLSSignatureAggregator();
blsSignatureAggregator = new BLSSignatureAggregator(entryPointAddress);
safeBlsPlugin = new SafeBlsPluginHarness(
entryPointAddress,

View File

@@ -7,7 +7,6 @@ import {TestHelper} from "../utils/TestHelper.sol";
import {SafeECDSAPluginHarness} from "../utils/SafeECDSAPluginHarness.sol";
import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
import {UserOperation, UserOperationLib} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
/* solhint-disable func-name-mixedcase */

View File

@@ -9,6 +9,7 @@ import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
import {Safe} from "safe-contracts/contracts/Safe.sol";
import {SafeProxy} from "safe-contracts/contracts/proxies/SafeProxy.sol";
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
/* solhint-disable func-name-mixedcase */
/* solhint-disable private-vars-leading-underscore */
@@ -292,7 +293,10 @@ contract SafeECDSARecoveryPluginTest is TestHelper {
bytes32 invalidOwnerHash = keccak256(
abi.encodePacked("invalid address hash")
);
bytes32 ethSignedHash = invalidOwnerHash.toEthSignedMessageHash();
bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(
invalidOwnerHash
);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(newOwner, ethSignedHash);
bytes memory newOwnerSignature = abi.encodePacked(r, s, v); // note the order here is different from the tuple above.
@@ -331,7 +335,9 @@ contract SafeECDSARecoveryPluginTest is TestHelper {
Vm.Wallet memory newOwner = Carol;
bytes32 currentOwnerHash = keccak256(abi.encodePacked(owner));
bytes32 ethSignedHash = currentOwnerHash.toEthSignedMessageHash();
bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(
currentOwnerHash
);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(newOwner, ethSignedHash);
bytes memory newOwnerSignature = abi.encodePacked(r, s, v); // note the order here is different from the tuple above.

View File

@@ -7,7 +7,7 @@ import {TestHelper} from "../utils/TestHelper.sol";
import {SafeWebAuthnPluginHarness} from "../utils/SafeWebAuthnPluginHarness.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/SafeWebAuthnPlugin.sol";
import {Safe4337Base} from "../../../src/safe/utils/Safe4337Base.sol";
import {UserOperation, UserOperationLib} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
/* solhint-disable func-name-mixedcase */
@@ -26,7 +26,7 @@ contract SafeWebAuthnPluginTest is TestHelper {
function test_validateSignature_ValidSignature() public {
// Arrange
UserOperation memory userOp = buildUserOp();
PackedUserOperation memory userOp = buildUserOp();
bytes32 userOpHash = entryPoint.getUserOpHash(userOp);
uint256 expectedValidationData = 0;
@@ -45,7 +45,7 @@ contract SafeWebAuthnPluginTest is TestHelper {
function test_validateSignature_InvalidSignature() public {
// Arrange
UserOperation memory userOp = buildUserOp();
PackedUserOperation memory userOp = buildUserOp();
bytes32 userOpHash = entryPoint.getUserOpHash(userOp);
uint256 expectedValidationData = 1;

View File

@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {SafeBlsPlugin} from "../../../src/safe/SafeBlsPlugin.sol";
/** Helper contract to expose internal functions for testing */

View File

@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {SafeECDSAPlugin} from "../../../src/safe/SafeECDSAPlugin.sol";
/** Helper contract to expose internal functions for testing */

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {SafeWebAuthnPlugin} from "../../../src/safe/SafeWebAuthnPlugin.sol";
/** Helper contract to expose internal functions for testing */
@@ -12,7 +12,7 @@ contract SafeWebAuthnPluginHarness is SafeWebAuthnPlugin {
) SafeWebAuthnPlugin(entryPointAddress, pubKey) {}
function exposed_validateSignature(
UserOperation calldata userOp,
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external returns (uint256) {
return _validateSignature(userOp, userOpHash);

View File

@@ -2,7 +2,7 @@
pragma solidity ^0.8.12;
import "forge-std/Test.sol";
import {EntryPoint, UserOperation} from "account-abstraction/contracts/core/EntryPoint.sol";
import {EntryPoint, PackedUserOperation} from "account-abstraction/contracts/core/EntryPoint.sol";
/* solhint-disable private-vars-leading-underscore */
/* solhint-disable var-name-mixedcase */
@@ -26,29 +26,29 @@ abstract contract TestHelper is Test {
Dave = vm.createWallet("Dave");
}
function buildUserOp() public view returns (UserOperation memory userOp) {
function buildUserOp()
public
view
returns (PackedUserOperation memory userOp)
{
address sender = Alice.addr;
uint256 nonce = 0;
bytes memory initCode = hex"00";
bytes memory callData = hex"00";
uint256 callGasLimit = 0;
uint256 verificationGasLimit = 0;
bytes32 accountGasLimits = hex"00";
uint256 preVerificationGas = 0;
uint256 maxFeePerGas = 0;
uint256 maxPriorityFeePerGas = 0;
bytes32 gasFees = hex"00";
bytes memory paymasterAndData = hex"00";
bytes memory signature = hex"00";
userOp = UserOperation(
userOp = PackedUserOperation(
sender,
nonce,
initCode,
callData,
callGasLimit,
verificationGasLimit,
accountGasLimits,
preVerificationGas,
maxFeePerGas,
maxPriorityFeePerGas,
gasFees,
paymasterAndData,
signature
);

View File

@@ -7,6 +7,14 @@
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
"@account-abstraction/contracts@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@account-abstraction/contracts/-/contracts-0.7.0.tgz#f0f60d73e8d6ef57b794b07f8ab7b4779a3814c6"
integrity sha512-Bt/66ilu3u8I9+vFZ9fTd+cWs55fdb9J5YKfrhsrFafH1drkzwuCSL/xEot1GGyXXNJLQuXbMRztQPyelNbY1A==
dependencies:
"@openzeppelin/contracts" "^5.0.0"
"@uniswap/v3-periphery" "^1.4.3"
"@account-abstraction/contracts@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@account-abstraction/contracts/-/contracts-0.6.0.tgz#7188a01839999226e6b2796328af338329543b76"
@@ -865,11 +873,21 @@
"@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1"
"@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1"
"@openzeppelin/contracts@3.4.2-solc-0.7":
version "3.4.2-solc-0.7"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz#38f4dbab672631034076ccdf2f3201fab1726635"
integrity sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA==
"@openzeppelin/contracts@^4.7.3":
version "4.9.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364"
integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==
"@openzeppelin/contracts@^5.0.0":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210"
integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==
"@pkgr/utils@^2.3.1":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc"
@@ -1249,6 +1267,32 @@
"@typescript-eslint/types" "6.7.0"
eslint-visitor-keys "^3.4.1"
"@uniswap/lib@^4.0.1-alpha":
version "4.0.1-alpha"
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-4.0.1-alpha.tgz#2881008e55f075344675b3bca93f020b028fbd02"
integrity sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA==
"@uniswap/v2-core@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425"
integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==
"@uniswap/v3-core@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0"
integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==
"@uniswap/v3-periphery@^1.4.3":
version "1.4.4"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.4.4.tgz#d2756c23b69718173c5874f37fd4ad57d2f021b7"
integrity sha512-S4+m+wh8HbWSO3DKk4LwUCPZJTpCugIsHrWR86m/OrUyvSqGDTXKFfc2sMuGXCZrD1ZqO3rhQsKgdWg3Hbb2Kw==
dependencies:
"@openzeppelin/contracts" "3.4.2-solc-0.7"
"@uniswap/lib" "^4.0.1-alpha"
"@uniswap/v2-core" "^1.0.1"
"@uniswap/v3-core" "^1.0.0"
base64-sol "1.0.1"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -1623,6 +1667,11 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
base64-sol@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/base64-sol/-/base64-sol-1.0.1.tgz#91317aa341f0bc763811783c5729f1c2574600f6"
integrity sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg==
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"