-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); |