← Back
/** 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);