Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/eip712 test #140

Merged
merged 5 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
"solidity-coverage": "^0.8.12",
"ts-node": ">=10.9.2",
"typechain": "^8.3.2",
"typescript": ">=5.4.5",
"viem": "^2.12.5"
"typescript": ">=5.4.5"
},
"keywords": [
"nexus",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract TestERC1271Account_IsValidSignature is NexusTest_Base {
}

/// @notice Tests the failure of an EIP-712 signature validation due to a wrong signer.
function test_isValidSignature_EIP712Sign_MockValidator_Wrong1271Signer_Fail() public {
function test_isValidSignature_EIP712Sign_MockValidator_Wrong1271Signer_Fail() public view {
TestTemps memory t;
t.contents = keccak256("123");
(t.v, t.r, t.s) = vm.sign(BOB.privateKey, toERC1271Hash(t.contents, payable(address(ALICE_ACCOUNT))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ contract TestModuleManager_EnableMode is Test, TestModuleManagement_Base {

bytes memory enableModeSig = signMessage(BOB, hashToSign); //should be signed by current owner
enableModeSig = abi.encodePacked(address(VALIDATOR_MODULE), enableModeSig); //append validator address

// Enable Mode Sig Prefix
// uint256 moduleTypeId
// bytes4 initDataLength
Expand Down
1 change: 0 additions & 1 deletion test/foundry/utils/NexusTest_Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.26;

import "./Imports.sol";
import "./TestHelper.t.sol";
import "./EventsAndErrors.sol";

/// @title NexusTest_Base - Base contract for testing Nexus smart account functionalities
Expand Down
9 changes: 4 additions & 5 deletions test/hardhat/common/Stakeable.specs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ethers } from "hardhat";
import { expect } from "chai";
import { AddressLike, parseEther } from "ethers";
import { AddressLike, parseEther, ZeroAddress } from "ethers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { EntryPoint, Nexus, Stakeable } from "../../../typechain-types";
import { deployContractsAndSAFixture } from "../utils/deployment";
import { zeroAddress } from "viem";

describe("Stakeable tests", function () {
let smartAccount: Nexus;
Expand Down Expand Up @@ -66,19 +65,19 @@ describe("Stakeable tests", function () {

it("Should fail to add stake to an incorrect entrypoint address", async function () {
await expect(
stakeable.addStake(zeroAddress, 0, { value: parseEther("1") }),
stakeable.addStake(ZeroAddress, 0, { value: parseEther("1") }),
).to.be.revertedWithCustomError(stakeable, "InvalidEntryPointAddress");
});

it("Should fail to unlock stake from an incorrect entrypoint address", async function () {
await expect(
stakeable.unlockStake(zeroAddress),
stakeable.unlockStake(ZeroAddress),
).to.be.revertedWithCustomError(stakeable, "InvalidEntryPointAddress");
});

it("Should fail to withdraw stake from an incorrect entrypoint address", async function () {
await expect(
stakeable.withdrawStake(zeroAddress, ownerAddress),
stakeable.withdrawStake(ZeroAddress, ownerAddress),
).to.be.revertedWithCustomError(stakeable, "InvalidEntryPointAddress");
});

Expand Down
132 changes: 63 additions & 69 deletions test/hardhat/smart-account/Nexus.Basics.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import {
AddressLike,
Signer,
ZeroAddress,
concat,
hashMessage,
keccak256,
solidityPacked,
toBeHex,
zeroPadBytes,
} from "ethers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import {
Expand All @@ -21,13 +18,14 @@ import {
} from "../../../typechain-types";
import { ExecutionMethod, ModuleType } from "../utils/types";
import { deployContractsAndSAFixture } from "../utils/deployment";
import { encodeData, to18 } from "../utils/encoding";
import { to18 } from "../utils/encoding";
import {
getInitCode,
buildPackedUserOp,
generateUseropCallData,
getNonce,
MODE_VALIDATION
MODE_VALIDATION,
getAccountDomainStructFields
} from "../utils/operationHelpers";
import {
CALLTYPE_BATCH,
Expand All @@ -39,7 +37,6 @@ import {
MODE_PAYLOAD,
UNUSED,
} from "../utils/erc7579Utils";
import { Hex, hashTypedData, toHex } from "viem";

describe("Nexus Basic Specs", function () {
let factory: K1ValidatorFactory;
Expand Down Expand Up @@ -135,7 +132,6 @@ describe("Nexus Basic Specs", function () {

it("Should get implementation address of smart account", async () => {
const saImplementation = await smartAccount.getImplementation();
console.log("Implementation Address: ", saImplementation);
expect(saImplementation).to.not.equal(ZeroAddress);
});

Expand All @@ -162,42 +158,6 @@ describe("Nexus Basic Specs", function () {
expect(domainSeparator).to.not.equal(ZeroAddress);
});

it("Should get hashed typed data", async () => {
const hash = hashTypedData({
domain: {
name: "Nexus",
version: "1",
chainId: 1,
verifyingContract: smartAccountAddress as Hex,
},
types: {
Person: [
{ name: "name", type: "string" },
{ name: "wallet", type: "address" },
],
Mail: [
{ name: "from", type: "Person" },
{ name: "to", type: "Person" },
{ name: "contents", type: "string" },
],
},
primaryType: "Mail",
message: {
from: {
name: "Cow",
wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
},
to: {
name: "Bob",
wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
},
contents: "Hello, Bob!",
},
});
const hashedTypedData = await smartAccount.hashTypedData(hash);
expect(hashedTypedData).to.not.be.undefined;
});

it("Should verify supported account modes", async function () {
expect(
await smartAccount.supportsExecutionMode(
Expand Down Expand Up @@ -335,7 +295,7 @@ describe("Nexus Basic Specs", function () {
// https://github.com/frangio/eip712-wrapper-for-eip1271/blob/master/src/eip1271-account.ts#L34
// https://github.com/wevm/viem/blob/main/src/actions/wallet/signMessage.ts
// https://github.com/ethers-io/ethers.js/blob/92761872198cf6c9334570da3d110bca2bafa641/src.ts/providers/provider-jsonrpc.ts#L435
it("Should check signature validity using smart account isValidSignature", async function () {
it("Should check signature validity using smart account isValidSignature for Personal Sign", async function () {
const isModuleInstalled = await smartAccount.isModuleInstalled(
ModuleType.Validation,
await validatorModule.getAddress(),
Expand All @@ -346,30 +306,12 @@ describe("Nexus Basic Specs", function () {
// 1. Convert foundry util to ts code (as below)

const data = keccak256("0x1234");

// Define constants as per the original Solidity function
const DOMAIN_NAME = "Nexus";
const DOMAIN_VERSION = "1.0.0-beta";
const DOMAIN_TYPEHASH =
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)";
const PARENT_TYPEHASH = "PersonalSign(bytes prefixed)";
const ALICE_ACCOUNT = smartAccountAddress;
const network = await ethers.provider.getNetwork();
const chainId = network.chainId;

// Calculate the domain separator
const domainSeparator = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "bytes32", "bytes32", "uint256", "address"],
[
ethers.keccak256(ethers.toUtf8Bytes(DOMAIN_TYPEHASH)),
ethers.keccak256(ethers.toUtf8Bytes(DOMAIN_NAME)),
ethers.keccak256(ethers.toUtf8Bytes(DOMAIN_VERSION)),
chainId,
ALICE_ACCOUNT,
],
),
);
const domainSeparator = await smartAccount.DOMAIN_SEPARATOR();

// Calculate the parent struct hash
const parentStructHash = ethers.keccak256(
Expand All @@ -384,11 +326,6 @@ describe("Nexus Basic Specs", function () {
ethers.concat(["0x1901", domainSeparator, parentStructHash]),
);

console.log(
"being signed",
ethers.hashMessage(ethers.getBytes(resultHash)),
);

const signature = await smartAccountOwner.signMessage(
ethers.getBytes(resultHash),
);
Expand All @@ -403,6 +340,63 @@ describe("Nexus Basic Specs", function () {

expect(isValid).to.equal("0x1626ba7e");
});

it("Should check signature validity using smart account isValidSignature for EIP 712 signature", async function () {
const PARENT_TYPEHASH = "TypedDataSign(Contents contents,bytes1 fields,string name,string version,uint256 chainId,address verifyingContract,bytes32 salt,uint256[] extensions)Contents(bytes32 stuff)";
const APP_DOMAIN_SEPARATOR = "0xa1a044077d7677adbbfa892ded5390979b33993e0e2a457e3f974bbcda53821b";
const data = "0x1234";
const contents = ethers.keccak256(ethers.toUtf8Bytes(data));

const accountDomainStructFields = await getAccountDomainStructFields(smartAccount);

const parentStructHash = ethers.keccak256(
ethers.solidityPacked(["bytes", "bytes"],[
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "bytes32"],
[ethers.keccak256(ethers.toUtf8Bytes(PARENT_TYPEHASH)), contents]
),
accountDomainStructFields
])
);

const dataToSign = ethers.keccak256(
ethers.concat([
'0x1901',
APP_DOMAIN_SEPARATOR,
parentStructHash
])
);

const signature = await smartAccountOwner.signMessage(ethers.getBytes(dataToSign));

const contentsType = ethers.toUtf8Bytes("Contents(bytes32 stuff)");

const signatureData = ethers.concat([
signature,
APP_DOMAIN_SEPARATOR,
contents,
contentsType,
ethers.toBeHex(contentsType.length, 2)
]);

const contentsHash = keccak256(
ethers.concat([
'0x1901',
APP_DOMAIN_SEPARATOR,
contents
])
);

const finalSignature = ethers.solidityPacked(["address", "bytes"],[
await validatorModule.getAddress(),
signatureData
]);

const isValid = await smartAccount.isValidSignature(contentsHash, finalSignature);

expect(isValid).to.equal("0x1626ba7e");
});

});

describe("Smart Account check Only Entrypoint actions", function () {
Expand Down
12 changes: 4 additions & 8 deletions test/hardhat/smart-account/Nexus.Factory.specs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from "hardhat";
import { expect } from "chai";
import { AddressLike, Signer, keccak256, solidityPacked } from "ethers";
import { AddressLike, Signer, ZeroAddress, keccak256, solidityPacked } from "ethers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import {
K1ValidatorFactory,
Expand All @@ -12,19 +12,15 @@ import {
Bootstrap,
BootstrapLib,
MockHook,
MockExecutor,
MockHandler,
MockRegistry,
} from "../../../typechain-types";
import {
deployContractsAndSAFixture,
deployContractsFixture,
} from "../utils/deployment";
import { encodeData, to18 } from "../utils/encoding";
import { to18 } from "../utils/encoding";
import { MODE_VALIDATION, buildPackedUserOp, getNonce } from "../utils/operationHelpers";
import { BootstrapConfigStruct } from "../../../typechain-types/contracts/factory/K1ValidatorFactory";
import { toBytes, zeroAddress } from "viem";
import { GENERIC_FALLBACK_SELECTOR } from "../utils/erc7579Utils";
import { BootstrapConfigStruct } from "../../../typechain-types/contracts/lib/BootstrapLib";

describe("Nexus Factory Tests", function () {
let factory: K1ValidatorFactory;
Expand Down Expand Up @@ -349,7 +345,7 @@ describe("Nexus Factory Tests", function () {
owner,
);
await expect(
ContractFactory.deploy(zeroAddress, owner),
ContractFactory.deploy(ZeroAddress, owner),
).to.be.revertedWithCustomError(
factory,
"ImplementationAddressCanNotBeZero()",
Expand Down
3 changes: 1 addition & 2 deletions test/hardhat/smart-account/Nexus.ModuleManager.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
UNUSED,
installModule,
} from "../utils/erc7579Utils";
import { toBytes } from "viem";

describe("Nexus Module Management Tests", () => {
let deployedNexus: Nexus;
Expand Down Expand Up @@ -632,7 +631,7 @@ describe("Nexus Module Management Tests", () => {
it("Should correctly install a fallback handler module on the smart account", async () => {
const exampleSender = await deployedNexus.getAddress();
const exampleValue = 12345;
const exampleData = toBytes("0x12345678");
const exampleData = ethers.getBytes("0x12345678");

await expect(
mockFallbackHandler.onGenericFallback(
Expand Down
18 changes: 17 additions & 1 deletion test/hardhat/utils/operationHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ethers } from "hardhat";
import { toGwei } from "./encoding";
import { ExecutionMethod, PackedUserOperation, UserOperation } from "./types";
import { Signer, AddressLike, BytesLike, BigNumberish, toBeHex, concat } from "ethers";
import { EntryPoint } from "../../../typechain-types";
import { EntryPoint, Nexus } from "../../../typechain-types";
import {
CALLTYPE_SINGLE,
EXECTYPE_DEFAULT,
Expand Down Expand Up @@ -31,6 +31,7 @@ export const DefaultsForUserOp: UserOperation = {
export const MODE_VALIDATION = "0x00";
export const MODE_MODULE_ENABLE = "0x01";

const abiCoder = new ethers.AbiCoder();
/**
* Simplifies the creation of a PackedUserOperation object by abstracting repetitive logic and enhancing readability.
* @param userOp The user operation details.
Expand Down Expand Up @@ -404,6 +405,21 @@ export async function getNonce(
return await entryPoint.getNonce(accountAddress, key);
}

export async function getAccountDomainStructFields(account: Nexus): Promise<string> {
const [fields, name, version, chainId, verifyingContract, salt, extensions] = await account.eip712Domain();
return ethers.AbiCoder.defaultAbiCoder().encode(
["bytes1", "bytes32", "bytes32", "uint256", "address", "bytes32", "bytes32"],
[
fields, // matches Solidity
ethers.keccak256(ethers.toUtf8Bytes(name)), // matches Solidity
ethers.keccak256(ethers.toUtf8Bytes(version)), // matches Solidity
chainId,
verifyingContract,
salt,
ethers.keccak256(ethers.solidityPacked(["uint256[]"], [extensions]))
]
);
}
// More functions to be added
// 1. simulateValidation (using EntryPointSimulations)
// 2. simulareHandleOps
Loading
Loading