/** M3c-auth: SERVER authenticates the user to Polymarket CLOB — signs ClobAuth
* (EIP-712) on behalf of the user's EOA via server-auth, then creates an API key
* through the screener's proxied endpoint. Proves the server can act as the user
* on CLOB without them present. No funds needed. */
import fs from 'node:fs';
import { PrivyClient } from '@privy-io/server-auth';
const env = {};
for (const l of fs.readFileSync(new URL('../.env.privy', import.meta.url), 'utf8').split('\n')) {
const t = l.trim(); if (!t || t.startsWith('#')) continue;
const i = t.indexOf('='); if (i > 0) env[t.slice(0, i).trim()] = t.slice(i + 1).trim();
}
const privy = new PrivyClient(env.PRIVY_APP_ID, env.PRIVY_APP_SECRET, {
walletApi: { authorizationPrivateKey: env.PRIVY_AUTHORIZATION_KEY },
});
const SCREENER = 'http://localhost:3240';
// user EOA
const users = await privy.getUsers();
let eoa = null;
for (const u of users) for (const a of u.linkedAccounts || [])
if (a.type === 'wallet' && a.walletClientType === 'privy') eoa = a.address;
console.log('user EOA:', eoa);
// ClobAuth EIP-712 (domain ClobAuthDomain v1 chainId 137)
const timestamp = Math.floor(Date.now() / 1000).toString();
const typedData = {
domain: { name: 'ClobAuthDomain', version: '1', chainId: 137 },
types: { ClobAuth: [
{ name: 'address', type: 'address' }, { name: 'timestamp', type: 'string' },
{ name: 'nonce', type: 'uint256' }, { name: 'message', type: 'string' },
] },
primaryType: 'ClobAuth',
message: { address: eoa, timestamp, nonce: '0', message: 'This message attests that I control the given wallet' },
};
const { signature } = await privy.walletApi.ethereum.signTypedData({ address: eoa, chainType: 'ethereum', typedData });
console.log('server signed ClobAuth:', signature.slice(0, 22) + '…');
const post = async (path, body) => {
const r = await fetch(SCREENER + path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
return { status: r.status, body: await r.json().catch(() => ({})) };
};
// create API key for the EOA (fresh wallet → create, not derive)
let r = await post('/api/auth/create-key', { address: eoa, signature, timestamp, nonce: 0 });
console.log('[create-key]', r.status, JSON.stringify(r.body).slice(0, 160));
if (!r.body?.success) {
r = await post('/api/auth/derive-key', { address: eoa, signature, timestamp, nonce: 0 });
console.log('[derive-key fallback]', r.status, JSON.stringify(r.body).slice(0, 160));
}
console.log(r.body?.success ? '🎯 M3c-auth PASS — server authenticated user to CLOB (got API creds)' : '⚠️ auth failed (verdict above)');
process.exit(r.body?.success ? 0 : 1);