-
Notifications
You must be signed in to change notification settings - Fork 83
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
1 parent
cdaa8ea
commit 9c46126
Showing
5 changed files
with
267 additions
and
21 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,79 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
/* solhint-disable no-unused-import */ | ||
|
||
import {BaseAuthorizationModule} from "./BaseAuthorizationModule.sol"; | ||
import {EIP1271_MAGIC_VALUE} from "contracts/smart-account/interfaces/ISignatureValidator.sol"; | ||
import {UserOperation} from "@account-abstraction/contracts/interfaces/UserOperation.sol"; | ||
import {IAuthorizationModule} from "../interfaces/IAuthorizationModule.sol"; | ||
import {ISignatureValidator} from "../interfaces/ISignatureValidator.sol"; | ||
|
||
/** | ||
* @title Mock validation module which allows any user operation. | ||
* @notice DO NOT USE THIS MODULE IN PRODUCTION | ||
*/ | ||
|
||
contract MockValidationModule is BaseAuthorizationModule { | ||
string public constant NAME = "Mock Validation Module"; | ||
string public constant VERSION = "0.1.0"; | ||
|
||
function initForSmartAccount() external returns (address) { | ||
return address(this); | ||
} | ||
|
||
/// @inheritdoc IAuthorizationModule | ||
function validateUserOp( | ||
UserOperation calldata userOp, | ||
bytes32 userOpHash | ||
) external view virtual override returns (uint256) { | ||
(userOp, userOpHash); | ||
return VALIDATION_SUCCESS; | ||
} | ||
|
||
/** | ||
* @inheritdoc ISignatureValidator | ||
* @dev Validates a signature for a message. | ||
* @dev Appends smart account address to the hash to avoid replay attacks | ||
* To be called from a Smart Account. | ||
* @param dataHash Hash of the message that was signed. | ||
* @param moduleSignature Signature to be validated. | ||
* @return EIP1271_MAGIC_VALUE if signature is valid, 0xffffffff otherwise. | ||
*/ | ||
function isValidSignature( | ||
bytes32 dataHash, | ||
bytes memory moduleSignature | ||
) public view virtual override returns (bytes4) { | ||
return | ||
isValidSignatureForAddress(dataHash, moduleSignature, msg.sender); | ||
} | ||
|
||
function isValidSignatureForAddress( | ||
bytes32 dataHash, | ||
bytes memory moduleSignature, | ||
address smartAccount | ||
) public view virtual returns (bytes4) { | ||
return EIP1271_MAGIC_VALUE; | ||
} | ||
|
||
/// @inheritdoc ISignatureValidator | ||
function isValidSignatureUnsafe( | ||
bytes32 dataHash, | ||
bytes memory moduleSignature | ||
) public view virtual returns (bytes4) { | ||
return | ||
isValidSignatureForAddressUnsafe( | ||
dataHash, | ||
moduleSignature, | ||
msg.sender | ||
); | ||
} | ||
|
||
function isValidSignatureForAddressUnsafe( | ||
bytes32 dataHash, | ||
bytes memory moduleSignature, | ||
address smartAccount | ||
) public view virtual returns (bytes4) { | ||
return EIP1271_MAGIC_VALUE; | ||
} | ||
} |
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
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
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,150 @@ | ||
import { expect } from "chai"; | ||
import hre, { ethers, deployments, waffle } from "hardhat"; | ||
import { hashMessage, solidityKeccak256 } from "ethers/lib/utils"; | ||
import { | ||
makeEcdsaModuleUserOp, | ||
getUserOpHash, | ||
fillAndSign, | ||
} from "../utils/userOp"; | ||
import { | ||
getEntryPoint, | ||
getSmartAccountFactory, | ||
getEcdsaOwnershipRegistryModule, | ||
deployContract, | ||
getMockToken, | ||
getSmartAccountWithModule, | ||
} from "../utils/setupHelper"; | ||
import { encodeTransfer } from "../utils/testUtils"; | ||
import { AddressZero } from "@ethersproject/constants"; | ||
import { mock } from "node:test"; | ||
|
||
describe("ECDSA Registry Module: ", async () => { | ||
const [deployer, smartAccountOwner, alice, bob, charlie] = | ||
waffle.provider.getWallets(); | ||
const smartAccountDeploymentIndex = 0; | ||
const SIG_VALIDATION_FAILED = 1; | ||
const EIP1271_INVALID_SIGNATURE = "0xffffffff"; | ||
const EIP1271_MAGIC_VALUE = "0x1626ba7e"; | ||
const SIG_VALIDATION_SUCCESS = 0; | ||
|
||
const setupTests = deployments.createFixture(async ({ deployments }) => { | ||
await deployments.fixture(); | ||
|
||
const entryPoint = await getEntryPoint(); | ||
const saFactory = await getSmartAccountFactory(); | ||
const mockToken = await getMockToken(); | ||
|
||
const MockValidationModule = await hre.ethers.getContractFactory( | ||
"MockValidationModule" | ||
); | ||
|
||
const mockModule = await MockValidationModule.deploy(); | ||
console.log(mockModule.address); | ||
|
||
const setupData = mockModule.interface.encodeFunctionData( | ||
"initForSmartAccount", | ||
[] | ||
); | ||
|
||
const userSA = await getSmartAccountWithModule( | ||
mockModule.address, | ||
setupData, // can not be 0x! | ||
smartAccountDeploymentIndex | ||
); | ||
|
||
const tokensToMint = ethers.utils.parseEther("100"); | ||
await mockToken.mint(userSA.address, tokensToMint.toString()); | ||
await mockToken.mint(bob.address, tokensToMint.toString()); | ||
|
||
await deployer.sendTransaction({ | ||
to: userSA.address, | ||
value: ethers.utils.parseEther("60"), | ||
}); | ||
|
||
await deployer.sendTransaction({ | ||
to: smartAccountOwner.address, | ||
value: ethers.utils.parseEther("60"), | ||
}); | ||
|
||
const randomContractCode = ` | ||
contract random { | ||
function returnAddress() public view returns(address){ | ||
return address(this); | ||
} | ||
} | ||
`; | ||
const randomContract = await deployContract(deployer, randomContractCode); | ||
|
||
return { | ||
entryPoint: entryPoint, | ||
saFactory: saFactory, | ||
mockModule: mockModule, | ||
randomContract: randomContract, | ||
userSA: userSA, | ||
mockToken: mockToken, | ||
}; | ||
}); | ||
|
||
// validateUserOp(UserOperation calldata userOp,bytes32 userOpHash) | ||
describe("validateUserOp(): ", async () => { | ||
it("Returns SIG_VALIDATION_SUCCESS for a valid UserOp and valid userOpHash ", async () => { | ||
const { mockModule, entryPoint, userSA, mockToken } = await setupTests(); | ||
const userSABalanceBefore = await mockToken.balanceOf(userSA.address); | ||
const bobBalanceBefore = await mockToken.balanceOf(bob.address); | ||
const tokenAmountToTransfer = ethers.utils.parseEther("3.5672"); | ||
|
||
const txnData = mockToken.interface.encodeFunctionData("transfer", [ | ||
bob.address, | ||
tokenAmountToTransfer.toString(), | ||
]); | ||
const userOp = await makeEcdsaModuleUserOp( | ||
"execute_ncC", | ||
[mockToken.address, 0, txnData], | ||
userSA.address, | ||
charlie, // random dude | ||
entryPoint, | ||
mockModule.address, | ||
{ | ||
preVerificationGas: 50000, | ||
} | ||
); | ||
// Construct userOpHash | ||
const provider = entryPoint?.provider; | ||
const chainId = await provider!.getNetwork().then((net) => net.chainId); | ||
const userOpHash = getUserOpHash(userOp, entryPoint.address, chainId); | ||
|
||
const res = await mockModule.validateUserOp(userOp, userOpHash); | ||
expect(res).to.be.equal(SIG_VALIDATION_SUCCESS); | ||
await entryPoint.handleOps([userOp], bob.address); | ||
expect(await mockToken.balanceOf(bob.address)).to.equal( | ||
bobBalanceBefore.add(tokenAmountToTransfer) | ||
); | ||
expect(await mockToken.balanceOf(userSA.address)).to.equal( | ||
userSABalanceBefore.sub(tokenAmountToTransfer) | ||
); | ||
}); | ||
}); | ||
|
||
describe("isValidSignatureForAddress(): ", async () => { | ||
it("Returns EIP1271_MAGIC_VALUE always", async () => { | ||
const { mockModule, userSA } = await setupTests(); | ||
|
||
const stringMessage = "random dude signed this message"; | ||
const messageHash = solidityKeccak256(["string"], [stringMessage]); | ||
const messageHashAndAddress = ethers.utils.arrayify( | ||
ethers.utils.hexConcat([messageHash, userSA.address]) | ||
); | ||
|
||
// signMessage prepends the message with the prefix and length and then hashes it | ||
const signature = await bob.signMessage(messageHashAndAddress); | ||
|
||
expect( | ||
await mockModule.isValidSignatureForAddress( | ||
messageHash, | ||
signature, | ||
userSA.address | ||
) | ||
).to.equal(EIP1271_MAGIC_VALUE); | ||
}); | ||
}); | ||
}); |
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