Skip to content

Commit

Permalink
Merge pull request #9 from openfort-xyz/test_eip712
Browse files Browse the repository at this point in the history
Test eip712
  • Loading branch information
Haypierre authored Nov 14, 2024
2 parents d03baf0 + 32166c4 commit 7861bfb
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 85 deletions.
4 changes: 2 additions & 2 deletions tasks/prepareTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ task("test")
const {factory, implementation} = await hre.run("deploy-factory")
// wait for sophon backend service to whitelist the factory in their paymaster
factoryAddress = factory
if (chain.name == "Sophon") await sleep(30000)
if (chain.name == "Sophon") await sleep(10000)
accountAddress = await hre.run("create-account", { factory, implementation, nonce: args.nonce })
// wait for sophon backend service to whitelist the new account in their paymaster
if (chain.name == "Sophon") await sleep(30000)
if (chain.name == "Sophon") await sleep(10000)
}
hre.openfortAccountAddress = accountAddress
hre.factoryAddress = factoryAddress
Expand Down
125 changes: 79 additions & 46 deletions tasks/utils.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,88 @@
import { defineChain, WalletClient } from "viem"
import { Address, defineChain, PublicClient, WalletClient } from "viem"
import { chainConfig, getGeneralPaymasterInput, zksyncInMemoryNode, zksyncSepoliaTestnet } from "viem/zksync"

export async function writeContract(c: WalletClient, contractParams) {
// add paymaster info for sophon
if (hre.network.config.url.includes("sophon")) {
contractParams.paymaster = process.env.SOPHON_TESTNET_PAYMASTER_ADDRESS
contractParams.paymasterInput = getGeneralPaymasterInput({ innerInput: new Uint8Array() })
}
return c.writeContract(contractParams)
export async function writeContract(c: WalletClient, contractParams) {
// add paymaster info for sophon
if (hre.network.config.url.includes("sophon")) {
contractParams.paymaster = process.env.SOPHON_TESTNET_PAYMASTER_ADDRESS
contractParams.paymasterInput = getGeneralPaymasterInput({ innerInput: new Uint8Array() })
}
return c.writeContract(contractParams)
}

export function getViemChainFromConfig() {
const sophon = defineChain({
...chainConfig,
id: 531050104,
name: "Sophon",
network: "sepolia",
nativeCurrency: {
name: "SOPHON",
symbol: "SOPH",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://rpc.testnet.sophon.xyz"],
},
public: {
http: ["https://rpc.testnet.sophon.xyz"],
},
},
blockExplorers: {
default: {
name: "Sophon Testnet Explorer",
url: "https://explorer.testnet.sophon.xyz/",
},
},
testnet: true,
})
const sophon = defineChain({
...chainConfig,
id: 531050104,
name: "Sophon",
network: "sepolia",
nativeCurrency: {
name: "SOPHON",
symbol: "SOPH",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://rpc.testnet.sophon.xyz"],
},
public: {
http: ["https://rpc.testnet.sophon.xyz"],
},
},
blockExplorers: {
default: {
name: "Sophon Testnet Explorer",
url: "https://explorer.testnet.sophon.xyz/",
},
},
testnet: true,
})

switch (hre.network.config.url) {
case "http://127.0.0.1:8011":
return zksyncInMemoryNode
case "https://sepolia.era.zksync.dev":
return zksyncSepoliaTestnet
case "https://rpc.testnet.sophon.xyz":
return sophon
default:
throw new Error("Unkown network")
}
switch (hre.network.config.url) {
case "http://127.0.0.1:8011":
return zksyncInMemoryNode
case "https://sepolia.era.zksync.dev":
return zksyncSepoliaTestnet
case "https://rpc.testnet.sophon.xyz":
return sophon
default:
throw new Error("Unkown network")
}
}

export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
return new Promise(resolve => setTimeout(resolve, ms));
}

export async function getEIP712Domain(contractAddress: Address, client: PublicClient) {
const abi = [
{
"constant": true,
"inputs": [],
"name": "eip712Domain",
"outputs": [
{ "name": "fields", "type": "bytes1" },
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" },
{ "name": "salt", "type": "bytes32" },
{ "name": "extensions", "type": "uint256[]" }
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];

try {
const result = await client.readContract({
address: contractAddress,
abi: abi,
functionName: "eip712Domain",
});
return result;
} catch (error) {
console.error("Error calling eip712Domain:", error);
}
}
113 changes: 76 additions & 37 deletions test/upgradeableOpenfortAccountTest.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { expect } from "chai"
import { encodeFunctionData, encodePacked, parseAbi } from "viem"
import { concat, encodeFunctionData, encodePacked, keccak256, pad, parseAbi, toHex } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { eip712WalletActions, toSinglesigSmartAccount } from "viem/zksync"
import { createWalletClient, createPublicClient, hashTypedData, http } from "viem"
import { getViemChainFromConfig, writeContract } from "../tasks/utils"
import { getEIP712Domain, getViemChainFromConfig, writeContract } from "../tasks/utils"
import { getGeneralPaymasterInput, serializeTransaction } from "viem/zksync"
import hre from "hardhat";

Expand Down Expand Up @@ -209,7 +209,7 @@ describe("ERC20 interactions from Openfort Account", function () {
value: 0n,
callData: encodeFunctionData({
abi: mintAbi,
functionName: 'mint',
functionName: "mint",
args: [openfortAccountAddress, 10n]
})
},
Expand All @@ -218,7 +218,7 @@ describe("ERC20 interactions from Openfort Account", function () {
value: 0n,
callData: encodeFunctionData({
abi: mintAbi,
functionName: 'mint',
functionName: "mint",
args: [openfortAccountAddress, 20n]
})
},
Expand All @@ -227,7 +227,7 @@ describe("ERC20 interactions from Openfort Account", function () {
value: 0n,
callData: encodeFunctionData({
abi: mintAbi,
functionName: 'mint',
functionName: "mint",
args: [openfortAccountAddress, 30n]
})
},
Expand All @@ -236,46 +236,45 @@ describe("ERC20 interactions from Openfort Account", function () {
value: 0n,
callData: encodeFunctionData({
abi: mintAbi,
functionName: 'mint',
functionName: "mint",
args: [openfortAccountAddress, 40n]
})
}
];

const abi = [
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Call[]",
name: "calls",
type: "tuple[]"
}
],
internalType: "struct Call[]",
name: "calls",
type: "tuple[]"
}
],
name: "batchCall",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
name: "batchCall",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];


const data = encodeFunctionData({
abi: abi,
functionName: "batchCall",
Expand Down Expand Up @@ -337,4 +336,44 @@ describe("ERC20 interactions from Openfort Account", function () {
// Assert that the final balance is the initial balance plus the sum of all minted amounts
expect(finalBalance - initialBalance).to.equal(10n + 20n + 30n + 40n);
});
})

it("should validate the EIP-712 signature correctly with the given message structure", async function () {
// keccak256("OpenfortMessage(bytes32 message)")
const OF_MSG_TYPEHASH = "0x57159f03b9efda178eab2037b2ec0b51ce11be0051b8a2a9992c29dc260e4a30"
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
const TYPE_HASH = "0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f"

const messageToSign = "Signed by Owner"
const hash = keccak256(new TextEncoder().encode(messageToSign))
const structHash = keccak256(encodePacked(["bytes32", "bytes32"], [OF_MSG_TYPEHASH, hash]))

const [,name, version, chainId, verifyingContract,,] = await getEIP712Domain(openfortAccountAddress, publicClient)

// Manually calculate domain separator
// to include TYPE_HASH
const domainSeparator = keccak256(
concat([
TYPE_HASH,
pad(keccak256(Buffer.from(name)), { size: 32 }),
pad(keccak256(Buffer.from(version)), { size: 32 }),
pad(toHex(chainId), { size: 32 }),
pad(verifyingContract, { size: 32 })
])
);
const hashToSign = keccak256(concat(["0x1901", domainSeparator, structHash]))
// Sign the message
const signature = await owner.sign({ hash: hashToSign })
const abi = parseAbi(["function isValidSignature(bytes32 _hash, bytes memory _signature) external view returns (bytes4)"]);
const isValid = await publicClient.readContract({
address: openfortAccountAddress,
abi,
functionName: "isValidSignature",
args: [hash, signature],
});
// Assert that the signature is valid
expect(isValid).to.equal("0x1626ba7e"); // EIP1271_SUCCESS_RETURN_VALUE
});
})



0 comments on commit 7861bfb

Please sign in to comment.