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
161 changes: 100 additions & 61 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,64 +106,103 @@ jobs:
name: demo-app-4337-playwright-report
path: examples/demo-app/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

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

- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: wasm32-unknown-unknown

- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# Install foundry for Anvil
- name: Install foundry
uses: foundry-rs/foundry-toolchain@v1.5.0

# Start Anvil on port 8545 (chain-id 1337 to match erc4337-contracts)
- 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
- name: Install dependencies
run: pnpm install -r --frozen-lockfile

# Web SDK depends on ERC-4337 contracts for WASM build
- 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 web SDK (includes WASM)
- name: Build web SDK
run: pnpm nx build web-sdk

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

# Deploy ERC-4337 contracts (MSAFactory, validators, etc.) to Anvil
# This also generates contracts-anvil.json with the deployed addresses
- name: Deploy ERC-4337 contracts
run: pnpm nx deploy-msa-factory nft-quest

# Deploy NFT contract to Anvil
- name: Deploy NFT contract
run: pnpm nx deploy:anvil nft-quest-contracts

# Start Alto bundler with CORS proxy for account abstraction
- 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 (eth_chainId)..."
timeout 60 bash -c 'until curl -s -X POST -H "Content-Type: application/json" --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_chainId\",\"params\":[],\"id\":1}" http://localhost:4337 | grep -q "\"result\":"; do sleep 1; done'
echo "Bundler is ready!"
working-directory: packages/erc4337-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
30 changes: 30 additions & 0 deletions examples/nft-quest-contracts/deploy/deploy-anvil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fs from "fs";
import hre from "hardhat";
import path from "path";

async function main() {
// Use standard hardhat ethers to deploy on Anvil (localhost:8545)
const [deployer] = await hre.ethers.getSigners();
console.log("Deploying with:", deployer.address);

// Simple NFT contract deployment using OpenZeppelin ERC721
const baseTokenURI = "https://nft.zksync.dev/nft/metadata.json";
const ZeekFactory = await hre.ethers.getContractFactory("ZeekNFTQuest");
const nft = await ZeekFactory.deploy(baseTokenURI);
await nft.waitForDeployment();
const nftAddress = await nft.getAddress();
console.log("ZeekNFTQuest deployed:", nftAddress);

// For minimal flow we skip paymaster; rich account funds gas directly.

// Write env for front-end consumption (duplicated pattern acceptable)
const envFilePath = path.join(__dirname, "../../nft-quest/.env.local");
const envContent = `NUXT_PUBLIC_CONTRACTS_NFT=${nftAddress}\n`;
fs.writeFileSync(envFilePath, envContent, { encoding: "utf8" });
console.log("Updated .env.local with NFT address");
}

main().catch((err) => {
console.error(err);
process.exit(1);
});
21 changes: 19 additions & 2 deletions examples/nft-quest-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,33 @@ const config: HardhatUserConfig = {
hardhat: {
zksync: true,
},
localhost: {
url: "http://127.0.0.1:8545",
// This network uses a standard local Ethereum node (e.g., Anvil or Hardhat Network) that does not support zkSync-specific features.
// zkSync is disabled here to allow minimal ERC721 deployment and testing without zkSync extensions.
zksync: false,
},
},
zksolc: {
version: "latest",
settings: {
// find all available options in the official documentation
// https://docs.zksync.io/build/tooling/hardhat/hardhat-zksync-solc#configuration
// enable viaIR for complex system contract compilation when needed
optimizer: {
enabled: true,
runs: 200,
},
viaIR: true,
},
},
solidity: {
version: "0.8.17",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
viaIR: true,
},
},
};

Expand Down
8 changes: 8 additions & 0 deletions examples/nft-quest-contracts/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
},
"dependsOn": ["build"]
},
"deploy:anvil": {
"executor": "nx:run-commands",
"options": {
"cwd": "examples/nft-quest-contracts",
"command": "WALLET_PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 hardhat run deploy/deploy-anvil.ts --network localhost"
},
"dependsOn": ["build"]
},
"test": {
"executor": "nx:run-commands",
"options": {
Expand Down
21 changes: 11 additions & 10 deletions examples/nft-quest/composables/useMintNft.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import { waitForTransactionReceipt, writeContract } from "@wagmi/core";
import type { Address } from "viem";
import { getGeneralPaymasterInput } from "viem/zksync";

export const useMintNft = async (_address: MaybeRef<Address>) => {
const address = toRef(_address);

return await useAsyncData("mintZeek", async () => {
const runtimeConfig = useRuntimeConfig();
const { wagmiConfig } = storeToRefs(useConnectorStore());
const clientStore = useClientStore();

const client = clientStore.getClient();

const mintingForAddress = address.value;
const transactionHash = await writeContract(wagmiConfig.value, {

const hash = await client.writeContract({
address: runtimeConfig.public.contracts.nft as Address,
abi: nftAbi,
functionName: "mint",
args: [mintingForAddress],
paymaster: runtimeConfig.public.contracts.paymaster as Address,
paymasterInput: getGeneralPaymasterInput({ innerInput: "0x" }),
});

const transactionReceipt = await waitForTransactionReceipt(wagmiConfig.value, { hash: transactionHash });
if (transactionReceipt.status === "reverted") {
throw new Error("Transaction reverted");
// Wait for transaction receipt
const receipt = await client.waitForTransactionReceipt({ hash });

if (receipt.status !== "success") {
throw new Error("Mint transaction failed");
}

return transactionReceipt;
return receipt;
}, {
server: false,
immediate: false,
Expand Down
7 changes: 7 additions & 0 deletions examples/nft-quest/contracts-anvil.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"eoaValidator": "0xde612c1b1C06982551157FB53E61a123c6045DFC",
"webauthnValidator": "0xE854be130245223cBDD20c85f4F126996669Cf08",
"sessionValidator": "0x41b6892Bf7aFB4feeACDd96AAf34205124380A47",
"factory": "0x099961116Ba4371a2516034233177Ee3eFcdaf39",
"bundlerUrl": "http://localhost:4337"
}
54 changes: 48 additions & 6 deletions examples/nft-quest/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
import { defineNuxtConfig } from "nuxt/config";
import { zksyncInMemoryNode, zksyncSepoliaTestnet } from "viem/chains";
import type { Chain } from "viem/chains";
import { defineChain } from "viem/utils";
import topLevelAwait from "vite-plugin-top-level-await";
import wasm from "vite-plugin-wasm";

// Anvil chain configuration (chain ID 1337 to match erc4337-contracts setup)
const anvilChain: Chain = {
id: 1337,
name: "Anvil",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["http://127.0.0.1:8545"] },
},
};

const zksyncOsTestnet = defineChain({
id: 8022833,
name: "ZKsyncOS Testnet",
nativeCurrency: {
name: "Ether",
symbol: "ETH",
decimals: 18,
},
rpcUrls: {
default: {
http: ["https://zksync-os-testnet-alpha.zksync.dev"],
},
},
blockExplorers: {
default: {
name: "ZKsyncOS Testnet Explorer",
url: "https://zksync-os-testnet-alpha.staging-scan-v2.zksync.dev",
},
},
});

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
Expand All @@ -19,14 +53,16 @@ export default defineNuxtConfig({
$production: {
runtimeConfig: {
public: {
chain: zksyncSepoliaTestnet,
chain: zksyncOsTestnet, // Update to use atlas testnet when deploying
contracts: {
nft: "0x4D533d3B20b50b57268f189F93bFaf8B39c36AB6",
paymaster: "0x60eef092977DF2738480a6986e2aCD10236b1FA7",
webauthnValidator: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
},
bundlerUrl: "http://localhost:4337",
baseUrl: "https://nft.zksync.dev",
authServerUrl: "https://auth-test.zksync.dev/confirm",
explorerUrl: "https://sepolia.explorer.zksync.io",
explorerUrl: "https://zksync-os-testnet-alpha.staging-scan-v2.zksync.dev",
},
},
},
Expand Down Expand Up @@ -57,11 +93,13 @@ export default defineNuxtConfig({
},
runtimeConfig: {
public: {
chain: zksyncInMemoryNode,
chain: anvilChain,
contracts: {
nft: "0xF4E1ee85f0645b5871B03bc40d151C174F0e86f6",
paymaster: "0x25B89fa6e157937f845ec0Fb41733B29bc20A4d3",
nft: process.env.NUXT_PUBLIC_CONTRACTS_NFT || "0x4c07ce6454D5340591f62fD7d3978B6f42Ef953e",
paymaster: process.env.NUXT_PUBLIC_CONTRACTS_PAYMASTER || "",
webauthnValidator: process.env.NUXT_PUBLIC_CONTRACTS_WEBAUTHN_VALIDATOR || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
},
bundlerUrl: process.env.NUXT_PUBLIC_BUNDLER_URL || "http://localhost:4337",
baseUrl: "http://localhost:3006",
authServerUrl: "http://localhost:3002/confirm",
explorerUrl: "http://localhost:3010",
Expand All @@ -77,6 +115,10 @@ export default defineNuxtConfig({
},
},
vite: {
plugins: [
wasm(),
topLevelAwait(),
],
css: {
preprocessorOptions: {
scss: {
Expand Down
Loading
Loading