Skip to content

Commit

Permalink
✅ Add SmartAccount test file
Browse files Browse the repository at this point in the history
  • Loading branch information
Aboudjem committed Feb 19, 2024
1 parent 5720389 commit 5738568
Show file tree
Hide file tree
Showing 2 changed files with 288 additions and 0 deletions.
111 changes: 111 additions & 0 deletions test/foundry/Account.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.24 <0.9.0;

import "./Imports.sol";

contract SmartAccountTest is PRBTest, StdCheats {
SmartAccount public smartAccount;

function setUp() public {
smartAccount = new SmartAccount();
}

function testAccountId() public {
string memory expectedAccountId = "ModularSmartAccount";
// Assuming `accountId` is set in the `SmartAccount` constructor or through some initialization function
assertEq(smartAccount.accountId(), expectedAccountId);
}

function testSupportsAccountMode() public {
// Example encodedMode, replace with actual data
bytes32 encodedMode = keccak256("exampleMode");
// Assuming the SmartAccount contract has logic to support certain modes
assertTrue(smartAccount.supportsAccountMode(encodedMode));
}

function testSupportsModule() public {
uint256 moduleTypeId = 1; // Example module type ID
// Assuming the SmartAccount contract has logic to support certain module types
assertTrue(smartAccount.supportsModule(moduleTypeId));
}

function testInstallAndCheckModule(
uint256 dummyModuleType,
address dummyModuleAddress,
bytes calldata dummyInitData
)
public
{
vm.assume(dummyModuleAddress != address(0));
vm.assume(dummyModuleType != 0);
smartAccount.installModule(dummyModuleType, dummyModuleAddress, dummyInitData);
assertTrue(smartAccount.isModuleInstalled(dummyModuleType, dummyModuleAddress, dummyInitData));
}

function testUninstallAndCheckModule(
uint256 dummyModuleType,
address dummyModuleAddress,
bytes calldata dummyInitData
)
public
{
smartAccount.uninstallModule(dummyModuleType, dummyModuleAddress, dummyInitData);
// assertFalse(smartAccount.isModuleInstalled(dummyModuleType, dummyModuleAddress, "0x"));
}

function testExecute() public {
// Prepare test data
bytes32 mode = keccak256("TEST_MODE");
bytes memory executionCalldata = abi.encodeWithSignature("testFunction()");

// Since the execute function doesn't have actual logic, can't directly test its effects.
smartAccount.execute(mode, executionCalldata);
}

function testExecuteFromExecutor() public {
// Similar setup to testExecute, adapted for executeFromExecutor specifics
bytes32 mode = keccak256("EXECUTOR_MODE");
bytes memory executionCalldata = abi.encodeWithSignature("executorFunction()");

// Since the execute function doesn't have actual logic, can't directly test its effects.
bytes[] memory res = smartAccount.executeFromExecutor(mode, executionCalldata);
assertEq(res.length, 0);
}

function testExecuteUserOp() public {
// Mock a PackedUserOperation struct
PackedUserOperation memory userOp = PackedUserOperation({
sender: address(this),
nonce: 1,
initCode: "",
callData: abi.encodeWithSignature("test()"),
accountGasLimits: bytes32(0),
preVerificationGas: 0,
gasFees: bytes32(0),
paymasterAndData: "",
signature: ""
});
bytes32 userOpHash = keccak256(abi.encode(userOp));

smartAccount.executeUserOp(userOp, userOpHash);
}

function testValidateUserOp() public {
PackedUserOperation memory userOp = PackedUserOperation({
sender: address(this),
nonce: 1,
initCode: "",
callData: abi.encodeWithSignature("test()"),
accountGasLimits: bytes32(0),
preVerificationGas: 0,
gasFees: bytes32(0),
paymasterAndData: "",
signature: ""
});
bytes32 userOpHash = keccak256(abi.encode(userOp));

uint256 missingAccountFunds = 0;
uint256 res = smartAccount.validateUserOp(userOp, userOpHash, missingAccountFunds);
assertEq(res, 0);
}
}
177 changes: 177 additions & 0 deletions test/hardhat/Account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { ethers } from "hardhat";
import { expect } from "chai";
import { Signer } from "ethers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { buildUserOp, toBytes32 } from "./utils/utils";

async function deploySmartAccountFixture() {
const accounts: Signer[] = await ethers.getSigners();
const addresses = await Promise.all(
accounts.map((account) => account.getAddress()),
);

const Entrypoint = await ethers.getContractFactory("EntryPoint");
const entryPoint = await Entrypoint.deploy();
await entryPoint.waitForDeployment();

const SmartAccount = await ethers.getContractFactory("SmartAccount");
const smartAccount = await SmartAccount.deploy();
await smartAccount.waitForDeployment();

return { entryPoint, smartAccount, accounts, addresses };
}

describe("SmartAccount Contract Tests", function () {
let smartAccount: any;
let entryPoint: any;
let accounts: Signer[];
let addresses: string[];

before(async function () {
const setup = await loadFixture(deploySmartAccountFixture);
entryPoint = setup.entryPoint;
smartAccount = setup.smartAccount;
accounts = setup.accounts;
addresses = setup.addresses;
});

describe("Account Configuration", function () {
it("Should return the correct account ID", async function () {
expect(await smartAccount.accountId()).to.equal("ModularSmartAccount");
});

it("Should support specific execution modes", async function () {
expect(await smartAccount.supportsAccountMode(toBytes32("0x01"))).to.be
.true;
expect(await smartAccount.supportsAccountMode(toBytes32("0xFF"))).to.be
.true;
});

it("Should support specific module types", async function () {
expect(await smartAccount.supportsModule(1)).to.be.true;
expect(await smartAccount.supportsModule(99)).to.be.true;
});
});

describe("Module Configuration", function () {
let moduleAddress: string = ethers.hexlify(ethers.randomBytes(20));
const moduleType = "1";

it("Should allow installing a module", async function () {
expect(
await smartAccount.isModuleInstalled(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
),
).to.be.false;

await smartAccount.installModule(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
);

expect(
await smartAccount.isModuleInstalled(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
),
).to.be.true;
});

it("Should allow uninstalling a module", async function () {
expect(
await smartAccount.isModuleInstalled(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
),
).to.be.true;

await smartAccount.uninstallModule(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
);

expect(
await smartAccount.isModuleInstalled(
moduleType,
moduleAddress,
ethers.hexlify("0x"),
),
).to.be.false;
});
});

describe("Execution", function () {
it("Should successfully call execute", async function () {
const mode = toBytes32("0x01"); // Example mode
const executionData = ethers.randomBytes(20); // Example execution data

await expect(smartAccount.execute(mode, executionData)).to.not.be
.reverted;
});

it("Should successfully call executeFromExecutor", async function () {
const mode = toBytes32("0x01"); // Example mode
const executionData = ethers.randomBytes(20); // Example execution data

await expect(smartAccount.executeFromExecutor(mode, executionData)).to.not
.be.reverted;
});

it("Should successfully call executeUserOp", async function () {
// Construct a dummy user operation
const userOp = {
sender: await smartAccount.getAddress(),
nonce: 0,
initCode: "0x",
callData: "0x",
callGasLimit: 0,
executionGasLimit: 0,
verificationGasLimit: 0,
preVerificationGas: 0,
maxFeePerGas: 0,
maxPriorityFeePerGas: 0,
paymaster: ethers.ZeroAddress,
paymasterData: "0x",
signature: "0x",
};

const packedUserOp = buildUserOp(userOp);

const userOpHash = ethers.keccak256(ethers.toUtf8Bytes("dummy"));

await expect(smartAccount.executeUserOp(packedUserOp, userOpHash)).to.not
.be.reverted;
});
});

describe("Validation", function () {
it("Should validate user operations correctly", async function () {
const validUserOp = buildUserOp({
sender: await smartAccount.getAddress(),
nonce: 1,
initCode: "0x",
callData: "0x",
callGasLimit: 400_000,
executionGasLimit: 100_000,
verificationGasLimit: 400_000,
preVerificationGas: 150_000,
maxFeePerGas: 100_000,
maxPriorityFeePerGas: 100_000,
paymaster: ethers.ZeroAddress,
paymasterData: "0x",
signature: "0x",
});

const userOpHash = await entryPoint.getUserOpHash(validUserOp);
const tx = await smartAccount.validateUserOp(validUserOp, userOpHash, 0);
const receipt = await tx.wait();
expect(receipt.status).to.equal(1);
});
});
});

0 comments on commit 5738568

Please sign in to comment.