Skip to content
Open
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
150 changes: 90 additions & 60 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,63 +129,93 @@ jobs:
path: packages/auth-server/playwright-report/
retention-days: 3

# e2e-nft-quest:
# runs-on: ubuntu-latest
# defaults:
# run:
# working-directory: ./
# steps:
# - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
# with:
# submodules: recursive

# # Start node
# - name: Era Test Node Action
# uses: dutterbutter/era-test-node-action@36ffd2eefd46dc16e7e2a8e1715124400ec0a3ba # v1

# - name: Setup pnpm
# uses: pnpm/action-setup@v4
# with:
# version: 9.11.0

# - name: Use Node.js
# uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
# with:
# node-version: lts/Iron
# cache: pnpm

# # Install dependencies for repo
# - name: Install dependencies
# run: pnpm install -r --frozen-lockfile

# # Install dependencies for the submodule
# - name: Install contract dependencies
# run: pnpm install -r --frozen-lockfile
# working-directory: packages/contracts

# - name: Build contracts
# run: pnpm build
# working-directory: packages/contracts

# - name: Deploy contracts
# run: pnpm run deploy --file ../auth-server/stores/local-node.json
# working-directory: packages/contracts

# - name: Build SDK
# run: pnpm nx build sdk

# - name: Deploy NFT contracts
# run: pnpm nx deploy:local nft-quest-contracts

# # Run E2E tests
# - name: Install Playwright Chromium Browser
# run: pnpm exec playwright install chromium
# working-directory: examples/nft-quest
# - name: Run e2e tests
# run: pnpm nx e2e nft-quest
# - uses: actions/upload-artifact@v4
# if: ${{ !cancelled() }}
# with:
# name: nft-quest-playwright-report
# path: examples/nft-quest/playwright-report/
# retention-days: 3
# TODO: Consider sharing CI infrastructure between demo-app and nft-quest
# Currently duplicated for test isolation and independence
e2e-nft-quest:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
defaults:
run:
working-directory: ./
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
submodules: recursive

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.11.0

- name: Use Node.js
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version: lts/Iron
cache: pnpm

# Install Foundry for Anvil and ERC-4337 contract deployment
- name: Install foundry
uses: foundry-rs/foundry-toolchain@v1.5.0

# Start Anvil for ERC-4337 (standard EVM on port 8545)
- name: Start Anvil
run: |
pnpm run anvil &
echo "Waiting for Anvil to be ready..."
timeout 30 bash -c 'until cast client --rpc-url http://localhost:8545 > /dev/null 2>&1; do sleep 1; done'
echo "Anvil is ready!"
working-directory: packages/erc4337-contracts

# Install dependencies for repo
- name: Install dependencies
run: pnpm install -r --frozen-lockfile

# Install and build ERC-4337 contracts
- name: Install ERC-4337 contract dependencies
run: forge soldeer install
working-directory: packages/erc4337-contracts

- name: Build ERC-4337 contracts
run: forge build
working-directory: packages/erc4337-contracts

# Build ERC-4337 SDK packages
- name: Build ERC-4337 related packages
run: |
pnpm nx build web-sdk
pnpm nx build sdk-4337
pnpm nx build:erc4337 demo-app

# Deploy MSA Factory and infrastructure (nft-quest will read from contracts-anvil.json)
- name: Deploy Demo-App ERC-4337 contracts
run: pnpm nx deploy-msa-factory demo-app

# Start Alto bundler with CORS proxy
- name: Start Alto bundler with CORS proxy
run: |
pnpm run bundler:with-proxy &
BUNDLER_PID=$!
echo "BUNDLER_PID=$BUNDLER_PID" >> $GITHUB_ENV
echo "Waiting for bundler to be ready on port 4337..."
timeout 60 bash -c 'until curl -s http://localhost:4337 > /dev/null 2>&1; do sleep 1; done'
echo "Bundler is ready!"
working-directory: packages/erc4337-contracts

# Deploy NFT Quest contracts (reads MSA infrastructure from contracts-anvil.json)
- name: Deploy NFT contracts
run: pnpm nx deploy:local nft-quest-contracts

# Run E2E tests
- name: Install Playwright Chromium Browser
run: pnpm exec playwright install chromium
working-directory: examples/nft-quest
- name: Run e2e tests
run: pnpm nx e2e nft-quest
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: nft-quest-playwright-report
path: examples/nft-quest/playwright-report/
retention-days: 3
22 changes: 22 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- nx configuration start-->
<!-- Leave the start & end comments to automatically receive updates. -->

# General Guidelines for working with Nx

- When running tasks (for example build, lint, test, e2e, etc.), always prefer
running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`)
instead of using the underlying tooling directly
- You have access to the Nx MCP server and its tools, use them to help the user
- When answering questions about the repository, use the `nx_workspace` tool
first to gain an understanding of the workspace architecture where applicable.
- When working in individual projects, use the `nx_project_details` mcp tool to
analyze and understand the specific project structure and dependencies
- For questions around nx configuration, best practices or if you're unsure, use
the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead
of assuming things about nx configuration
- If the user needs help with an Nx configuration or project graph error, use
the `nx_workspace` tool to get any errors
- For Nx plugin best practices, check `node_modules/@nx/<plugin>/PLUGIN.md`. Not
all plugins have this file - proceed without it if unavailable.

<!-- nx configuration end-->
66 changes: 30 additions & 36 deletions cspell-config/cspell-misc.txt
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
// auth-server
// examples/demo-app
// examples/nft-quest
// packages/auther-server-api
// packages/bundler
// paymasters
// zk and circom
alice
cancun
clsx
COSEALG
COSEKTY
ctap
debian
distroless
dockerized
ethereum
EVMLA
foundryup
fren
Fren
groth
Groth
jwks
lintstagedrc
merkle
nfts
nuxt
Nuxt
nuxtjs
testid
vueuse
dockerized
ethereum
sepolia
foundryup
unpermitted

// auth-server
oidc
Oidc
jwks
merkle

// zk and circom
Groth
groth
zkey
pimlico
rustup
sepolia
snark
snarkjs

// examples/demo-app
testid
unpermitted
unstake
vueuse
wght
zkey
zkout
cancun
EVMLA

// paymasters
zyfi

// examples/nft-quest
Fren
fren
nfts
wght

// packages/auther-server-api
rustup
distroless
debian

// packages/bundler
pimlico
23 changes: 21 additions & 2 deletions examples/demo-app/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,44 @@ export default defineConfig({
],

/* Run local servers before starting the tests */
/* IMPORTANT: Order matters! Contracts must be deployed BEFORE auth-server-api starts,
* because auth-server-api reads contract addresses from contracts.json at startup.
* Playwright starts webServers sequentially and waits for each URL before starting the next.
*/
webServer: [
{
command: "PORT=3004 pnpm nx dev auth-server-api",
// Step 1: Deploy all contracts first (creates/updates contracts.json)
// The "server" is just a simple echo to satisfy playwright's URL check
command: "pnpm nx deploy-msa-factory demo-app && echo 'Contracts deployed' && node -e \"require('http').createServer((req,res)=>{res.writeHead(200);res.end('ok')}).listen(3099)\"",
url: "http://localhost:3099",
reuseExistingServer: !process.env.CI,
stdout: "pipe",
stderr: "pipe",
timeout: 180_000,
},
{
// Step 2: Start auth-server-api (reads fresh contracts.json)
// Run directly with node to ensure PORT env var is passed correctly
command: "cd ../../packages/auth-server-api && PORT=3004 node --experimental-wasm-modules --import tsx src/index.ts",
url: "http://localhost:3004/api/health",
reuseExistingServer: !process.env.CI,
stdout: "pipe",
stderr: "pipe",
timeout: 180_000,
},
{
// Step 3: Start auth-server (UI for account creation)
// Use dev:nuxt-only since we start auth-server-api separately in step 2
command:
"NUXT_PUBLIC_AUTH_SERVER_API_URL=http://localhost:3004 PORT=3002 pnpm nx dev:no-deploy auth-server",
"NUXT_PUBLIC_AUTH_SERVER_API_URL=http://localhost:3004 pnpm nx dev:nuxt-only auth-server",
url: "http://localhost:3002",
reuseExistingServer: !process.env.CI,
stdout: "pipe",
stderr: "pipe",
timeout: 180_000,
},
{
// Step 4: Start demo-app dev server (contracts already deployed in step 1)
command: "PORT=3005 pnpm nx dev demo-app",
url: "http://localhost:3005",
reuseExistingServer: !process.env.CI,
Expand Down
89 changes: 89 additions & 0 deletions examples/demo-app/scripts/check-paymaster-deposit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/bash
set -e

# Configuration
RPC_URL="http://localhost:8545"
ENTRYPOINT="0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"

# Read paymaster address from contracts-anvil.json
CONTRACTS_FILE="$WORKSPACE_ROOT/examples/demo-app/contracts-anvil.json"

if [ ! -f "$CONTRACTS_FILE" ]; then
echo "❌ contracts-anvil.json not found at $CONTRACTS_FILE"
echo "Please run deploy-msa-anvil.sh first"
exit 1
fi

PAYMASTER=$(jq -r '.testPaymaster // .mockPaymaster' "$CONTRACTS_FILE")

if [ -z "$PAYMASTER" ] || [ "$PAYMASTER" == "null" ]; then
echo "❌ Paymaster address not found in contracts-anvil.json"
exit 1
fi

echo "🔍 Checking paymaster deposit status..."
echo ""
echo "Paymaster address: $PAYMASTER"
echo "EntryPoint address: $ENTRYPOINT"
echo ""

# Check paymaster's ETH balance
echo "💰 Paymaster contract balance:"
BALANCE=$(cast balance "$PAYMASTER" --rpc-url "$RPC_URL")
echo " $BALANCE wei ($(cast --to-unit "$BALANCE" ether) ETH)"
echo ""

# Check deposit in EntryPoint using balanceOf(address)
echo "💳 Deposit in EntryPoint:"
DEPOSIT_RAW=$(cast call "$ENTRYPOINT" "balanceOf(address)(uint256)" "$PAYMASTER" --rpc-url "$RPC_URL")
# Extract just the number without the scientific notation
DEPOSIT=$(echo "$DEPOSIT_RAW" | awk '{print $1}')
DEPOSIT_ETH=$(cast --to-unit "$DEPOSIT" ether 2>/dev/null || echo "0")
echo " $DEPOSIT wei ($DEPOSIT_ETH ETH)"
echo ""

# Convert to decimal for comparison
DEPOSIT_DEC="$DEPOSIT"

if [ "$DEPOSIT_DEC" == "0" ]; then
echo "❌ Paymaster has NO deposit in EntryPoint!"
echo ""
echo "To fix this, run:"
echo " cast send '$PAYMASTER' 'deposit()' --value 10ether --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url '$RPC_URL'"
exit 1
else
echo "✅ Paymaster has deposit in EntryPoint"
fi

echo "✅ Deposit amount: $DEPOSIT_ETH ETH"
echo ""

# Check stake using getDepositInfo(address)
echo "🔒 Checking stake status:"
DEPOSIT_INFO=$(cast call "$ENTRYPOINT" "getDepositInfo(address)" "$PAYMASTER" --rpc-url "$RPC_URL")

# Parse the returned tuple: (deposit, staked, stake, unstakeDelaySec, withdrawTime)
# The output is a hex string with 5 values concatenated
STAKED_HEX=$(echo "$DEPOSIT_INFO" | cut -c67-130) # Second 32 bytes (staked boolean)
STAKE_HEX=$(echo "$DEPOSIT_INFO" | cut -c131-194) # Third 32 bytes (stake amount)

# Convert hex to decimal (remove leading zeros)
STAKED_VALUE="0x$STAKED_HEX"
STAKE_VALUE="0x$STAKE_HEX"

# Check if staked (any non-zero value means true)
if [ "$STAKED_VALUE" != "0x0000000000000000000000000000000000000000000000000000000000000000" ]; then
STAKE_ETH=$(cast --to-unit "$STAKE_VALUE" ether 2>/dev/null || echo "0")
echo " ✅ Paymaster is staked"
echo " ✅ Stake amount: $STAKE_ETH ETH"
else
echo " ❌ Paymaster is NOT staked!"
echo ""
echo " To fix this, run:"
echo " cast send '$PAYMASTER' 'addStake(uint32)' 86400 --value 1ether --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url '$RPC_URL'"
exit 1
fi
Loading
Loading