mirror of
https://github.com/luxfi/eoa-contracts.git
synced 2026-10-30 16:58:32 +00:00
635 lines
22 KiB
TypeScript
635 lines
22 KiB
TypeScript
import { expect } from "chai";
|
|
import hre, { ethers, deployments, waffle } from "hardhat";
|
|
import { hashMessage } 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";
|
|
|
|
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 setupTests = deployments.createFixture(async ({ deployments }) => {
|
|
await deployments.fixture();
|
|
|
|
const entryPoint = await getEntryPoint();
|
|
const saFactory = await getSmartAccountFactory();
|
|
const ecdsaRegistryModule = await getEcdsaOwnershipRegistryModule();
|
|
const mockToken = await getMockToken();
|
|
|
|
const ecdsaOwnershipSetupData =
|
|
ecdsaRegistryModule.interface.encodeFunctionData("initForSmartAccount", [
|
|
smartAccountOwner.address,
|
|
]);
|
|
const userSA = await getSmartAccountWithModule(
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
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,
|
|
ecdsaRegistryModule: ecdsaRegistryModule,
|
|
ecdsaOwnershipSetupData: ecdsaOwnershipSetupData,
|
|
randomContract: randomContract,
|
|
userSA: userSA,
|
|
mockToken: mockToken,
|
|
};
|
|
});
|
|
|
|
describe("initForSmartAccount: ", async () => {
|
|
it("Reverts when trying to set Smart Contract as owner of the Smart Account via deployment userOp", async () => {
|
|
// DISCLAIMER:
|
|
// In theory it is still possible to set smart contractd address as owner of a SA via initForSmartAccount,
|
|
// if factory has been called directly, not via userOp.
|
|
// In Biconomy SDK will will perform an additional off-chain verification that address provided as owner is EOA
|
|
// Also the initForSmartAccount method's parameter is now called `eoaOwner` instead of just `owner` to
|
|
// highlight it should be EOA.
|
|
// Assuming this, the explicit check for eoaOwner is not smart contract can be removed because of this
|
|
// issue: https://github.com/eth-infinitism/bundler/issues/137
|
|
|
|
const { saFactory, ecdsaRegistryModule, randomContract, entryPoint } =
|
|
await setupTests();
|
|
|
|
const ecdsaOwnershipSetupData =
|
|
ecdsaRegistryModule.interface.encodeFunctionData(
|
|
"initForSmartAccount",
|
|
[randomContract.address]
|
|
);
|
|
|
|
const deploymentData = saFactory.interface.encodeFunctionData(
|
|
"deployCounterFactualAccount",
|
|
[
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex,
|
|
]
|
|
);
|
|
|
|
const expectedSmartAccountAddress =
|
|
await saFactory.getAddressForCounterFactualAccount(
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex
|
|
);
|
|
|
|
await deployer.sendTransaction({
|
|
to: expectedSmartAccountAddress,
|
|
value: ethers.utils.parseEther("60"),
|
|
});
|
|
|
|
// deployment userOp
|
|
const deploymentUserOp = await fillAndSign(
|
|
{
|
|
sender: expectedSmartAccountAddress,
|
|
callGasLimit: 1_000_000,
|
|
initCode: ethers.utils.hexConcat([saFactory.address, deploymentData]),
|
|
callData: "0x",
|
|
preVerificationGas: 50000,
|
|
},
|
|
smartAccountOwner, // need to sign by someone at least
|
|
entryPoint,
|
|
"nonce"
|
|
);
|
|
|
|
const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[deploymentUserOp.signature, ecdsaRegistryModule.address]
|
|
);
|
|
|
|
deploymentUserOp.signature = signatureWithModuleAddress;
|
|
|
|
await expect(entryPoint.handleOps([deploymentUserOp], charlie.address))
|
|
.to.be.revertedWith("FailedOp")
|
|
.withArgs(0, "AA24 signature error");
|
|
|
|
await expect(
|
|
ecdsaRegistryModule.getOwner(expectedSmartAccountAddress)
|
|
).to.be.revertedWith("NoOwnerRegisteredForSmartAccount");
|
|
});
|
|
});
|
|
|
|
describe("initForSmartAccount: ", async () => {
|
|
it("Reverts when trying to set Smart Contract as owner of the Smart Account via deployment userOp", async () => {
|
|
// DISCLAIMER:
|
|
// In theory it is still possible to set smart contractd address as owner of a SA via initForSmartAccount,
|
|
// if factory has been called directly, not via userOp.
|
|
// In Biconomy SDK will will perform an additional off-chain verification that address provided as owner is EOA
|
|
// Also the initForSmartAccount method's parameter is now called `eoaOwner` instead of just `owner` to
|
|
// highlight it should be EOA.
|
|
// Assuming this, the explicit check for eoaOwner is not smart contract can be removed because of this
|
|
// issue: https://github.com/eth-infinitism/bundler/issues/137
|
|
|
|
const { saFactory, ecdsaRegistryModule, randomContract, entryPoint } =
|
|
await setupTests();
|
|
|
|
const ecdsaOwnershipSetupData =
|
|
ecdsaRegistryModule.interface.encodeFunctionData(
|
|
"initForSmartAccount",
|
|
[randomContract.address]
|
|
);
|
|
|
|
const deploymentData = saFactory.interface.encodeFunctionData(
|
|
"deployCounterFactualAccount",
|
|
[
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex,
|
|
]
|
|
);
|
|
|
|
const expectedSmartAccountAddress =
|
|
await saFactory.getAddressForCounterFactualAccount(
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex
|
|
);
|
|
|
|
await deployer.sendTransaction({
|
|
to: expectedSmartAccountAddress,
|
|
value: ethers.utils.parseEther("60"),
|
|
});
|
|
|
|
// deployment userOp
|
|
const deploymentUserOp = await fillAndSign(
|
|
{
|
|
sender: expectedSmartAccountAddress,
|
|
callGasLimit: 1_000_000,
|
|
initCode: ethers.utils.hexConcat([saFactory.address, deploymentData]),
|
|
callData: "0x",
|
|
preVerificationGas: 50000,
|
|
},
|
|
smartAccountOwner, // need to sign by someone at least
|
|
entryPoint,
|
|
"nonce",
|
|
true
|
|
);
|
|
|
|
const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[deploymentUserOp.signature, ecdsaRegistryModule.address]
|
|
);
|
|
|
|
deploymentUserOp.signature = signatureWithModuleAddress;
|
|
|
|
await expect(entryPoint.handleOps([deploymentUserOp], charlie.address))
|
|
.to.be.revertedWith("FailedOp")
|
|
.withArgs(0, "AA24 signature error");
|
|
|
|
await expect(
|
|
ecdsaRegistryModule.getOwner(expectedSmartAccountAddress)
|
|
).to.be.revertedWith("NoOwnerRegisteredForSmartAccount");
|
|
});
|
|
|
|
it("Reverts when calling again after initialization", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, userSA } = await setupTests();
|
|
const txnData1 = ecdsaRegistryModule.interface.encodeFunctionData(
|
|
"initForSmartAccount",
|
|
[bob.address]
|
|
);
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[ecdsaRegistryModule.address, 0, txnData1],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const tx = await entryPoint.handleOps([userOp], alice.address);
|
|
await expect(tx).to.emit(entryPoint, "UserOperationRevertReason");
|
|
});
|
|
});
|
|
|
|
describe("transferOwnership: ", async () => {
|
|
it("MOVED: Call transferOwnership from userSA and it successfully changes owner ", async () => {
|
|
// moved to /test/bundler-integration/module/ECDSARegistry.Module.specs.ts
|
|
});
|
|
|
|
it("Reverts when trying to set Smart Contract Address as owner via transferOwnership() ", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, randomContract, userSA } =
|
|
await setupTests();
|
|
const previousOwner = await ecdsaRegistryModule.getOwner(userSA.address);
|
|
const txnData1 = ecdsaRegistryModule.interface.encodeFunctionData(
|
|
"transferOwnership",
|
|
[randomContract.address]
|
|
);
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[ecdsaRegistryModule.address, 0, txnData1],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
|
|
const tx = await entryPoint.handleOps([userOp], charlie.address);
|
|
await expect(tx).to.emit(entryPoint, "UserOperationRevertReason");
|
|
expect(await ecdsaRegistryModule.getOwner(userSA.address)).to.be.equal(
|
|
previousOwner
|
|
);
|
|
});
|
|
|
|
it("Reverts when trying to set address(0) as owner", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, userSA } = await setupTests();
|
|
const previousOwner = await ecdsaRegistryModule.getOwner(userSA.address);
|
|
const txnData1 = ecdsaRegistryModule.interface.encodeFunctionData(
|
|
"transferOwnership",
|
|
[AddressZero]
|
|
);
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[ecdsaRegistryModule.address, 0, txnData1],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
|
|
const tx = await entryPoint.handleOps([userOp], charlie.address);
|
|
await expect(tx).to.emit(entryPoint, "UserOperationRevertReason");
|
|
expect(await ecdsaRegistryModule.getOwner(userSA.address)).to.be.equal(
|
|
previousOwner
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("renounceOwnership():", async () => {
|
|
it("MOVED: Should be able to renounce ownership and the new owner should be address(0)", async () => {
|
|
// moved to /test/bundler-integration/module/ECDSARegistry.Module.specs.ts
|
|
});
|
|
});
|
|
|
|
// validateUserOp(UserOperation calldata userOp,bytes32 userOpHash)
|
|
describe("validateUserOp(): ", async () => {
|
|
it("MOVED: Returns SIG_VALIDATION_SUCCESS for a valid UserOp and valid userOpHash ", async () => {
|
|
// moved to /test/bundler-integration/module/ECDSARegistry.Module.specs.ts
|
|
});
|
|
|
|
// Pass in valid userOp with invalid userOpHash
|
|
it("Returns SIG_VALIDATION_FAILED when invalid chainId is passed in userOpHash", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, mockToken, userSA } =
|
|
await setupTests();
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("7.934");
|
|
|
|
const txnData = mockToken.interface.encodeFunctionData("transfer", [
|
|
bob.address,
|
|
tokenAmountToTransfer.toString(),
|
|
]);
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[mockToken.address, 0, txnData],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const provider = entryPoint?.provider;
|
|
const chainId = await provider!.getNetwork().then((net) => net.chainId);
|
|
const invalidChainId = 2 * chainId;
|
|
const invalidUserOpHash = getUserOpHash(
|
|
userOp,
|
|
entryPoint.address,
|
|
invalidChainId
|
|
);
|
|
|
|
expect(
|
|
await ecdsaRegistryModule.validateUserOp(userOp, invalidUserOpHash)
|
|
).to.be.equal(SIG_VALIDATION_FAILED);
|
|
});
|
|
|
|
it("Returns SIG_VALIDATION_FAILED when invalid entryPoint address is passed to userOpHash", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, userSA, mockToken } =
|
|
await setupTests();
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("0.23436");
|
|
|
|
const txnData = mockToken.interface.encodeFunctionData("transfer", [
|
|
bob.address,
|
|
tokenAmountToTransfer.toString(),
|
|
]);
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[mockToken.address, 0, txnData],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const provider = entryPoint?.provider;
|
|
const chainId = await provider!.getNetwork().then((net) => net.chainId);
|
|
const invalidEntryPointAddress = bob.address;
|
|
const userOpHash = getUserOpHash(
|
|
userOp,
|
|
invalidEntryPointAddress,
|
|
chainId
|
|
);
|
|
expect(
|
|
await ecdsaRegistryModule.validateUserOp(userOp, userOpHash)
|
|
).to.be.equal(SIG_VALIDATION_FAILED);
|
|
});
|
|
|
|
it("Returns SIG_VALIDATION_FAILED when userOp is signed by an invalid owner ", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, userSA, mockToken } =
|
|
await setupTests();
|
|
const userSABalanceBefore = await mockToken.balanceOf(userSA.address);
|
|
const bobBalanceBefore = await mockToken.balanceOf(bob.address);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("1.3425");
|
|
|
|
const txnData = mockToken.interface.encodeFunctionData("transfer", [
|
|
bob.address,
|
|
tokenAmountToTransfer.toString(),
|
|
]);
|
|
|
|
const notSmartAccountOwner = charlie;
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[mockToken.address, 0, txnData],
|
|
userSA.address,
|
|
notSmartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const provider = entryPoint?.provider;
|
|
const chainId = await provider!.getNetwork().then((net) => net.chainId);
|
|
const userOpHash = getUserOpHash(userOp, entryPoint.address, chainId);
|
|
|
|
expect(
|
|
await ecdsaRegistryModule.validateUserOp(userOp, userOpHash)
|
|
).to.be.equal(SIG_VALIDATION_FAILED);
|
|
await expect(
|
|
entryPoint.handleOps([userOp], smartAccountOwner.address)
|
|
).to.be.revertedWith("FailedOp");
|
|
expect(await mockToken.balanceOf(bob.address)).to.equal(bobBalanceBefore);
|
|
expect(await mockToken.balanceOf(userSA.address)).to.equal(
|
|
userSABalanceBefore
|
|
);
|
|
});
|
|
|
|
it("Reverts when userOp.sender is an Unregistered Smart Account", async () => {
|
|
const {
|
|
saFactory,
|
|
ecdsaRegistryModule,
|
|
ecdsaOwnershipSetupData,
|
|
entryPoint,
|
|
mockToken,
|
|
} = await setupTests();
|
|
const bobBalanceBefore = await mockToken.balanceOf(bob.address);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("1.9999");
|
|
const EcdsaOwnershipRegistryModule = await hre.ethers.getContractFactory(
|
|
"EcdsaOwnershipRegistryModule"
|
|
);
|
|
|
|
// get a new smart account
|
|
const unregisteredSmartAccountAddress =
|
|
await saFactory.getAddressForCounterFactualAccount(
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex + 1
|
|
);
|
|
await saFactory.deployCounterFactualAccount(
|
|
ecdsaRegistryModule.address,
|
|
ecdsaOwnershipSetupData,
|
|
smartAccountDeploymentIndex + 1
|
|
);
|
|
const unregisteredSA = await hre.ethers.getContractAt(
|
|
"SmartAccount",
|
|
unregisteredSmartAccountAddress
|
|
);
|
|
|
|
// fund the smart account
|
|
await mockToken.mint(
|
|
unregisteredSmartAccountAddress,
|
|
ethers.utils.parseEther("1000").toString()
|
|
);
|
|
await deployer.sendTransaction({
|
|
to: unregisteredSmartAccountAddress,
|
|
value: ethers.utils.parseEther("60"),
|
|
});
|
|
|
|
// renounce ownership
|
|
const renounceOwnershipUserOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[
|
|
ecdsaRegistryModule.address,
|
|
0,
|
|
EcdsaOwnershipRegistryModule.interface.encodeFunctionData(
|
|
"renounceOwnership",
|
|
[]
|
|
),
|
|
],
|
|
unregisteredSmartAccountAddress,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const handleOpsTx = await entryPoint.handleOps(
|
|
[renounceOwnershipUserOp],
|
|
smartAccountOwner.address
|
|
);
|
|
await expect(handleOpsTx).to.not.emit(
|
|
entryPoint,
|
|
"UserOperationRevertReason"
|
|
);
|
|
await expect(
|
|
ecdsaRegistryModule.getOwner(unregisteredSA.address)
|
|
).to.be.revertedWith("NoOwnerRegisteredForSmartAccount");
|
|
|
|
const sendTokenUserOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[
|
|
mockToken.address,
|
|
0,
|
|
encodeTransfer(bob.address, tokenAmountToTransfer.toString()),
|
|
],
|
|
unregisteredSmartAccountAddress,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
|
|
const provider = entryPoint?.provider;
|
|
const chainId = await provider!.getNetwork().then((net) => net.chainId);
|
|
const userOpHash = getUserOpHash(
|
|
sendTokenUserOp,
|
|
entryPoint.address,
|
|
chainId
|
|
);
|
|
await expect(
|
|
ecdsaRegistryModule.validateUserOp(sendTokenUserOp, userOpHash)
|
|
).to.be.revertedWith("NoOwnerRegisteredForSmartAccount");
|
|
|
|
const unregisteredSABalanceBefore = await mockToken.balanceOf(
|
|
unregisteredSA.address
|
|
);
|
|
await expect(
|
|
entryPoint.handleOps([sendTokenUserOp], smartAccountOwner.address)
|
|
).to.be.revertedWith("FailedOp");
|
|
expect(await mockToken.balanceOf(bob.address)).to.equal(bobBalanceBefore);
|
|
expect(await mockToken.balanceOf(unregisteredSA.address)).to.equal(
|
|
unregisteredSABalanceBefore
|
|
);
|
|
});
|
|
|
|
it("Reverts when length of user.signature is less than 65 ", async () => {
|
|
const { ecdsaRegistryModule, entryPoint, mockToken, userSA } =
|
|
await setupTests();
|
|
const userSABalanceBefore = await mockToken.balanceOf(userSA.address);
|
|
const bobBalanceBefore = await mockToken.balanceOf(bob.address);
|
|
const tokenAmountToTransfer = ethers.utils.parseEther("3.632");
|
|
|
|
const txnData = await mockToken.interface.encodeFunctionData("transfer", [
|
|
bob.address,
|
|
tokenAmountToTransfer.toString(),
|
|
]);
|
|
|
|
const userOp = await makeEcdsaModuleUserOp(
|
|
"execute_ncC",
|
|
[mockToken.address, 0, txnData],
|
|
userSA.address,
|
|
smartAccountOwner,
|
|
entryPoint,
|
|
ecdsaRegistryModule.address
|
|
);
|
|
const provider = entryPoint?.provider;
|
|
const chainId = await provider!.getNetwork().then((net) => net.chainId);
|
|
const userOpHash = await getUserOpHash(
|
|
userOp,
|
|
entryPoint.address,
|
|
chainId
|
|
);
|
|
|
|
// construct signature of length < 65
|
|
const invalidSignature = new Uint8Array(64);
|
|
for (let i = 0; i < invalidSignature.length; i++) {
|
|
invalidSignature[i] = i; // Set each byte to its index value
|
|
}
|
|
|
|
const invalidSignatureWithModuleAddress =
|
|
ethers.utils.defaultAbiCoder.encode(
|
|
["bytes", "address"],
|
|
[invalidSignature, ecdsaRegistryModule.address]
|
|
);
|
|
userOp.signature = invalidSignatureWithModuleAddress;
|
|
|
|
await expect(
|
|
ecdsaRegistryModule.validateUserOp(userOp, userOpHash)
|
|
).to.be.revertedWith("WrongSignatureLength");
|
|
await expect(entryPoint.handleOps([userOp], smartAccountOwner.address)).to
|
|
.be.reverted;
|
|
expect(await mockToken.balanceOf(bob.address)).to.equal(bobBalanceBefore);
|
|
expect(await mockToken.balanceOf(userSA.address)).to.equal(
|
|
userSABalanceBefore
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("isValidSignatureForAddress(): ", async () => {
|
|
it("Returns EIP1271_MAGIC_VALUE for valid signature signed by Smart Account Owner", async () => {
|
|
const { ecdsaRegistryModule, userSA } = await setupTests();
|
|
|
|
const messageToSign = "SCW signed this message";
|
|
const dataHash = hashMessage(messageToSign);
|
|
const signature = await smartAccountOwner.signMessage(messageToSign);
|
|
expect(
|
|
await ecdsaRegistryModule.isValidSignatureForAddress(
|
|
dataHash,
|
|
signature,
|
|
userSA.address
|
|
)
|
|
).to.equal(EIP1271_MAGIC_VALUE);
|
|
});
|
|
|
|
it("Reverts when Unregistered Smart Account calls isValidSignature()", async () => {
|
|
const { ecdsaRegistryModule, randomContract } = await setupTests();
|
|
const unregisteredSmartAccount = randomContract.address;
|
|
const messageToSign = "SCW signed this message";
|
|
const dataHash = hashMessage(messageToSign);
|
|
const signature = await smartAccountOwner.signMessage(messageToSign);
|
|
|
|
// set msg.sender to be unregisteredSmartAccount instead of userSA.address
|
|
await expect(
|
|
ecdsaRegistryModule.isValidSignatureForAddress(
|
|
dataHash,
|
|
signature,
|
|
unregisteredSmartAccount
|
|
)
|
|
).to.be.revertedWith("NoOwnerRegisteredForSmartAccount");
|
|
});
|
|
|
|
it("Reverts when signature length is less than 65", async () => {
|
|
const { ecdsaRegistryModule, userSA } = await setupTests();
|
|
|
|
const messageToSign = "SCW signed this message";
|
|
const dataHash = hashMessage(messageToSign);
|
|
// construct signature of length < 65
|
|
const invalidSignature = new Uint8Array(64);
|
|
for (let i = 0; i < invalidSignature.length; i++) {
|
|
invalidSignature[i] = i; // Set each byte to its index value
|
|
}
|
|
await expect(
|
|
ecdsaRegistryModule.isValidSignatureForAddress(
|
|
dataHash,
|
|
invalidSignature,
|
|
userSA.address
|
|
)
|
|
).to.be.revertedWith("WrongSignatureLength");
|
|
});
|
|
|
|
it("Returns 0xffffffff for signatures not signed by Smart Account Owners ", async () => {
|
|
const { ecdsaRegistryModule, userSA } = await setupTests();
|
|
|
|
const messageToSign = "SCW signed this message";
|
|
const dataHash = hashMessage(messageToSign);
|
|
const invalidOwner = charlie;
|
|
const signature = await invalidOwner.signMessage(messageToSign);
|
|
|
|
expect(
|
|
await ecdsaRegistryModule.isValidSignatureForAddress(
|
|
dataHash,
|
|
signature,
|
|
userSA.address
|
|
)
|
|
).to.equal(EIP1271_INVALID_SIGNATURE);
|
|
});
|
|
});
|
|
});
|