Skip to content

Commit 51a0924

Browse files
committed
feat: support of latest prividium version
1 parent 8349b92 commit 51a0924

File tree

27 files changed

+1484
-701
lines changed

27 files changed

+1484
-701
lines changed

examples/demo-app/pages/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ const sessionConfig = {
160160
161161
const buildConnector = (mode: "regular" | "session" | "paymaster" | "session-paymaster") => {
162162
const baseConfig: Parameters<typeof zksyncSsoConnector>[0] = {
163-
authServerUrl: "http://localhost:3002/confirm",
163+
authServerUrl: "http://localhost:3003/confirm",
164164
};
165165
166166
if (mode === "session" || mode === "session-paymaster") {

examples/demo-app/scripts/deploy-msa-anvil.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
88

99
# Configuration
1010
DEPLOYER_ADDRESS="0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" # Anvil account #9
11-
RPC_URL="http://localhost:8545"
12-
CHAIN_ID=1337
11+
RPC_URL="http://localhost:5050"
12+
CHAIN_ID=6565
1313
CONTRACTS_DIR="$WORKSPACE_ROOT/packages/erc4337-contracts"
1414

1515
echo "🚀 Deploying MSA Factory and modules to Anvil (standard EVM)..."
@@ -44,7 +44,7 @@ FACTORY=$(echo "$DEPLOY_OUTPUT" | grep "MSAFactory:" | awk '{print $2}')
4444
echo ""
4545
echo "📦 Deploying MockPaymaster..."
4646
cd "$CONTRACTS_DIR"
47-
PAYMASTER_OUTPUT=$(forge create test/mocks/MockPaymaster.sol:MockPaymaster --rpc-url "$RPC_URL" --private-key 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 --broadcast 2>&1)
47+
PAYMASTER_OUTPUT=$(forge create test/mocks/MockPaymaster.sol:MockPaymaster --rpc-url "$RPC_URL" --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast 2>&1)
4848
echo "$PAYMASTER_OUTPUT"
4949
PAYMASTER=$(echo "$PAYMASTER_OUTPUT" | grep "Deployed to:" | awk '{print $3}')
5050

@@ -70,14 +70,14 @@ fi
7070

7171
echo ""
7272
echo "✅ Deployment complete!"
73+
echo " MockPaymaster: $PAYMASTER"
7374
echo " EOAKeyValidator: $EOA_VALIDATOR"
7475
echo " SessionKeyValidator: $SESSION_VALIDATOR"
7576
echo " WebAuthnValidator: $WEBAUTHN_VALIDATOR"
7677
echo " GuardianExecutor: $GUARDIAN_EXECUTOR"
7778
echo " ModularSmartAccount impl: $ACCOUNT_IMPL"
7879
echo " UpgradeableBeacon: $BEACON"
7980
echo " MSAFactory: $FACTORY"
80-
echo " MockPaymaster: $PAYMASTER"
8181

8282
# Create contracts-anvil.json
8383
echo ""

packages/auth-server-api/.env.example

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf
1010

1111
# Blockchain Configuration
1212
RPC_URL=http://127.0.0.1:8545
13-
BUNDLER_URL=http://127.0.0.1:4337
1413

1514
# Contract Addresses (OPTIONAL - will fall back to src/contracts.json if not provided)
1615
# You can provide these via environment variables OR update src/contracts.json
@@ -19,3 +18,12 @@ BUNDLER_URL=http://127.0.0.1:4337
1918
# EOA_VALIDATOR_ADDRESS=0x...
2019
# WEBAUTHN_VALIDATOR_ADDRESS=0x...
2120
# SESSION_VALIDATOR_ADDRESS=0x...
21+
22+
# Prividium Mode Configuration (OPTIONAL)
23+
# When enabled, requires user authentication via Prividium and routes deployments through Prividium RPC proxy
24+
# PRIVIDIUM_MODE=true
25+
# PRIVIDIUM_RPC_PROXY_BASE_URL=https://rpc.prividium.io
26+
# PRIVIDIUM_PERMISSIONS_BASE_URL=https://permissions.prividium.io
27+
# PRIVIDIUM_ADMIN_PRIVATE_KEY=0x... # Private key of a user with 'admin' role in Prividium
28+
# PRIVIDIUM_TEMPLATE_KEY=sso-smart-account # Template key for whitelisting deployed contracts
29+
# SSO_AUTH_SERVER_BASE_URL=https://sso.example.com # Base URL of the SSO auth server frontend (used as SIWE domain for admin authorization)

packages/auth-server-api/src/app.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import cors from "cors";
22
import express, { type NextFunction, type Request, type Response } from "express";
33

4-
import { env } from "./config.js";
4+
import { env, prividiumConfig } from "./config.js";
55
import { deployAccountHandler } from "./handlers/deploy-account.js";
6+
import { prividiumAuthMiddleware } from "./middleware/prividium-auth.js";
67

78
// Initialize Express app
89
const app = express();
@@ -33,8 +34,8 @@ app.get("/api/health", (_req: Request, res: Response) => {
3334
res.json({ status: "ok", timestamp: new Date().toISOString() });
3435
});
3536

36-
// Deploy account endpoint
37-
app.post("/api/deploy-account", deployAccountHandler);
37+
// Deploy account endpoint (with Prividium auth middleware when enabled)
38+
app.post("/api/deploy-account", prividiumAuthMiddleware(prividiumConfig), deployAccountHandler);
3839

3940
// Global error handler
4041
// eslint-disable-next-line @typescript-eslint/no-unused-vars

packages/auth-server-api/src/config.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,17 @@ const envSchema = z.object({
3434
CORS_ORIGINS: z.string().default("http://localhost:3002,http://localhost:3003,http://localhost:3004,http://localhost:3005,http://localhost:3000"),
3535
DEPLOYER_PRIVATE_KEY: z.string().default("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"),
3636
RPC_URL: z.string().default("http://127.0.0.1:8545"),
37-
BUNDLER_URL: z.string().default("http://127.0.0.1:4337"),
3837
FACTORY_ADDRESS: z.string().optional(),
3938
EOA_VALIDATOR_ADDRESS: z.string().optional(),
4039
WEBAUTHN_VALIDATOR_ADDRESS: z.string().optional(),
4140
SESSION_VALIDATOR_ADDRESS: z.string().optional(),
41+
// Prividium Mode Configuration
42+
PRIVIDIUM_MODE: z.string().transform((v) => v === "true").default("false"),
43+
PRIVIDIUM_PERMISSIONS_BASE_URL: z.string().optional(),
44+
PRIVIDIUM_RPC_PROXY_BASE_URL: z.string().optional(),
45+
PRIVIDIUM_ADMIN_PRIVATE_KEY: z.string().optional(),
46+
PRIVIDIUM_TEMPLATE_KEY: z.string().optional(),
47+
SSO_AUTH_SERVER_BASE_URL: z.string().optional(),
4248
});
4349

4450
// Parse and validate environment variables
@@ -50,6 +56,21 @@ try {
5056
process.exit(1);
5157
}
5258

59+
// Validate Prividium configuration when enabled
60+
if (env.PRIVIDIUM_MODE) {
61+
const missingPrividiumVars: string[] = [];
62+
if (!env.PRIVIDIUM_PERMISSIONS_BASE_URL) missingPrividiumVars.push("PRIVIDIUM_PERMISSIONS_BASE_URL");
63+
if (!env.PRIVIDIUM_RPC_PROXY_BASE_URL) missingPrividiumVars.push("PRIVIDIUM_RPC_PROXY_BASE_URL");
64+
if (!env.PRIVIDIUM_ADMIN_PRIVATE_KEY) missingPrividiumVars.push("PRIVIDIUM_ADMIN_PRIVATE_KEY");
65+
if (!env.PRIVIDIUM_TEMPLATE_KEY) missingPrividiumVars.push("PRIVIDIUM_TEMPLATE_KEY");
66+
if (!env.SSO_AUTH_SERVER_BASE_URL) missingPrividiumVars.push("SSO_AUTH_SERVER_BASE_URL");
67+
68+
if (missingPrividiumVars.length > 0) {
69+
console.error("PRIVIDIUM_MODE is enabled but missing required configuration:", missingPrividiumVars.join(", "));
70+
process.exit(1);
71+
}
72+
}
73+
5374
// Use env vars if provided, otherwise fall back to contracts.json
5475
const FACTORY_ADDRESS = env.FACTORY_ADDRESS || contractsFromFile.factory;
5576
const EOA_VALIDATOR_ADDRESS = env.EOA_VALIDATOR_ADDRESS || contractsFromFile.eoaValidator;
@@ -89,7 +110,21 @@ const zksyncOsTestnet = defineChain({
89110
},
90111
},
91112
});
92-
const SUPPORTED_CHAINS: Chain[] = [localhost, zksyncOsTestnet];
113+
const zksyncOsLocal = defineChain({
114+
id: 6565,
115+
name: "ZKsyncOS Local",
116+
nativeCurrency: {
117+
name: "Ether",
118+
symbol: "ETH",
119+
decimals: 18,
120+
},
121+
rpcUrls: {
122+
default: {
123+
http: ["http://localhost:5050"],
124+
},
125+
},
126+
});
127+
const SUPPORTED_CHAINS: Chain[] = [localhost, zksyncOsTestnet, zksyncOsLocal];
93128

94129
function getChain(chainId: number): Chain {
95130
const chain = SUPPORTED_CHAINS.find((c) => c.id === chainId);
@@ -99,4 +134,23 @@ function getChain(chainId: number): Chain {
99134
return chain;
100135
}
101136

102-
export { env, EOA_VALIDATOR_ADDRESS, FACTORY_ADDRESS, getChain, SESSION_VALIDATOR_ADDRESS, SUPPORTED_CHAINS, WEBAUTHN_VALIDATOR_ADDRESS };
137+
// Prividium configuration object for services
138+
export interface PrividiumConfig {
139+
enabled: boolean;
140+
permissionsApiUrl: string;
141+
proxyUrl: string;
142+
adminPrivateKey: string;
143+
templateKey: string;
144+
ssoAuthServerBaseUrl: string;
145+
}
146+
147+
const prividiumConfig: PrividiumConfig = {
148+
enabled: env.PRIVIDIUM_MODE,
149+
permissionsApiUrl: env.PRIVIDIUM_PERMISSIONS_BASE_URL || "",
150+
proxyUrl: env.PRIVIDIUM_RPC_PROXY_BASE_URL ? `${env.PRIVIDIUM_RPC_PROXY_BASE_URL}/rpc` : "",
151+
adminPrivateKey: env.PRIVIDIUM_ADMIN_PRIVATE_KEY || "",
152+
templateKey: env.PRIVIDIUM_TEMPLATE_KEY || "",
153+
ssoAuthServerBaseUrl: env.SSO_AUTH_SERVER_BASE_URL || "",
154+
};
155+
156+
export { env, EOA_VALIDATOR_ADDRESS, FACTORY_ADDRESS, getChain, prividiumConfig, SESSION_VALIDATOR_ADDRESS, SUPPORTED_CHAINS, WEBAUTHN_VALIDATOR_ADDRESS };

packages/auth-server-api/src/handlers/deploy-account.ts

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { Request, Response } from "express";
2-
import { type Address, createPublicClient, createWalletClient, type Hex, http } from "viem";
2+
import { type Address, createPublicClient, createWalletClient, type Hex, http, parseEther } from "viem";
33
import { privateKeyToAccount } from "viem/accounts";
44
import { waitForTransactionReceipt } from "viem/actions";
55
import { getAccountAddressFromLogs, prepareDeploySmartAccount } from "zksync-sso-4337/client";
66

7-
import { env, EOA_VALIDATOR_ADDRESS, FACTORY_ADDRESS, getChain, SESSION_VALIDATOR_ADDRESS, WEBAUTHN_VALIDATOR_ADDRESS } from "../config.js";
7+
import { env, EOA_VALIDATOR_ADDRESS, FACTORY_ADDRESS, getChain, prividiumConfig, SESSION_VALIDATOR_ADDRESS, WEBAUTHN_VALIDATOR_ADDRESS } from "../config.js";
88
import { deployAccountSchema } from "../schemas.js";
9+
import { addAddressToUser, createProxyTransport, getAdminAuthService, whitelistContract } from "../services/prividium/index.js";
910

1011
type DeployAccountRequest = {
1112
chainId: number;
@@ -14,7 +15,6 @@ type DeployAccountRequest = {
1415
originDomain: string;
1516
userId?: string;
1617
eoaSigners?: Address[];
17-
paymaster?: Address;
1818
};
1919

2020
// Deploy account endpoint
@@ -34,25 +34,46 @@ export const deployAccountHandler = async (req: Request, res: Response): Promise
3434
// Get chain from request
3535
const chain = getChain(body.chainId);
3636

37+
// Get admin token if in Prividium mode (needed for RPC proxy, whitelisting, and address association)
38+
let adminToken: string | undefined;
39+
if (prividiumConfig.enabled && req.prividiumUser) {
40+
try {
41+
const adminAuth = getAdminAuthService(prividiumConfig);
42+
adminToken = await adminAuth.getValidToken();
43+
} catch (error) {
44+
console.error("Admin authentication failed:", error);
45+
res.status(500).json({
46+
error: "Admin authentication failed",
47+
});
48+
return;
49+
}
50+
}
51+
52+
// Create transport - use Prividium proxy if enabled, otherwise direct RPC
53+
const transport = prividiumConfig.enabled && adminToken
54+
? createProxyTransport(prividiumConfig.proxyUrl, adminToken)
55+
: http(env.RPC_URL);
56+
3757
// Create clients
3858
const publicClient = createPublicClient({
3959
chain,
40-
transport: http(env.RPC_URL),
60+
transport,
4161
});
4262

4363
const deployerAccount = privateKeyToAccount(env.DEPLOYER_PRIVATE_KEY as Hex);
4464
const walletClient = createWalletClient({
4565
account: deployerAccount,
4666
chain,
47-
transport: http(env.RPC_URL),
67+
transport,
4868
});
4969

5070
// Check deployer balance
5171
const balance = await publicClient.getBalance({ address: deployerAccount.address });
52-
const minBalance = BigInt("100000000000000000"); // 0.1 ETH minimum
72+
const minBalance = parseEther("0.01"); // Minimum balance required for deployment
5373
if (balance < minBalance) {
74+
console.error(`Deployer balance too low: ${balance.toString()} < ${minBalance.toString()}`);
5475
res.status(500).json({
55-
error: `Insufficient balance to cover deployment. Current: ${balance.toString()}, Required: ${minBalance.toString()}`,
76+
error: "Deployer doesn't have enough balance to cover deployment",
5677
});
5778
return;
5879
}
@@ -82,17 +103,11 @@ export const deployAccountHandler = async (req: Request, res: Response): Promise
82103
// Send transaction
83104
let txHash: Hex;
84105
try {
85-
const txParams: any = {
106+
const txParams = {
86107
to: transaction.to,
87108
data: transaction.data,
88109
};
89110

90-
// Add paymaster if provided
91-
if (body.paymaster) {
92-
txParams.paymaster = body.paymaster;
93-
txParams.paymasterInput = "0x";
94-
}
95-
96111
txHash = await walletClient.sendTransaction(txParams);
97112
} catch (error) {
98113
console.error("Transaction send failed:", error);
@@ -104,7 +119,7 @@ export const deployAccountHandler = async (req: Request, res: Response): Promise
104119
return;
105120
}
106121
res.status(500).json({
107-
error: `Transaction failed: ${errorMessage}`,
122+
error: "Internal server error",
108123
});
109124
return;
110125
}
@@ -119,17 +134,16 @@ export const deployAccountHandler = async (req: Request, res: Response): Promise
119134
});
120135
} catch (error) {
121136
console.error("Transaction receipt failed:", error);
122-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
123137
res.status(500).json({
124-
error: `Transaction failed to be mined: ${errorMessage}`,
138+
error: "Internal server error",
125139
});
126140
return;
127141
}
128142

129143
// Check if transaction was successful
130144
if (receipt.status === "reverted") {
131145
res.status(500).json({
132-
error: "Deployment reverted: Transaction execution failed",
146+
error: "Deployment reverted",
133147
});
134148
return;
135149
}
@@ -140,22 +154,55 @@ export const deployAccountHandler = async (req: Request, res: Response): Promise
140154
deployedAddress = getAccountAddressFromLogs(receipt.logs);
141155
} catch (error) {
142156
console.error("Failed to extract address from logs:", error);
143-
const errorMessage = error instanceof Error ? error.message : "";
144157
res.status(500).json({
145-
error: `Deployment failed: Could not extract account address from logs. ${errorMessage}`,
158+
error: "Internal server error",
146159
});
147160
return;
148161
}
149162

150163
console.log("Account deployed at:", deployedAddress);
151164

165+
// Prividium post-deployment steps (all blocking)
166+
if (prividiumConfig.enabled && req.prividiumUser && adminToken) {
167+
// Step 1: Whitelist the contract with template (blocking)
168+
try {
169+
await whitelistContract(
170+
deployedAddress,
171+
prividiumConfig.templateKey,
172+
adminToken,
173+
prividiumConfig.permissionsApiUrl,
174+
);
175+
} catch (error) {
176+
console.error("Failed to whitelist contract:", error);
177+
res.status(500).json({
178+
error: "Failed to whitelist contract",
179+
});
180+
return;
181+
}
182+
183+
// Step 2: Associate address with user (blocking)
184+
try {
185+
await addAddressToUser(
186+
req.prividiumUser.userId,
187+
[deployedAddress],
188+
adminToken,
189+
prividiumConfig.permissionsApiUrl,
190+
);
191+
} catch (error) {
192+
console.error("Failed to associate address with user:", error);
193+
res.status(500).json({
194+
error: "Failed to associate address with user",
195+
});
196+
return;
197+
}
198+
}
199+
152200
// Return success response
153201
res.json({ address: deployedAddress });
154202
} catch (error) {
155203
console.error("Deployment error:", error);
156-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
157204
res.status(500).json({
158-
error: `Deployment failed: ${errorMessage}`,
205+
error: "Internal server error",
159206
});
160207
}
161208
};

packages/auth-server-api/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ app.listen(port, () => {
1111
console.log(`CORS origins: ${allowlist.join(", ")}`);
1212
console.log(`Deployer address: ${privateKeyToAccount(env.DEPLOYER_PRIVATE_KEY as Hex).address}`);
1313
console.log(`RPC URL: ${env.RPC_URL}`);
14-
console.log(`Bundler URL: ${env.BUNDLER_URL}`);
1514
});

0 commit comments

Comments
 (0)