/** M3a: SERVER signs a real Polymarket CTF Exchange order on behalf of the user's
* delegated wallet (sigType 2: signer=EOA, maker=Safe). Proves we can produce a
* CLOB-shaped signed order without the user. Order struct = client order.ts (V2). */
import fs from 'node:fs';
import { hashTypedData, recoverTypedDataAddress } from 'viem';
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 },
});
// user EOA (delegated embedded wallet)
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;
if (!eoa) { console.log('no embedded wallet'); process.exit(1); }
// NOTE: maker should be the user's Polymarket Safe (sigType 2) in M3c; for the
// signing proof maker=EOA is fine (doesn't affect signature recoverability).
const safe = eoa;
console.log('EOA (signer):', eoa, '| maker(placeholder)=EOA');
const CTF_EXCHANGE = '0xE111180000d2663C0091e4f400237545B87B996B';
const ZERO32 = '0x' + '00'.repeat(32);
const BUILDER = '0x3c829d5150b70f3ba347670d4b1eda96be3c255b3f64895a7eeef5caea7952d5';
const types = { Order: [
{ name: 'salt', type: 'uint256' }, { name: 'maker', type: 'address' }, { name: 'signer', type: 'address' },
{ name: 'tokenId', type: 'uint256' }, { name: 'makerAmount', type: 'uint256' }, { name: 'takerAmount', type: 'uint256' },
{ name: 'side', type: 'uint8' }, { name: 'signatureType', type: 'uint8' }, { name: 'timestamp', type: 'uint256' },
{ name: 'metadata', type: 'bytes32' }, { name: 'builder', type: 'bytes32' },
] };
const domain = { name: 'Polymarket CTF Exchange', version: '2', chainId: 137, verifyingContract: CTF_EXCHANGE };
const message = {
salt: 123456789n, maker: safe, signer: eoa,
tokenId: 88275040060084773376557187972215267513049848642895776801789297917961077894224n,
makerAmount: 1000000n, takerAmount: 10000000n, side: 0, signatureType: 2,
timestamp: 1733600000000n, metadata: ZERO32, builder: BUILDER,
};
const digest = hashTypedData({ domain, types, primaryType: 'Order', message });
console.log('order digest:', digest);
// SERVER signs the EIP-712 typed data on behalf of the EOA (authorization key)
const { signature } = await privy.walletApi.ethereum.signTypedData({
address: eoa, chainType: 'ethereum',
typedData: { domain, types, primaryType: 'Order', message: {
...message, salt: message.salt.toString(), tokenId: message.tokenId.toString(),
makerAmount: message.makerAmount.toString(), takerAmount: message.takerAmount.toString(),
timestamp: message.timestamp.toString(),
} },
});
const recovered = await recoverTypedDataAddress({ domain, types, primaryType: 'Order', message, signature });
const ok = recovered.toLowerCase() === eoa.toLowerCase();
console.log('signature:', signature.slice(0, 26) + '…');
console.log(ok ? '🎯 M3a PASS — server signed a valid Polymarket order, recovers to EOA' : `⚠️ recover mismatch: ${recovered}`);
process.exit(ok ? 0 : 1);