Skip to content

Commit ad06bc7

Browse files
committed
fix: update for tests
1 parent 2bc0264 commit ad06bc7

File tree

8 files changed

+194
-69
lines changed

8 files changed

+194
-69
lines changed

.github/workflows/ci.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ jobs:
144144
- name: Install foundry
145145
uses: foundry-rs/foundry-toolchain@v1.5.0
146146

147-
# Start Anvil on port 8545 (chain-id 31337)
147+
# Start Anvil on port 8545 (chain-id 1337 to match erc4337-contracts)
148148
- name: Start Anvil
149149
run: |
150-
anvil --host 0.0.0.0 &
150+
anvil --host 0.0.0.0 --chain-id 1337 &
151151
echo "Waiting for Anvil to be ready..."
152152
timeout 30 bash -c 'until cast client --rpc-url http://localhost:8545 > /dev/null 2>&1; do sleep 1; done'
153153
echo "Anvil is ready!"
@@ -173,10 +173,26 @@ jobs:
173173
- name: Build sdk-4337
174174
run: pnpm nx build sdk-4337
175175

176+
# Deploy ERC-4337 contracts (MSAFactory, validators, etc.) to Anvil
177+
# This also generates contracts-anvil.json with the deployed addresses
178+
- name: Deploy ERC-4337 contracts
179+
run: pnpm nx deploy-msa-factory nft-quest
180+
176181
# Deploy NFT contract to Anvil
177182
- name: Deploy NFT contract
178183
run: pnpm nx deploy:anvil nft-quest-contracts
179184

185+
# Start Alto bundler with CORS proxy for account abstraction
186+
- name: Start Alto bundler with CORS proxy
187+
run: |
188+
pnpm run bundler:with-proxy &
189+
BUNDLER_PID=$!
190+
echo "BUNDLER_PID=$BUNDLER_PID" >> $GITHUB_ENV
191+
echo "Waiting for bundler to be ready on port 4337..."
192+
timeout 60 bash -c 'until curl -s http://localhost:4337 > /dev/null 2>&1; do sleep 1; done'
193+
echo "Bundler is ready!"
194+
working-directory: packages/erc4337-contracts
195+
180196
# Run E2E tests
181197
- name: Install Playwright Chromium Browser
182198
run: pnpm exec playwright install chromium
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"eoaValidator": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
3-
"webauthnValidator": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
4-
"sessionValidator": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
5-
"factory": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
2+
"eoaValidator": "0xde612c1b1C06982551157FB53E61a123c6045DFC",
3+
"webauthnValidator": "0xE854be130245223cBDD20c85f4F126996669Cf08",
4+
"sessionValidator": "0x41b6892Bf7aFB4feeACDd96AAf34205124380A47",
5+
"factory": "0x099961116Ba4371a2516034233177Ee3eFcdaf39",
66
"bundlerUrl": "http://localhost:4337"
77
}

examples/nft-quest/playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default defineConfig({
2222
/* Opt out of parallel tests on CI. */
2323
workers: process.env.CI ? 1 : undefined,
2424
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
25-
reporter: "html",
25+
reporter: [["html", { open: "never" }]],
2626
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
2727
use: {
2828
/* Base URL to use in actions like `await page.goto('/')`. */

examples/nft-quest/project.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@
8888
"command": "pnpm exec playwright install chromium"
8989
}
9090
},
91+
"deploy-msa-factory": {
92+
"executor": "nx:run-commands",
93+
"options": {
94+
"cwd": "examples/nft-quest",
95+
"command": "./scripts/deploy-msa-anvil.sh"
96+
},
97+
"dependsOn": ["build-erc4337-contracts"]
98+
},
99+
"build-erc4337-contracts": {
100+
"executor": "nx:run-commands",
101+
"options": {
102+
"cwd": "packages/erc4337-contracts",
103+
"command": "forge build"
104+
}
105+
},
91106
"e2e": {
92107
"executor": "nx:run-commands",
93108
"options": {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/bash
2+
set -ex
3+
4+
# Get the directory where this script is located
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
# Navigate to the workspace root (3 levels up from scripts/)
7+
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
8+
9+
# Configuration
10+
DEPLOYER_ADDRESS="0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" # Anvil account #9
11+
RPC_URL="http://localhost:8545"
12+
CHAIN_ID=1337
13+
CONTRACTS_DIR="$WORKSPACE_ROOT/packages/erc4337-contracts"
14+
15+
echo "🚀 Deploying MSA Factory and modules to Anvil (standard EVM)..."
16+
echo ""
17+
echo "📍 Deployer: $DEPLOYER_ADDRESS"
18+
echo "🌐 RPC URL: $RPC_URL"
19+
echo ""
20+
21+
cd "$CONTRACTS_DIR"
22+
23+
# Build contracts first to ensure everything is compiled
24+
echo "🔨 Building contracts..."
25+
forge build
26+
27+
# Use pnpm deploy-test to deploy all contracts
28+
echo ""
29+
echo "📦 Deploying contracts using pnpm deploy-test..."
30+
DEPLOY_OUTPUT=$(pnpm deploy-test 2>&1)
31+
32+
echo "$DEPLOY_OUTPUT"
33+
34+
# Extract addresses from the deployment output
35+
EOA_VALIDATOR=$(echo "$DEPLOY_OUTPUT" | grep "EOAKeyValidator:" | awk '{print $2}')
36+
SESSION_VALIDATOR=$(echo "$DEPLOY_OUTPUT" | grep "SessionKeyValidator:" | awk '{print $2}')
37+
WEBAUTHN_VALIDATOR=$(echo "$DEPLOY_OUTPUT" | grep "WebAuthnValidator:" | awk '{print $2}')
38+
GUARDIAN_EXECUTOR=$(echo "$DEPLOY_OUTPUT" | grep "GuardianExecutor:" | awk '{print $2}')
39+
ACCOUNT_IMPL=$(echo "$DEPLOY_OUTPUT" | grep "ModularSmartAccount implementation:" | awk '{print $3}')
40+
BEACON=$(echo "$DEPLOY_OUTPUT" | grep "UpgradeableBeacon:" | awk '{print $2}')
41+
FACTORY=$(echo "$DEPLOY_OUTPUT" | grep "MSAFactory:" | awk '{print $2}')
42+
43+
# Verify all addresses were extracted
44+
if [ -z "$EOA_VALIDATOR" ] || [ -z "$SESSION_VALIDATOR" ] || [ -z "$WEBAUTHN_VALIDATOR" ] || \
45+
[ -z "$GUARDIAN_EXECUTOR" ] || [ -z "$ACCOUNT_IMPL" ] || [ -z "$BEACON" ] || [ -z "$FACTORY" ]; then
46+
echo "❌ Failed to extract all contract addresses from deployment output"
47+
echo "Please check the deployment logs above"
48+
exit 1
49+
fi
50+
51+
echo ""
52+
echo "✅ Deployment complete!"
53+
echo " EOAKeyValidator: $EOA_VALIDATOR"
54+
echo " SessionKeyValidator: $SESSION_VALIDATOR"
55+
echo " WebAuthnValidator: $WEBAUTHN_VALIDATOR"
56+
echo " GuardianExecutor: $GUARDIAN_EXECUTOR"
57+
echo " ModularSmartAccount impl: $ACCOUNT_IMPL"
58+
echo " UpgradeableBeacon: $BEACON"
59+
echo " MSAFactory: $FACTORY"
60+
61+
# Create contracts-anvil.json
62+
echo ""
63+
echo "💾 Creating contracts-anvil.json..."
64+
cd "$WORKSPACE_ROOT/examples/nft-quest"
65+
66+
cat > contracts-anvil.json << EOF
67+
{
68+
"eoaValidator": "$EOA_VALIDATOR",
69+
"webauthnValidator": "$WEBAUTHN_VALIDATOR",
70+
"sessionValidator": "$SESSION_VALIDATOR",
71+
"factory": "$FACTORY",
72+
"bundlerUrl": "http://localhost:4337"
73+
}
74+
EOF
75+
76+
echo "✅ Created contracts-anvil.json"
77+
78+
# Copy to public directory for the Nuxt app
79+
cp contracts-anvil.json public/contracts.json
80+
echo "✅ Copied to public/contracts.json"
81+
82+
echo ""
83+
echo "🎉 Deployment complete!"
84+
echo ""
85+
echo "📝 Contract Addresses (Anvil/Alto):"
86+
cat contracts-anvil.json

examples/nft-quest/stores/client.ts

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import type { Address, Hex } from "viem";
22
import { createPublicClient, http } from "viem";
33
import { createBundlerClient } from "viem/account-abstraction";
44
import type { Chain } from "viem/chains";
5-
import { getGeneralPaymasterInput } from "viem/zksync";
65
import { createPasskeyClient } from "zksync-sso-4337/client";
76

8-
// Anvil chain configuration (chain ID 31337)
7+
import contractsConfig from "../contracts-anvil.json";
8+
9+
// Anvil chain configuration (chain ID 1337 to match erc4337-contracts setup)
910
const anvilChain: Chain = {
10-
id: 31337,
11+
id: 1337,
1112
name: "Anvil",
1213
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
1314
rpcUrls: {
@@ -29,26 +30,13 @@ export const useClientStore = defineStore("client", () => {
2930

3031
const getBundlerClient = () => {
3132
const publicClient = getPublicClient();
32-
const runtimeConfig = useRuntimeConfig();
33-
const paymasterAddress = runtimeConfig.public.contracts.paymaster as Address;
34-
const bundlerUrl = runtimeConfig.public.bundlerUrl as string;
33+
const bundlerUrl = contractsConfig.bundlerUrl;
3534

3635
return createBundlerClient({
3736
client: publicClient,
3837
chain,
3938
transport: http(bundlerUrl),
40-
paymaster: {
41-
async getPaymasterData() {
42-
return {
43-
paymasterAndData: `${paymasterAddress}${getGeneralPaymasterInput({ innerInput: "0x" }).substring(2)}` as Hex,
44-
};
45-
},
46-
async getPaymasterStubData() {
47-
return {
48-
paymasterAndData: `${paymasterAddress}${getGeneralPaymasterInput({ innerInput: "0x" }).substring(2)}` as Hex,
49-
};
50-
},
51-
},
39+
// EntryPoint 0.8 - no paymaster for now to simplify testing
5240
userOperation: {
5341
async estimateFeesPerGas() {
5442
const feesPerGas = await publicClient.estimateFeesPerGas();
@@ -67,13 +55,12 @@ export const useClientStore = defineStore("client", () => {
6755
if (!address.value) throw new Error("Address is not set");
6856
if (!credentialId.value) throw new Error("Credential ID is not set");
6957

70-
const runtimeConfig = useRuntimeConfig();
7158
const bundlerClient = getBundlerClient();
7259

7360
const client = createPasskeyClient({
7461
account: {
7562
address: address.value,
76-
validatorAddress: runtimeConfig.public.contracts.webauthnValidator as Address,
63+
validatorAddress: contractsConfig.webauthnValidator as Address,
7764
credentialId: credentialId.value,
7865
rpId: window.location.hostname,
7966
origin: window.location.origin,
@@ -93,13 +80,12 @@ export const useClientStore = defineStore("client", () => {
9380
address: Address;
9481
credentialId: Hex;
9582
}) => {
96-
const runtimeConfig = useRuntimeConfig();
9783
const bundlerClient = getBundlerClient();
9884

9985
return createPasskeyClient({
10086
account: {
10187
address: addr,
102-
validatorAddress: runtimeConfig.public.contracts.webauthnValidator as Address,
88+
validatorAddress: contractsConfig.webauthnValidator as Address,
10389
credentialId: credId,
10490
rpId: window.location.hostname,
10591
origin: window.location.origin,

examples/nft-quest/stores/connector.ts

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
import { startRegistration } from "@simplewebauthn/browser";
22
import type { Address, Hex } from "viem";
3-
import { concat, decodeErrorResult, encodeFunctionData } from "viem";
4-
import { entryPoint07Address } from "viem/account-abstraction";
5-
import { prepareDeploySmartAccount } from "zksync-sso-4337/client";
3+
import { createWalletClient, http, parseEther } from "viem";
4+
import { privateKeyToAccount } from "viem/accounts";
5+
import type { Chain } from "viem/chains";
6+
import { getAccountAddressFromLogs, prepareDeploySmartAccount } from "zksync-sso-4337/client";
67

78
import contractsConfig from "../contracts-anvil.json";
89
import { useAccountStore } from "./account";
910
import { useClientStore } from "./client";
1011

12+
// Anvil's default funded account (first account)
13+
const DEPLOYER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as Hex;
14+
15+
// Anvil chain configuration (chain ID 1337 to match erc4337-contracts setup)
16+
const anvilChain: Chain = {
17+
id: 1337,
18+
name: "Anvil",
19+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
20+
rpcUrls: {
21+
default: { http: ["http://127.0.0.1:8545"] },
22+
},
23+
};
24+
1125
export const useConnectorStore = defineStore("connector", () => {
1226
const accountStore = useAccountStore();
1327
const { address, isConnected } = storeToRefs(accountStore);
@@ -135,41 +149,49 @@ export const useConnectorStore = defineStore("connector", () => {
135149
}],
136150
});
137151

138-
const initCode = concat([transaction.to, transaction.data]);
139-
const entryPoint = entryPoint07Address;
140-
141-
let senderAddress: Address;
142-
try {
143-
await publicClient.call({
144-
to: entryPoint,
145-
data: encodeFunctionData({
146-
abi: [{
147-
name: "getSenderAddress",
148-
type: "function",
149-
stateMutability: "view",
150-
inputs: [{ name: "initCode", type: "bytes" }],
151-
outputs: [],
152-
}],
153-
functionName: "getSenderAddress",
154-
args: [initCode],
155-
}),
156-
});
157-
throw new Error("getSenderAddress did not revert");
158-
} catch (e) {
159-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
160-
const data = (e as any).data || (e as any).cause?.data || (e as any).cause?.cause?.data;
161-
if (!data) throw e;
162-
163-
const error = decodeErrorResult({
164-
abi: [{
165-
name: "SenderAddressResult",
166-
type: "error",
167-
inputs: [{ name: "sender", type: "address" }],
168-
}],
169-
data,
170-
});
171-
senderAddress = error.args[0];
172-
}
152+
// eslint-disable-next-line no-console
153+
console.log("Prepared deployment transaction to factory:", transaction.to);
154+
155+
// Deploy the account using a funded deployer wallet
156+
const deployerAccount = privateKeyToAccount(DEPLOYER_PRIVATE_KEY);
157+
const walletClient = createWalletClient({
158+
account: deployerAccount,
159+
chain: anvilChain,
160+
transport: http(),
161+
});
162+
163+
// Send the deployment transaction
164+
// eslint-disable-next-line no-console
165+
console.log("Deploying account...");
166+
const deployTxHash = await walletClient.sendTransaction({
167+
to: transaction.to,
168+
data: transaction.data,
169+
});
170+
171+
// eslint-disable-next-line no-console
172+
console.log("Deploy tx hash:", deployTxHash);
173+
174+
// Wait for deployment to complete and get the receipt with logs
175+
const receipt = await publicClient.waitForTransactionReceipt({ hash: deployTxHash });
176+
177+
// eslint-disable-next-line no-console
178+
console.log("Deployment receipt received, logs count:", receipt.logs.length);
179+
180+
// Get the deployed account address from the AccountCreated event
181+
const senderAddress = getAccountAddressFromLogs(receipt.logs);
182+
183+
// eslint-disable-next-line no-console
184+
console.log("Account deployed at:", senderAddress);
185+
186+
// Fund the smart account with some ETH for gas
187+
const fundTxHash = await walletClient.sendTransaction({
188+
to: senderAddress,
189+
value: parseEther("1"),
190+
});
191+
await publicClient.waitForTransactionReceipt({ hash: fundTxHash });
192+
193+
// eslint-disable-next-line no-console
194+
console.log("Account funded!");
173195

174196
accountStore.setAccount(senderAddress, credIdHex);
175197
return { address: senderAddress, credentialId: credIdHex };

packages/sdk-4337/src/client/actions/deploy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ export function prepareDeploySmartAccount(
164164

165165
const encodedCallData = encode_deploy_account_call_data(
166166
accountId,
167-
eoaSigners || [],
168-
contracts.eoaValidator || null,
167+
hasEoaSigners ? eoaSigners : null,
168+
hasEoaSigners ? contracts.eoaValidator : null,
169169
// eslint-disable-next-line @typescript-eslint/no-explicit-any
170170
passkeyPayload as any,
171171
contracts.webauthnValidator || null,

0 commit comments

Comments
 (0)