Skip to content

Commit 03f2cea

Browse files
authored
Add zksync os setup in CI (#50)
* zksync os ci * use yarn * fix tests * add caching and wait until bundler is ready * always print logs * reduce timeout
1 parent 6f7eef7 commit 03f2cea

File tree

7 files changed

+132
-25
lines changed

7 files changed

+132
-25
lines changed

.github/workflows/ci.yaml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,91 @@ jobs:
127127

128128
- name: Run integration tests
129129
run: pnpm test
130+
131+
zksync-os:
132+
runs-on: ubuntu-latest
133+
steps:
134+
- name: Checkout the repository
135+
uses: actions/checkout@v5
136+
137+
- name: Install Foundry
138+
uses: foundry-rs/[email protected]
139+
with:
140+
# zksync-os-server's zkos-l1-state.json doesn't work on latest version of anvil
141+
version: v1.3.4
142+
143+
- name: Install dependencies
144+
run: forge soldeer install
145+
146+
- name: Build contracts
147+
run: forge build
148+
149+
- name: Setup pnpm
150+
uses: pnpm/action-setup@v4
151+
with:
152+
version: 10.20.0
153+
154+
- name: Use Node.js
155+
uses: actions/setup-node@v6
156+
with:
157+
node-version: lts/Jod
158+
cache: pnpm
159+
160+
- name: Install dependencies
161+
run: |
162+
pnpm install -r --frozen-lockfile
163+
cd ${{ github.workspace }}/..
164+
git clone https://github.com/eth-infinitism/account-abstraction
165+
git clone https://github.com/matter-labs/zksync-os-server
166+
167+
- name: Cache Rust build
168+
uses: Swatinem/rust-cache@v2
169+
with:
170+
workspaces: "../zksync-os-server -> target"
171+
172+
- name: Run server
173+
run: |
174+
cd ${{ github.workspace }}/../zksync-os-server
175+
cargo build --release --bin zksync-os-server
176+
anvil --load-state zkos-l1-state.json --port 8545 &> anvil.log &
177+
cargo run --release --bin zksync-os-server &> server.log &
178+
179+
- name: Deploy entrypoint
180+
run: |
181+
cd ${{ github.workspace }}/../account-abstraction
182+
git checkout v0.8.0
183+
sed -i "60a zksyncos: { url: 'http://localhost:3050', accounts: ['0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110'] }," hardhat.config.ts
184+
npm install -g yarn
185+
yarn install
186+
yarn deploy --network zksyncos
187+
188+
- name: Fund wallet
189+
run: |
190+
# Rich account
191+
PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110
192+
# send 10 ETH
193+
TO=0xa0Ee7A142d267C1f36714E4a8F75612F20a79720
194+
cast send --private-key ${PRIVATE_KEY} --rpc-url http://localhost:3050 ${TO} --value 10000000000000000000
195+
196+
- name: Deploy contracts and a test account
197+
run: pnpm deploy-test:zksync-os
198+
199+
- name: Run bundler
200+
run: |
201+
pnpm bundler:zksync-os &> bundler.log &
202+
sleep 5
203+
204+
- name: Run integration tests
205+
run: pnpm test:zksync-os
206+
207+
- name: Print anvil logs
208+
if: always()
209+
run: cat ${{ github.workspace }}/../zksync-os-server/anvil.log
210+
211+
- name: Print server logs
212+
if: always()
213+
run: cat ${{ github.workspace }}/../zksync-os-server/server.log
214+
215+
- name: Print bundler logs
216+
if: always()
217+
run: cat bundler.log

alto-zksync-os.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"entrypoints": "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
3+
"executor-private-keys": "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110",
4+
"utility-private-key": "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110",
5+
"rpc-url": "http://localhost:3050",
6+
"port": 4337,
7+
"safe-mode": false
8+
}

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
"scripts": {
77
"bundler": "alto --config ./alto.json",
88
"bundler:with-proxy": "node js/bundler-with-proxy.js",
9+
"bundler:zksync-os": "alto --config ./alto-zksync-os.json",
910
"bundler-proxy": "node js/bundler-proxy.js",
1011
"test": "node --test --import tsx --test-concurrency=1 test/integration/*.test.ts",
12+
"test:zksync-os": "CHAIN_ID=6565 PORT=3050 node --test --import tsx --test-concurrency=1 test/integration/*.test.ts",
1113
"anvil": "anvil --fork-url https://reth-ethereum.ithaca.xyz/rpc --chain-id 1337",
1214
"coverage": "forge coverage --ir-minimum --no-match-coverage 'script|test'",
1315
"//": "Uses the last private key of anvil's rich accounts list",
14-
"deploy-test": "forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 --sig 'deployAll()'"
16+
"deploy-test": "forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 --sig 'deployAll()'",
17+
"deploy-test:zksync-os": "forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:3050 --broadcast --private-key 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 --sig 'deployAll()'"
1518
},
1619
"keywords": [
1720
"zksync",

test/integration/account.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from "viem/account-abstraction";
1717

1818
import { hashTypedData, wrapTypedDataSignature } from "viem/experimental/erc7739";
19+
import { chainId } from "./utils";
1920

2021
const callAbi = [{
2122
components: [
@@ -78,7 +79,7 @@ export class SsoAccount {
7879
userOperation: { ...userOperation, sender: address },
7980
entryPointAddress: entryPoint08Address,
8081
entryPointVersion: '0.8',
81-
chainId: 1337
82+
chainId: chainId(),
8283
});
8384
return await sso.signer(userOpHash);
8485
},
@@ -96,7 +97,7 @@ export class SsoAccount {
9697
},
9798
async signTypedData(typedData) {
9899
const verifierDomain = {
99-
chainId: 1337,
100+
chainId: chainId(),
100101
name: "zksync-sso-1271",
101102
version: "1.0.0",
102103
verifyingContract: address,

test/integration/basic.test.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import { parseAbi } from "viem";
44
import { privateKeyToAccount } from "viem/accounts";
55

66
import { SsoAccount } from "./account";
7-
import { contractAddresses, toEOASigner, createClients, randomAddress, deployContract } from "./utils";
7+
import { contractAddresses, toEOASigner, createClients, randomAddress, deployContract, chainId, rpcPort } from "./utils";
88

9-
const anvilPort = 8545;
109
const altoPort = require("../../alto.json").port;
1110
const privateKey = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6";
1211

1312
test("executes a simple transfer signed using EOA", { timeout: 120_000 }, async () => {
1413
const { account } = contractAddresses();
15-
const { client, bundlerClient } = createClients(anvilPort, altoPort);
14+
const { client, bundlerClient } = createClients(rpcPort(), altoPort);
1615
const sso = await SsoAccount.create(client, account, toEOASigner(privateKey));
1716

1817
const target = randomAddress();
@@ -24,7 +23,7 @@ test("executes a simple transfer signed using EOA", { timeout: 120_000 }, async
2423
}],
2524
});
2625

27-
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 0 });
26+
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 2_000 });
2827
assert.equal(
2928
receipt.receipt.status,
3029
"success",
@@ -37,7 +36,7 @@ test("executes a simple transfer signed using EOA", { timeout: 120_000 }, async
3736

3837
test("executes a transaction sponsored by a paymaster", { timeout: 120_000 }, async () => {
3938
const { account } = contractAddresses();
40-
const { client, bundlerClient } = createClients(anvilPort, altoPort);
39+
const { client, bundlerClient } = createClients(rpcPort(), altoPort);
4140
const sso = await SsoAccount.create(client, account, toEOASigner(privateKey));
4241
const deployer = privateKeyToAccount(privateKey);
4342
const paymaster = await deployContract(client, privateKey, "MockPaymaster");
@@ -47,9 +46,9 @@ test("executes a transaction sponsored by a paymaster", { timeout: 120_000 }, as
4746
abi: parseAbi(["function deposit() external payable"]),
4847
functionName: "deposit",
4948
args: [],
50-
value: 10n ** 18n, // 1 ETH
49+
value: 10n ** 17n, // 0.1 ETH
5150
})
52-
await client.waitForTransactionReceipt({ hash: depositHash, timeout: 0 });
51+
await client.waitForTransactionReceipt({ hash: depositHash, timeout: 2_000 });
5352

5453
const balanceBefore = await client.getBalance({ address: account });
5554
const sponsored = await bundlerClient.sendUserOperation({
@@ -58,7 +57,7 @@ test("executes a transaction sponsored by a paymaster", { timeout: 120_000 }, as
5857
paymaster,
5958
});
6059

61-
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: sponsored, timeout: 0 });
60+
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: sponsored, timeout: 2_000 });
6261
const balanceAfter = await client.getBalance({ address: account });
6362

6463
assert.equal(receipt.receipt.status, "success", "sponsored user operation should execute successfully");
@@ -67,7 +66,7 @@ test("executes a transaction sponsored by a paymaster", { timeout: 120_000 }, as
6766

6867
test("checks ERC7739 EOA signature using ERC1271", { timeout: 120_000 }, async () => {
6968
const { account } = contractAddresses();
70-
const { client } = createClients(anvilPort, altoPort);
69+
const { client } = createClients(rpcPort(), altoPort);
7170
const sso = await SsoAccount.create(client, account, toEOASigner(privateKey));
7271

7372
const erc1271Caller = await deployContract(client, privateKey, "MockERC1271Caller");
@@ -84,7 +83,7 @@ test("checks ERC7739 EOA signature using ERC1271", { timeout: 120_000 }, async (
8483
]
8584
},
8685
domain: {
87-
chainId: 1337,
86+
chainId: chainId(),
8887
name: "ERC1271Caller",
8988
version: "1.0.0",
9089
verifyingContract: erc1271Caller,

test/integration/passkey.test.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import crypto from "crypto";
44
import { encodeFunctionData, toHex, parseAbi } from "viem";
55

66
import { SsoAccount } from "./account";
7-
import { contractAddresses, toEOASigner, toPasskeySigner, createClients, randomAddress, deployContract } from "./utils";
7+
import { contractAddresses, toEOASigner, toPasskeySigner, createClients, randomAddress, deployContract, chainId, rpcPort } from "./utils";
88

9-
const anvilPort = 8545;
109
const altoPort = require("../../alto.json").port;
1110
const privateKey = "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6";
1211

@@ -20,7 +19,7 @@ const publicKey = {
2019

2120
test("adds a Passkey to the account", { timeout: 120_000 }, async () => {
2221
const { account, webauthnValidator } = contractAddresses();
23-
const { client, bundlerClient } = createClients(anvilPort, altoPort);
22+
const { client, bundlerClient } = createClients(rpcPort(), altoPort);
2423
const sso = await SsoAccount.create(client, account, toEOASigner(privateKey));
2524

2625
// add validation key via the passkey validator contract
@@ -36,7 +35,7 @@ test("adds a Passkey to the account", { timeout: 120_000 }, async () => {
3635
}],
3736
});
3837

39-
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 0 });
38+
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 2_000 });
4039
assert.equal(
4140
receipt.receipt.status,
4241
"success",
@@ -46,7 +45,7 @@ test("adds a Passkey to the account", { timeout: 120_000 }, async () => {
4645

4746
test("executes a simple transfer signed using Passkey", { timeout: 120_000 }, async () => {
4847
const { account } = contractAddresses();
49-
const { client, bundlerClient } = createClients(anvilPort, altoPort);
48+
const { client, bundlerClient } = createClients(rpcPort(), altoPort);
5049
const sso = await SsoAccount.create(client, account, toPasskeySigner(keyPair.privateKey, credentialId));
5150

5251
// transfer to a random address using passkey signer
@@ -59,7 +58,7 @@ test("executes a simple transfer signed using Passkey", { timeout: 120_000 }, as
5958
}],
6059
});
6160

62-
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 0 });
61+
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash, timeout: 2_000 });
6362
assert.equal(
6463
receipt.receipt.status,
6564
"success",
@@ -72,7 +71,7 @@ test("executes a simple transfer signed using Passkey", { timeout: 120_000 }, as
7271

7372
test("checks ERC7739 Passkey signature using ERC1271", { timeout: 120_000 }, async () => {
7473
const { account } = contractAddresses();
75-
const { client } = createClients(anvilPort, altoPort);
74+
const { client } = createClients(rpcPort(), altoPort);
7675
const sso = await SsoAccount.create(client, account, toPasskeySigner(keyPair.privateKey, credentialId));
7776

7877
const erc1271Caller = await deployContract(client, privateKey, "MockERC1271Caller");
@@ -89,7 +88,7 @@ test("checks ERC7739 Passkey signature using ERC1271", { timeout: 120_000 }, asy
8988
]
9089
},
9190
domain: {
92-
chainId: 1337,
91+
chainId: chainId(),
9392
name: "ERC1271Caller",
9493
version: "1.0.0",
9594
verifyingContract: erc1271Caller,

test/integration/utils.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@ import {
88
concat,
99
type Hex,
1010
type Address,
11-
type WalletClient,
12-
type PublicClient,
1311
} from "viem";
1412
import { privateKeyToAccount } from "viem/accounts";
1513
import { createBundlerClient } from "viem/account-abstraction";
1614
import { localhost } from "viem/chains";
1715

1816
import crypto from "crypto";
1917

18+
export function chainId() {
19+
return Number(process.env.CHAIN_ID || '1337');
20+
}
21+
22+
export function rpcPort() {
23+
return Number(process.env.PORT || '8545');
24+
}
25+
2026
export function contractAddresses() {
21-
const txs = require('../../broadcast/Deploy.s.sol/1337/deployAll-latest.json').transactions;
27+
const txs = require(`../../broadcast/Deploy.s.sol/${chainId()}/deployAll-latest.json`).transactions;
2228
return {
2329
eoaValidator: txs[1].contractAddress as Address,
2430
sessionValidator: txs[3].contractAddress as Address,
@@ -33,6 +39,9 @@ export function createClients(anvilPort: number, bundlerPort: number) {
3339
// smaller polling interval to speed up the test
3440
const pollingInterval = 100;
3541

42+
// @ts-ignore
43+
localhost.id = chainId();
44+
3645
const client = createPublicClient({
3746
chain: localhost,
3847
transport: http(`http://localhost:${anvilPort}`),
@@ -59,7 +68,7 @@ export async function deployContract(client: any, privateKey: Hex, name: string)
5968
abi: [],
6069
bytecode: require(`../../out/${name}.sol/${name}.json`).bytecode.object
6170
});
62-
const deployment = await client.waitForTransactionReceipt({ hash: deploymentHash, timeout: 100 });
71+
const deployment = await client.waitForTransactionReceipt({ hash: deploymentHash, timeout: 2_000 });
6372
return deployment.contractAddress!;
6473
}
6574

0 commit comments

Comments
 (0)