mirror of
https://github.com/luxfi/eoa-contracts.git
synced 2026-10-30 16:58:32 +00:00
217 lines
7.0 KiB
TypeScript
217 lines
7.0 KiB
TypeScript
import { expect } from "chai";
|
|
import { ethers, deployments, waffle } from "hardhat";
|
|
import { encodeTransfer } from "../utils/testUtils";
|
|
import {
|
|
getEntryPoint,
|
|
getSmartAccountImplementation,
|
|
getSmartAccountFactory,
|
|
getMockToken,
|
|
getEcdsaOwnershipRegistryModule,
|
|
getSmartAccountWithModule,
|
|
getVerifyingPaymaster,
|
|
} from "../utils/setupHelper";
|
|
import { makeEcdsaModuleUserOp, fillAndSign } from "../utils/userOp";
|
|
|
|
describe("UserOps", async () => {
|
|
const [
|
|
deployer,
|
|
smartAccountOwner,
|
|
alice,
|
|
charlie,
|
|
verifiedSigner,
|
|
notEnabledModule,
|
|
] = waffle.provider.getWallets();
|
|
|
|
const setupTests = deployments.createFixture(async ({ deployments }) => {
|
|
await deployments.fixture();
|
|
|
|
const mockToken = await getMockToken();
|
|
|
|
const ecdsaModule = await getEcdsaOwnershipRegistryModule();
|
|
const EcdsaOwnershipRegistryModule = await ethers.getContractFactory(
|
|
"EcdsaOwnershipRegistryModule"
|
|
);
|
|
|
|
const ecdsaOwnershipSetupData =
|
|
EcdsaOwnershipRegistryModule.interface.encodeFunctionData(
|
|
"initForSmartAccount",
|
|
[await smartAccountOwner.getAddress()]
|
|
);
|
|
|
|
const smartAccountDeploymentIndex = 0;
|
|
|
|
const userSA = await getSmartAccountWithModule(
|
|
ecdsaModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex
|
|
);
|
|
|
|
await deployer.sendTransaction({
|
|
to: userSA.address,
|
|
value: ethers.utils.parseEther("10"),
|
|
});
|
|
|
|
await mockToken.mint(userSA.address, ethers.utils.parseEther("1000000"));
|
|
|
|
return {
|
|
entryPoint: await getEntryPoint(),
|
|
smartAccountImplementation: await getSmartAccountImplementation(),
|
|
smartAccountFactory: await getSmartAccountFactory(),
|
|
mockToken: mockToken,
|
|
ecdsaModule: ecdsaModule,
|
|
userSA: userSA,
|
|
verifyingPaymaster: await getVerifyingPaymaster(deployer, verifiedSigner),
|
|
};
|
|
});
|
|
|
|
describe("validateUserOp ", async () => {
|
|
it("MOVED: Can validate a userOp via proper Authorization Module", async () => {
|
|
// moved to /test/bundler-integration/smart-account/SA.UserOps.specs.ts
|
|
});
|
|
|
|
it("Reverts when trying to forward validateUserOp flow to the enabled module that doesnt implement proper interface", async () => {
|
|
const { entryPoint, mockToken, userSA, ecdsaModule } = await setupTests();
|
|
|
|
const MockInvalidAuthModule = await ethers.getContractFactory(
|
|
"MockInvalidAuthModule"
|
|
);
|
|
const mockInvalidAuthModule = await MockInvalidAuthModule.deploy();
|
|
|
|
const userOpEnableModule = await makeEcdsaModuleUserOp(
|
|
"enableModule",
|
|
[mockInvalidAuthModule.address],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaModule.address
|
|
);
|
|
await entryPoint.handleOps([userOpEnableModule], alice.address);
|
|
expect(
|
|
await userSA.isModuleEnabled(mockInvalidAuthModule.address)
|
|
).to.equal(true);
|
|
const charlieTokenBalanceBefore = await mockToken.balanceOf(
|
|
charlie.address
|
|
);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("0.5345");
|
|
|
|
const SmartAccount = await ethers.getContractFactory("SmartAccount");
|
|
const txnDataAA1 = SmartAccount.interface.encodeFunctionData(
|
|
"execute_ncC",
|
|
[
|
|
mockToken.address,
|
|
ethers.utils.parseEther("0"),
|
|
encodeTransfer(charlie.address, tokenAmountToTransfer.toString()),
|
|
]
|
|
);
|
|
const userOp = await fillAndSign(
|
|
{
|
|
sender: userSA.address,
|
|
callData: txnDataAA1,
|
|
},
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
"nonce",
|
|
true
|
|
);
|
|
// add invalid validator module address to the signature
|
|
const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[userOp.signature, mockInvalidAuthModule.address]
|
|
);
|
|
userOp.signature = signatureWithModuleAddress;
|
|
|
|
await expect(
|
|
entryPoint.handleOps([userOp], alice.address, { gasLimit: 10000000 })
|
|
).to.be.revertedWith("FailedOp");
|
|
expect(await mockToken.balanceOf(charlie.address)).to.equal(
|
|
charlieTokenBalanceBefore
|
|
);
|
|
});
|
|
|
|
it("Reverts when trying to forward validateUserOp flow to the SENTINEL_MODULES", async () => {
|
|
const { entryPoint, mockToken, userSA } = await setupTests();
|
|
|
|
const sentinelAddress = "0x0000000000000000000000000000000000000001";
|
|
const charlieTokenBalanceBefore = await mockToken.balanceOf(
|
|
charlie.address
|
|
);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("0.5345");
|
|
|
|
const SmartAccount = await ethers.getContractFactory("SmartAccount");
|
|
const txnDataAA1 = SmartAccount.interface.encodeFunctionData(
|
|
"execute_ncC",
|
|
[
|
|
mockToken.address,
|
|
ethers.utils.parseEther("0"),
|
|
encodeTransfer(charlie.address, tokenAmountToTransfer.toString()),
|
|
]
|
|
);
|
|
const userOp = await fillAndSign(
|
|
{
|
|
sender: userSA.address,
|
|
callData: txnDataAA1,
|
|
},
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
"nonce",
|
|
true
|
|
);
|
|
// add sentinel module address to the signature
|
|
const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[userOp.signature, sentinelAddress]
|
|
);
|
|
userOp.signature = signatureWithModuleAddress;
|
|
|
|
await expect(
|
|
entryPoint.handleOps([userOp], alice.address, { gasLimit: 10000000 })
|
|
).to.be.revertedWith("FailedOp");
|
|
expect(await mockToken.balanceOf(charlie.address)).to.equal(
|
|
charlieTokenBalanceBefore
|
|
);
|
|
});
|
|
|
|
it("Reverts when trying to forward validateUserOp flow to the module that is not enabled", async () => {
|
|
const { entryPoint, mockToken, userSA } = await setupTests();
|
|
|
|
const charlieTokenBalanceBefore = await mockToken.balanceOf(
|
|
charlie.address
|
|
);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("0.5345");
|
|
|
|
const SmartAccount = await ethers.getContractFactory("SmartAccount");
|
|
const txnDataAA1 = SmartAccount.interface.encodeFunctionData(
|
|
"execute_ncC",
|
|
[
|
|
mockToken.address,
|
|
ethers.utils.parseEther("0"),
|
|
encodeTransfer(charlie.address, tokenAmountToTransfer.toString()),
|
|
]
|
|
);
|
|
const userOp = await fillAndSign(
|
|
{
|
|
sender: userSA.address,
|
|
callData: txnDataAA1,
|
|
},
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
"nonce",
|
|
true
|
|
);
|
|
// add not enabled module address to the signature
|
|
const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[userOp.signature, notEnabledModule.address]
|
|
);
|
|
userOp.signature = signatureWithModuleAddress;
|
|
|
|
await expect(
|
|
entryPoint.handleOps([userOp], alice.address, { gasLimit: 10000000 })
|
|
).to.be.revertedWith("FailedOp");
|
|
expect(await mockToken.balanceOf(charlie.address)).to.equal(
|
|
charlieTokenBalanceBefore
|
|
);
|
|
});
|
|
});
|
|
});
|