← Back
import { encodeFunctionData, erc20Abi, maxUint256, createPublicClient, http } from 'viem';
import { polygon } from 'viem/chains';
import type { DwCall } from './dwApprovals';

// Client-side, USER-PRESENT redeem of resolved positions. Signed by the user's OWN embedded
// wallet (owner) — NOT the server delegate — so the orders-only Privy policy (which restricts
// only the delegate) does not block it. Mirrors node-executor/redeem-dw.mjs.
const CTF = '0x4D97DCd97eC945f40cF65F87097ACe5EA0476045';
const NEG_RISK_ADAPTER = '0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296';
const USDC_E = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174';
const ONRAMP = '0x93070a847efEf7F70739046A929D47a521F5B8ee';   // USDC.e → pUSD onramp
const ZERO32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
const REDEEM_ABI = [{ name: 'redeemPositions', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'collateralToken', type: 'address' }, { name: 'parentCollectionId', type: 'bytes32' }, { name: 'conditionId', type: 'bytes32' }, { name: 'indexSets', type: 'uint256[]' }], outputs: [] }] as const;
const WRAP_ABI = [{ name: 'wrap', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'asset', type: 'address' }, { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }], outputs: [] }] as const;

const pub = createPublicClient({ chain: polygon, transport: http('https://polygon.drpc.org') });

// Build redeemPositions calls for every resolved (redeemable) condition the DW holds.
export async function buildRedeemCalls(dw: string): Promise<DwCall[]> {
  const r = await fetch(`https://data-api.polymarket.com/positions?user=${dw}&sizeThreshold=0&redeemable=true&limit=100`, { signal: AbortSignal.timeout(15000) });
  const positions = await r.json();
  const list: { conditionId?: string; negativeRisk?: boolean }[] = Array.isArray(positions) ? positions : [];
  const seen = new Set<string>();
  const calls: DwCall[] = [];
  for (const p of list) {
    const cid = p.conditionId;
    if (!cid || seen.has(cid)) continue;
    seen.add(cid);
    calls.push({
      target: p.negativeRisk ? NEG_RISK_ADAPTER : CTF,
      value: '0',
      data: encodeFunctionData({ abi: REDEEM_ABI, functionName: 'redeemPositions', args: [USDC_E, ZERO32, cid as `0x${string}`, [1n, 2n]] }),
    });
  }
  return calls;
}

// Build approve+wrap calls to convert any USDC.e (from redeem) → pUSD (re-tradable).
export async function buildWrapCalls(dw: string): Promise<DwCall[]> {
  const usdce = await pub.readContract({ address: USDC_E, abi: erc20Abi, functionName: 'balanceOf', args: [dw as `0x${string}`] });
  if (usdce <= 10000n) return [];   // < $0.01
  const calls: DwCall[] = [];
  const allowance = await pub.readContract({ address: USDC_E, abi: erc20Abi, functionName: 'allowance', args: [dw as `0x${string}`, ONRAMP] });
  if (allowance < usdce) calls.push({ target: USDC_E, value: '0', data: encodeFunctionData({ abi: erc20Abi, functionName: 'approve', args: [ONRAMP, maxUint256] }) });
  calls.push({ target: ONRAMP, value: '0', data: encodeFunctionData({ abi: WRAP_ABI, functionName: 'wrap', args: [USDC_E, dw as `0x${string}`, usdce] }) });
  return calls;
}

📜 Git History

6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...