diff --git a/package.json b/package.json index a59cac8..1ef2203 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ }, "dependencies": { "@biconomy-devx/erc7579-msa": "^0.0.4", - "@openzeppelin/contracts": "^5.0.1", - "hardhat": "^2.20.1" + "@openzeppelin/contracts": "^5.0.2" }, "devDependencies": { "@bonadocs/docgen": "^1.0.1-alpha.1", @@ -30,7 +29,8 @@ "account-abstraction": "github:eth-infinitism/account-abstraction#develop", "chai": "^4.3.7", "codecov": "^3.8.3", - "ethers": "^6.11.1", + "ethers": "^5.7.2", + "hardhat": "^2.20.1", "hardhat-deploy": "^0.11.45", "hardhat-deploy-ethers": "^0.4.1", "hardhat-gas-reporter": "^1.0.10", diff --git a/test/hardhat/biconomy-sponsorship-paymaster-specs.ts b/test/hardhat/biconomy-sponsorship-paymaster-specs.ts index c3a48d4..07785ba 100644 --- a/test/hardhat/biconomy-sponsorship-paymaster-specs.ts +++ b/test/hardhat/biconomy-sponsorship-paymaster-specs.ts @@ -1,12 +1,9 @@ import { ethers } from "hardhat"; import { expect } from "chai"; import { - AbiCoder, - AddressLike, + BigNumber, BytesLike, Signer, - parseEther, - toBeHex, } from "ethers"; import { EntryPoint, @@ -30,26 +27,26 @@ import { } from "./utils/userOpHelpers"; import { parseValidationData } from "./utils/testUtils"; -export const AddressZero = ethers.ZeroAddress; +export const AddressZero = ethers.constants.AddressZero; const MOCK_VALID_UNTIL = "0x00000000deadbeef"; const MOCK_VALID_AFTER = "0x0000000000001234"; const MARKUP = 1100000; export const ENTRY_POINT_V7 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032"; -const coder = AbiCoder.defaultAbiCoder(); +const coder = ethers.utils.defaultAbiCoder; export async function deployEntryPoint( provider = ethers.provider, ): Promise { const epf = await (await ethers.getContractFactory("EntryPoint")).deploy(); // Retrieve the deployed contract bytecode - const deployedCode = await ethers.provider.getCode(await epf.getAddress()); + const deployedCode = await ethers.provider.getCode(await epf.address); // Use hardhat_setCode to set the contract code at the specified address await ethers.provider.send("hardhat_setCode", [ENTRY_POINT_V7, deployedCode]); - return epf.attach(ENTRY_POINT_V7) as EntryPoint; + return epf.attach(ENTRY_POINT_V7) as unknown as EntryPoint; } describe("EntryPoint with Biconomy Sponsorship Paymaster", function () { @@ -87,7 +84,7 @@ describe("EntryPoint with Biconomy Sponsorship Paymaster", function () { deployer, ).deploy( await deployer.getAddress(), - await entryPoint.getAddress(), + await entryPoint.address, offchainSignerAddress, feeCollectorAddess, ); @@ -95,66 +92,66 @@ describe("EntryPoint with Biconomy Sponsorship Paymaster", function () { smartWalletImp = await new SmartAccount__factory(deployer).deploy(); walletFactory = await new AccountFactory__factory(deployer).deploy( - await smartWalletImp.getAddress(), + await smartWalletImp.address, ); await walletFactory .connect(deployer) - .addStake(86400, { value: parseEther("2") }); + .addStake(86400, { value: ethers.utils.parseEther("2") }); const smartAccountDeploymentIndex = 0; // Module initialization data, encoded - const moduleInstallData = ethers.solidityPacked( + const moduleInstallData = ethers.utils.solidityPack( ["address"], [walletOwnerAddress], ); await walletFactory.createAccount( - await ecdsaModule.getAddress(), + await ecdsaModule.address, moduleInstallData, smartAccountDeploymentIndex, ); const expected = await walletFactory.getCounterFactualAddress( - await ecdsaModule.getAddress(), + await ecdsaModule.address, moduleInstallData, smartAccountDeploymentIndex, ); walletAddress = expected; - paymasterAddress = await paymaster.getAddress(); + paymasterAddress = await paymaster.address; await paymaster .connect(deployer) - .addStake(86400, { value: parseEther("2") }); + .addStake(86400, { value: ethers.utils.parseEther("2") }); await paymaster.depositFor(paymasterDepositorId, { - value: parseEther("1"), + value: ethers.utils.parseEther("1"), }); - await entryPoint.depositTo(paymasterAddress, { value: parseEther("1") }); + await entryPoint.depositTo(paymasterAddress, { value: ethers.utils.parseEther("1") }); await deployer.sendTransaction({ to: expected, - value: parseEther("1"), + value: ethers.utils.parseEther("1"), data: "0x", }); }); describe("Deployed Account : #validatePaymasterUserOp and #sendEmptySponsoredTx", () => { it("succeed with valid signature", async () => { - const nonceKey = ethers.zeroPadBytes(await ecdsaModule.getAddress(), 24); + const nonceKey = ethers.utils.zeroPad(await ecdsaModule.address, 24); const userOp1 = await fillAndSign( { sender: walletAddress, paymaster: paymasterAddress, - paymasterData: ethers.concat([ - ethers.zeroPadValue(paymasterDepositorId, 20), - ethers.zeroPadValue(toBeHex(MOCK_VALID_UNTIL), 6), - ethers.zeroPadValue(toBeHex(MOCK_VALID_AFTER), 6), - ethers.zeroPadValue(toBeHex(MARKUP), 4), + paymasterData: ethers.utils.hexConcat([ + ethers.utils.hexZeroPad(paymasterDepositorId, 20), + ethers.utils.hexZeroPad(MOCK_VALID_UNTIL, 6), + ethers.utils.hexZeroPad(MOCK_VALID_AFTER, 6), + ethers.utils.hexZeroPad(BigNumber.from(MARKUP).toHexString(), 4), "0x" + "00".repeat(65), ]), paymasterPostOpGasLimit: 40_000, diff --git a/test/hardhat/utils/testUtils.ts b/test/hardhat/utils/testUtils.ts index abe1776..638a095 100644 --- a/test/hardhat/utils/testUtils.ts +++ b/test/hardhat/utils/testUtils.ts @@ -1,12 +1,7 @@ import { - AbiCoder, - AddressLike, + BigNumber, BigNumberish, Contract, - Interface, - dataSlice, - parseEther, - toBeHex, } from "ethers"; import { ethers } from "hardhat"; import { EntryPoint__factory, IERC20 } from "../../../typechain-types"; @@ -21,16 +16,16 @@ export const MODE_DEFAULT = "0x00000000"; // 4 bytes export const UNUSED = "0x00000000"; // 4 bytes export const MODE_PAYLOAD = "0x00000000000000000000000000000000000000000000"; // 22 bytes -export const AddressZero = ethers.ZeroAddress; -export const HashZero = ethers.ZeroHash; -export const ONE_ETH = parseEther("1"); -export const TWO_ETH = parseEther("2"); -export const FIVE_ETH = parseEther("5"); +export const AddressZero = ethers.constants.AddressZero; +export const HashZero = ethers.constants.HashZero; +export const ONE_ETH = ethers.utils.parseEther("1"); +export const TWO_ETH = ethers.utils.parseEther("2"); +export const FIVE_ETH = ethers.utils.parseEther("5"); export const maxUint48 = 2 ** 48 - 1; export const tostr = (x: any): string => (x != null ? x.toString() : "null"); -const coder = AbiCoder.defaultAbiCoder(); +const coder = ethers.utils.defaultAbiCoder; export interface ValidationData { aggregator: string; @@ -59,7 +54,7 @@ export const Erc20 = [ "event Approval(address indexed _owner, address indexed _spender, uint256 _value)", ]; -export const Erc20Interface = new ethers.Interface(Erc20); +export const Erc20Interface = new ethers.utils.Interface(Erc20); export const encodeTransfer = ( target: string, @@ -111,7 +106,7 @@ export function rethrow(): (e: Error) => void { }; } -const decodeRevertReasonContracts = new Interface([ +const decodeRevertReasonContracts = new ethers.utils.Interface([ ...EntryPoint__factory.createInterface().fragments, "error ECDSAInvalidSignature()", ]); // .filter(f => f.type === 'error')) @@ -185,7 +180,7 @@ export async function fund( const [firstSigner] = await ethers.getSigners(); await firstSigner.sendTransaction({ to: address, - value: parseEther(amountEth), + value: ethers.utils.parseEther(amountEth), }); } @@ -209,13 +204,13 @@ export async function isDeployed(addr: string): Promise { // Getting initcode for AccountFactory which accepts one validator (with ECDSA owner required for installation) export async function getInitCode( - ownerAddress: AddressLike, - factoryAddress: AddressLike, - validatorAddress: AddressLike, + ownerAddress: string, + factoryAddress: string, + validatorAddress: string, saDeploymentIndex: number = 0, ): Promise { const AccountFactory = await ethers.getContractFactory("AccountFactory"); - const moduleInstallData = ethers.solidityPacked(["address"], [ownerAddress]); + const moduleInstallData = ethers.utils.solidityPack(["address"], [ownerAddress]); // Encode the createAccount function call with the provided parameters const factoryDeploymentData = AccountFactory.interface @@ -231,7 +226,7 @@ export async function getInitCode( export function callDataCost(data: string): number { return ethers - .getBytes(data) + .utils.arrayify(data) .map((x) => (x === 0 ? 4 : 16)) .reduce((sum, x) => sum + x); } @@ -239,15 +234,15 @@ export function callDataCost(data: string): number { export function parseValidationData( validationData: BigNumberish, ): ValidationData { - const data = ethers.zeroPadValue(toBeHex(validationData), 32); + const data = ethers.utils.hexZeroPad(BigNumber.from(Number(validationData)).toHexString(), 32); // string offsets start from left (msb) - const aggregator = dataSlice(data, 32 - 20); - let validUntil = parseInt(dataSlice(data, 32 - 26, 32 - 20)); + const aggregator = ethers.utils.hexDataSlice(data, 32 - 20); + let validUntil = parseInt(ethers.utils.hexDataSlice(data, 32 - 26, 32 - 20)); if (validUntil === 0) { validUntil = maxUint48; } - const validAfter = parseInt(dataSlice(data, 0, 6)); + const validAfter = parseInt(ethers.utils.hexDataSlice(data, 0, 6)); return { aggregator, diff --git a/test/hardhat/utils/userOpHelpers.ts b/test/hardhat/utils/userOpHelpers.ts index 50fccd5..b206509 100644 --- a/test/hardhat/utils/userOpHelpers.ts +++ b/test/hardhat/utils/userOpHelpers.ts @@ -8,21 +8,19 @@ import { PackedUserOperation, UserOperation } from "./types"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { TransactionRequest } from "@ethersproject/abstract-provider"; import { - AbiCoder, + BigNumber, BigNumberish, BytesLike, Contract, Signer, - dataSlice, - keccak256, - toBeHex, } from "ethers"; import { toGwei } from "./general"; import { callDataCost, decodeRevertReason, rethrow } from "./testUtils"; import EntryPointSimulationsJson from "../../../artifacts/account-abstraction/contracts/core/EntryPointSimulations.sol/EntryPointSimulations.json"; +import { keccak256 } from "ethers/lib/utils"; -const AddressZero = ethers.ZeroAddress; -const coder = AbiCoder.defaultAbiCoder(); +const AddressZero = ethers.constants.AddressZero; +const coder = ethers.utils.defaultAbiCoder; export function packUserOp(userOp: UserOperation): PackedUserOperation { const { @@ -35,7 +33,7 @@ export function packUserOp(userOp: UserOperation): PackedUserOperation { preVerificationGas = 2_000_000, maxFeePerGas = toGwei("20"), maxPriorityFeePerGas = toGwei("10"), - paymaster = ethers.ZeroAddress, + paymaster = ethers.constants.AddressZero, paymasterData = "0x", paymasterVerificationGasLimit = 3_00_000, paymasterPostOpGasLimit = 0, @@ -48,7 +46,7 @@ export function packUserOp(userOp: UserOperation): PackedUserOperation { ); const gasFees = packAccountGasLimits(maxPriorityFeePerGas, maxFeePerGas); let paymasterAndData = "0x"; - if (paymaster.toString().length >= 20 && paymaster !== ethers.ZeroAddress) { + if (paymaster.toString().length >= 20 && paymaster !== ethers.constants.AddressZero) { paymasterAndData = packPaymasterData( userOp.paymaster as string, paymasterVerificationGasLimit, @@ -133,10 +131,10 @@ export function packPaymasterData( postOpGasLimit: BigNumberish, paymasterData: BytesLike, ): BytesLike { - return ethers.concat([ + return ethers.utils.hexConcat([ paymaster, - ethers.zeroPadValue(toBeHex(Number(paymasterVerificationGasLimit)), 16), - ethers.zeroPadValue(toBeHex(Number(postOpGasLimit)), 16), + ethers.utils.hexZeroPad(BigNumber.from(Number(paymasterVerificationGasLimit)).toHexString(), 16), + ethers.utils.hexZeroPad(BigNumber.from(Number(postOpGasLimit)).toHexString(), 16), paymasterData, ]); } @@ -146,9 +144,9 @@ export function packAccountGasLimits( verificationGasLimit: BigNumberish, callGasLimit: BigNumberish, ): string { - return ethers.concat([ - ethers.zeroPadValue(toBeHex(Number(verificationGasLimit)), 16), - ethers.zeroPadValue(toBeHex(Number(callGasLimit)), 16), + return ethers.utils.hexConcat([ + ethers.utils.hexZeroPad(BigNumber.from(Number(verificationGasLimit)).toHexString(), 16), + ethers.utils.hexZeroPad(BigNumber.from(Number(callGasLimit)).toHexString(), 16), ]); } @@ -202,7 +200,7 @@ export async function signUserOp( ): Promise { const message = getUserOpHash(op, entryPoint, chainId); - const signature = await signer.signMessage(ethers.getBytes(message)); + const signature = await signer.signMessage(ethers.utils.arrayify(message)); return { ...op, @@ -248,8 +246,8 @@ export async function fillUserOp( const op1 = { ...op }; const provider = ethers.provider; if (op.initCode != null && op.initCode !== "0x") { - const initAddr = dataSlice(op1.initCode!, 0, 20); - const initCallData = dataSlice(op1.initCode!, 20); + const initAddr = ethers.utils.hexDataSlice(op1.initCode!, 0, 20); + const initCallData = ethers.utils.hexDataSlice(op1.initCode!, 20); if (op1.nonce == null) op1.nonce = 0; if (op1.sender == null) { if (provider == null) throw new Error("no entrypoint/provider"); @@ -260,7 +258,7 @@ export async function fillUserOp( if (op1.verificationGasLimit == null) { if (provider == null) throw new Error("no entrypoint/provider"); const initEstimate = await provider.estimateGas({ - from: await entryPoint?.getAddress(), + from: await entryPoint?.address, to: initAddr, data: initCallData, gasLimit: 10e6, @@ -281,7 +279,7 @@ export async function fillUserOp( if (provider == null) throw new Error("must have entryPoint for callGasLimit estimate"); const gasEtimated = await provider.estimateGas({ - from: await entryPoint?.getAddress(), + from: await entryPoint?.address, to: op1.sender, data: op1.callData as string, }); @@ -352,8 +350,8 @@ export async function fillAndSign( } const chainId = await provider!.getNetwork().then((net) => net.chainId); - const message = ethers.getBytes( - getUserOpHash(op2, await entryPoint!.getAddress(), Number(chainId)), + const message = ethers.utils.arrayify( + getUserOpHash(op2, await entryPoint!.address, Number(chainId)), ); let signature;