Skip to content
Merged
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
20 changes: 10 additions & 10 deletions .github/workflows/deploy-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@ jobs:
- name: Build the package
run: pnpm nx build sdk

# - name: Build the connector-export package
# run: pnpm nx build connector-export
- name: Build the wagmi-connector package
run: pnpm nx build connector-export

- name: Prepare package.json
working-directory: packages/sdk
run: node prepare-package.mjs
env:
INPUT_VERSION: ${{ github.event.inputs.version }}

# - name: Prepare connector-export package.json
# working-directory: packages/connector-export
# run: node prepare-package.mjs
# env:
# INPUT_VERSION: ${{ github.event.inputs.version }}
- name: Prepare wagmi connector package.json
working-directory: packages/connector-export
run: node prepare-package.mjs
env:
INPUT_VERSION: ${{ github.event.inputs.version }}

- name: Create .npmrc for NPM
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPMJS_NPM_MATTERLABS_AUTOMATION_TOKEN }}" > ~/.npmrc
Expand All @@ -84,7 +84,7 @@ jobs:
working-directory: packages/sdk
run: npm publish --access public --tag ${{ steps.npm_tag.outputs.tag }}

- name: Publish connector-export to NPM
- name: Publish wagmi connector to NPM
working-directory: packages/connector-export
run: npm publish --access public --tag ${{ steps.npm_tag.outputs.tag }}

Expand All @@ -101,9 +101,9 @@ jobs:
npm pkg set name="@matter-labs/zksync-sso"
npm publish --registry=https://npm.pkg.github.com --tag ${{ steps.npm_tag.outputs.tag }}

- name: Publish connector-export to GitHub Packages
- name: Publish wagmi connector to GitHub Packages
working-directory: packages/connector-export
run: |
# Update package name for GitHub Packages to be scoped to the organization
npm pkg set name="@matter-labs/zksync-sso-connector-export"
npm pkg set name="@matter-labs/zksync-sso-wagmi-connector"
npm publish --registry=https://npm.pkg.github.com --tag ${{ steps.npm_tag.outputs.tag }}
7 changes: 1 addition & 6 deletions .vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
{
"servers": {
"nx-mcp": {
"type": "sse",
"url": "http://localhost:9100/sse"
}
}
"servers": {}
}
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ COPY --from=build /prod/oidc-server /prod/oidc-server
WORKDIR /prod/oidc-server
COPY --from=build /usr/src/app/packages/oidc-server/dist ./dist
ENV SALT_SERVICE_PORT=3003
EXPOSE 3003
# Expose main service port and Prometheus metrics port
EXPOSE 3003 9090
CMD [ "node", "dist/salt-service.js" ]

FROM oidc-server AS key-registry
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"packageManager": "pnpm@9.11.0",
"dependencies": {
"prettier-plugin-solidity": "^1.4.1"
"prettier-plugin-solidity": "^1.4.1",
"snarkjs": "^0.7.5"
}
}
6 changes: 3 additions & 3 deletions packages/auth-server/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ export default defineNuxtConfig({
appKitProjectId: process.env.NUXT_PUBLIC_APPKIT_PROJECT_ID || "9bc5059f6eed355858cc56a3388e9b50",
oidc: {
googlePublicClient: "866068535821-e9em0h73pee93q4evoajtnnkldsjhqdk.apps.googleusercontent.com",
saltServiceUrl: process.env.NUXT_PUBLIC_SALT_SERVICE_URL,
zkeyUrl: process.env.NUXT_PUBLIC_ZKEY_URL,
witnessUrl: process.env.NUXT_PUBLIC_WITNESS_WASM_URL,
saltServiceUrl: process.env.NUXT_PUBLIC_SALT_SERVICE_URL || "https://salt.zksync-sso.com",
zkeyUrl: process.env.NUXT_PUBLIC_ZKEY_URL || "https://zkey.zksync-sso.com",
witnessUrl: process.env.NUXT_PUBLIC_WITNESS_WASM_URL || "https://witness.zksync-sso.com",
},
},
},
Expand Down
4 changes: 3 additions & 1 deletion packages/auth-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"private": true,
"type": "module",
"scripts": {
"postinstall": "nuxt prepare"
"postinstall": "nuxt prepare && pnpm run copy:snarkjs",
"copy:snarkjs": "node ./scripts/copy-snarkjs.mjs"
},
"dependencies": {
"@heroicons/vue": "^2.1.5",
Expand Down Expand Up @@ -36,6 +37,7 @@
"vue": "latest",
"wagmi": "^2.12.17",
"web3-avatar-vue": "^1.0.4",
"snarkjs": "^0.7.5",
"zksync-sso": "workspace:*",
"zksync-sso-circuits": "workspace:*",
"zod": "^3.24.1"
Expand Down
4 changes: 2 additions & 2 deletions packages/auth-server/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"executor": "nx:run-commands",
"options": {
"cwd": "packages/auth-server",
"command": "PORT=3002 nuxt dev"
"command": "pnpm run copy:snarkjs && PORT=3002 nuxt dev"
},
"dependsOn": ["^build", "zksync-sso-contracts:deploy", "nft-quest-contracts:deploy:local"]
},
Expand All @@ -24,7 +24,7 @@
"cwd": "packages/auth-server",
"commands": [
{
"command": "pnpm nuxt generate"
"command": "pnpm run copy:snarkjs && pnpm nuxt generate"
}
]
}
Expand Down
84 changes: 84 additions & 0 deletions packages/auth-server/scripts/copy-snarkjs.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env node
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

function main() {
// Resolve the path to snarkjs browser bundle
// Resolve snarkjs package base
let baseDir;
try {
// Prefer import.meta.resolve for pure ESM; fallback to createRequire for older Node versions
let pkgJsonPath;
if (typeof import.meta.resolve === "function") {
const resolvedUrl = import.meta.resolve("snarkjs/package.json");
pkgJsonPath = fileURLToPath(resolvedUrl);
} else {
const require = createRequire(import.meta.url);
pkgJsonPath = require.resolve("snarkjs/package.json");
}
baseDir = dirname(pkgJsonPath);
} catch (e) {
console.warn("[copy-snarkjs] snarkjs not installed yet; skipping copy", e);
return; // don't fail install, maybe another step will install it
}
const candidateRelPaths = ["dist/web/snarkjs.min.js", "dist/snarkjs.min.js", "build/snarkjs.min.js"];
let src;
for (const rel of candidateRelPaths) {
const cand = join(baseDir, rel);
if (existsSync(cand)) {
src = cand;
break;
}
}
if (!src) {
// Last resort: scan first level dirs for snarkjs.min.js
try {
const entries = readdirSync(baseDir, { withFileTypes: true });
for (const ent of entries) {
if (ent.isDirectory()) {
const cand = join(baseDir, ent.name, "snarkjs.min.js");
if (existsSync(cand)) {
src = cand;
break;
}
}
}
} catch (e) {
console.warn("[copy-snarkjs] Error scanning for snarkjs.min.js", e);
}
}
if (!src) {
console.warn(
"[copy-snarkjs] Could not locate snarkjs.min.js inside package; looked in: " + candidateRelPaths.join(", "),
);
return;
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const destDir = resolve(__dirname, "../public");
const dest = resolve(destDir, "snarkjs.min.js");

try {
statSync(destDir);
} catch {
mkdirSync(destDir, { recursive: true });
}
if (existsSync(dest)) {
try {
const srcStat = statSync(src);
const destStat = statSync(dest);
if (destStat.mtimeMs >= srcStat.mtimeMs) {
console.log("[copy-snarkjs] Existing snarkjs.min.js is up to date");
return;
}
} catch (e) {
console.warn("[copy-snarkjs] Error checking existing snarkjs.min.js", e);
}
}
copyFileSync(src, dest);
console.log(`[copy-snarkjs] Copied ${src} -> ${dest}`);
}

main();
16 changes: 8 additions & 8 deletions packages/auth-server/stores/local-node.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"beacon": "0xDfD52B58c321Ba2fa0C4D0f281B884Fcba56c803",
"session": "0x1660a46e1405d53A0abc98C330666c1455815418",
"passkey": "0xDdB1e5ECd29aAC588E0fb0a7eAB1b589fE7D7dcD",
"accountFactory": "0x816982130eDE57B11D3B60984a8874B0d22CC5F5",
"accountPaymaster": "0x4842668C02757e4Eac60dAceB6374CE7F752e3c8",
"recovery": "0xFB55FFb34f002e393232dC28EC27E3F8e8a423d1",
"recoveryOidc": "0x2f99523C6165b92b790A4379dC4e49295b51f57A",
"oidcKeyRegistry": "0x16A346FD3B6cEBF0C1DBf2E10cB66aCD95921115"
"beacon": "0x40f5C447f510d834e2b1fF3f54830CE0B4a157da",
"session": "0x031f5d8f86afE1d6011Dc3F3E8d27E274fbCEB13",
"passkey": "0x0A39934c490546FF547A102f378876e625C9610f",
"accountFactory": "0x799B1252B0a05e17daDb23E19553611997e1aAFE",
"accountPaymaster": "0xebC6385457c82f764b18d3e36a5EA18617340aC4",
"recovery": "0x6EdDCe98684947C563408198144E3C05823910Eb",
"recoveryOidc": "0x25441b19ba5A049684Ecc8d40B72400E7B80DA7C",
"oidcKeyRegistry": "0xDcF51c2486282F04447d8aaeCA0F23d36BFe9833"
}
1 change: 1 addition & 0 deletions packages/oidc-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"express-rate-limit": "^7.5.0",
"install": "^0.13.0",
"jose": "^5.9.6",
"prom-client": "^15.1.0",
"viem": "^2.22.19",
"zksync-sso": "workspace:*",
"zksync-sso-circuits": "workspace:*",
Expand Down
78 changes: 76 additions & 2 deletions packages/oidc-server/src/salt-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { config } from "dotenv";
import express from "express";
import { rateLimit } from "express-rate-limit";
import * as jose from "jose";
import client from "prom-client";
import { bytesToHex } from "viem";
import { z } from "zod";

Expand Down Expand Up @@ -40,6 +41,60 @@ const limiter = rateLimit({

const app = express();

// ----- Metrics setup -----
// Collect default metrics (Node.js process metrics, etc.)
client.collectDefaultMetrics();

// Normalize a path to reduce metrics label cardinality. Any dynamic-looking
// segment (numbers, long hex/base64-ish tokens, UUIDs) is replaced with a
// placeholder. If Express matched a route we prefer route.path which is
// already a pattern (e.g. "/user/:id").
function normalizePath(req: express.Request): string {
// If Express has a route pattern, use it (lowest cardinality already)
const routePath = (req as any).route?.path; // eslint-disable-line @typescript-eslint/no-explicit-any
if (routePath) return routePath;

const raw = req.path || "/";
const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const hexRe = /^[0-9a-fA-F]{8,}$/; // long-ish hex
const base64ishRe = /^[0-9a-zA-Z_-]{10,}$/; // tokens
return raw
.split("/")
.map((seg) => {
if (!seg) return seg;
if (/^[0-9]+$/.test(seg)) return ":int";
if (uuidRe.test(seg)) return ":uuid";
if (hexRe.test(seg)) return ":hex";
if (base64ishRe.test(seg) && seg.length > 16) return ":tok";
return seg;
})
.join("/") || "/";
}

// Custom metrics
const requestCounter = new client.Counter({
name: "salt_service_requests_total",
help: "Total number of requests received by path and method",
labelNames: ["method", "path", "status"],
});
const requestDuration = new client.Histogram({
name: "salt_service_request_duration_seconds",
help: "Histogram of request durations in seconds",
labelNames: ["method", "path", "status"],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5],
});

// Middleware to record metrics
app.use((req, res, next) => {
const end = requestDuration.startTimer();
res.on("finish", () => {
const labels = { method: req.method, path: normalizePath(req), status: res.statusCode.toString() };
requestCounter.inc(labels);
end(labels);
});
next();
});

app.use(cors({ origin: env.AUTH_SERVER_URL }));
app.use(limiter);

Expand Down Expand Up @@ -86,6 +141,25 @@ app.get("/salt", async (req, res): Promise<void> => {
res.json({ salt: bytesToHex(hash) });
});

app.listen(env.SALT_SERVICE_PORT, () => {
console.log(`Server listening on port ${env.SALT_SERVICE_PORT}`);
const mainPort = env.SALT_SERVICE_PORT || "3003";
app.listen(mainPort, () => {
console.log(`Server listening on port ${mainPort}`);
});

// Separate metrics server on port 9090
const METRICS_PORT = process.env.METRICS_PORT || 9090;
const metricsApp = express();
metricsApp.get("/metrics", async (_req, res) => {
try {
res.set("Content-Type", client.register.contentType);
res.end(await client.register.metrics());
} catch (err) {
res.status(500).end((err as Error).message);
}
});
metricsApp.get("/health", async (_req, res) => {
res.json({ status: "ok" });
});
metricsApp.listen(METRICS_PORT, () => {
console.log(`Metrics server listening on port ${METRICS_PORT}`);
});
Loading
Loading