@compass-labs/widgets 0.1.35 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -88,20 +88,49 @@ function createCompassHandler(config) {
88
88
  case "credit/bundle/prepare":
89
89
  return await handleCreditBundlePrepare(client, body);
90
90
  case "credit/bundle/execute":
91
- return await handleCreditExecute(body, config);
91
+ return await handleCreditExecute(client, body, config);
92
92
  case "credit/transfer":
93
93
  return await handleCreditTransfer(client, body);
94
+ case "approval/execute":
95
+ return await handleApprovalExecute(body, config);
94
96
  default:
95
97
  return jsonResponse({ error: `Unknown POST route: ${route}` }, 404);
96
98
  }
97
99
  }
98
100
  return jsonResponse({ error: `Method ${method} not allowed` }, 405);
99
101
  } catch (error) {
100
- const message = error instanceof Error ? error.message : "Internal server error";
101
- return jsonResponse({ error: message }, 500);
102
+ const { message, status } = extractErrorMessage(error);
103
+ return jsonResponse({ error: message }, status);
102
104
  }
103
105
  };
104
106
  }
107
+ function extractErrorMessage(error) {
108
+ if (!(error instanceof Error)) {
109
+ return { message: "Something went wrong. Please try again.", status: 500 };
110
+ }
111
+ const raw = error.message || "";
112
+ const jsonMatch = raw.match(/Body:\s*(\{[\s\S]*\})/);
113
+ if (jsonMatch) {
114
+ try {
115
+ const body = JSON.parse(jsonMatch[1]);
116
+ if (Array.isArray(body.detail)) {
117
+ const msgs = body.detail.map((d) => d.msg || d.message).filter(Boolean);
118
+ if (msgs.length > 0) return { message: msgs.join(". "), status: 422 };
119
+ }
120
+ if (typeof body.detail === "string") return { message: body.detail, status: 422 };
121
+ if (typeof body.error === "string") return { message: body.error, status: 500 };
122
+ if (typeof body.description === "string") return { message: body.description, status: 500 };
123
+ } catch {
124
+ }
125
+ }
126
+ if (error.name === "SDKValidationError" || raw.startsWith("Input validation failed")) {
127
+ return { message: "Invalid request data. Please check your inputs and try again.", status: 400 };
128
+ }
129
+ if (raw.includes("Insufficient") || raw.includes("not deployed") || raw.includes("reverted") || raw.includes("not configured") || raw.includes("Unsupported chain")) {
130
+ return { message: raw, status: 500 };
131
+ }
132
+ return { message: "Something went wrong. Please try again.", status: 500 };
133
+ }
105
134
  function jsonResponse(data, status = 200) {
106
135
  return new Response(JSON.stringify(data), {
107
136
  status,
@@ -342,8 +371,53 @@ async function handleTransferApprove(client, body) {
342
371
  throw error;
343
372
  }
344
373
  }
374
+ async function handleApprovalExecute(body, config) {
375
+ const { owner, chain = "base", transaction } = body;
376
+ const { gasSponsorPrivateKey, rpcUrls } = config;
377
+ if (!owner || !transaction) {
378
+ return jsonResponse({ error: "Missing required parameters (owner, transaction)" }, 400);
379
+ }
380
+ if (!gasSponsorPrivateKey) {
381
+ return jsonResponse(
382
+ { error: "Gas sponsor not configured. Set gasSponsorPrivateKey in handler config." },
383
+ 500
384
+ );
385
+ }
386
+ const viemChain = CHAIN_MAP[chain.toLowerCase()];
387
+ if (!viemChain) {
388
+ return jsonResponse({ error: `Unsupported chain: ${chain}` }, 500);
389
+ }
390
+ const rpcUrl = rpcUrls?.[chain.toLowerCase()];
391
+ if (!rpcUrl) {
392
+ return jsonResponse({ error: `No RPC URL configured for chain: ${chain}` }, 500);
393
+ }
394
+ const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey);
395
+ const walletClient = createWalletClient({
396
+ account: sponsorAccount,
397
+ chain: viemChain,
398
+ transport: http(rpcUrl)
399
+ });
400
+ const publicClient = createPublicClient({
401
+ chain: viemChain,
402
+ transport: http(rpcUrl)
403
+ });
404
+ const txHash = await walletClient.sendTransaction({
405
+ to: transaction.to,
406
+ data: transaction.data,
407
+ value: transaction.value ? BigInt(transaction.value) : 0n,
408
+ gas: transaction.gas ? BigInt(transaction.gas) : void 0
409
+ });
410
+ const receipt = await publicClient.waitForTransactionReceipt({
411
+ hash: txHash,
412
+ timeout: 6e4
413
+ });
414
+ if (receipt.status === "reverted") {
415
+ return jsonResponse({ error: "Approval transaction reverted" }, 500);
416
+ }
417
+ return jsonResponse({ txHash, status: "success" });
418
+ }
345
419
  async function handleTransferPrepare(client, body, config) {
346
- const { owner, chain = "base", token, amount, action } = body;
420
+ const { owner, chain = "base", token, amount, action, product } = body;
347
421
  const { gasSponsorPrivateKey } = config;
348
422
  if (!owner || !token || !amount || !action) {
349
423
  return jsonResponse({ error: "Missing required parameters" }, 400);
@@ -353,15 +427,28 @@ async function handleTransferPrepare(client, body, config) {
353
427
  const sponsorAccount = privateKeyToAccount(gasSponsorPrivateKey);
354
428
  spender = sponsorAccount.address;
355
429
  }
356
- const response = await client.earn.earnTransfer({
357
- owner,
358
- chain,
359
- token,
360
- amount,
361
- action,
362
- gasSponsorship: true,
363
- ...spender && { spender }
364
- });
430
+ let response;
431
+ if (product === "credit") {
432
+ response = await client.credit.creditTransfer({
433
+ owner,
434
+ chain,
435
+ token,
436
+ amount,
437
+ action,
438
+ gasSponsorship: true,
439
+ ...spender && { spender }
440
+ });
441
+ } else {
442
+ response = await client.earn.earnTransfer({
443
+ owner,
444
+ chain,
445
+ token,
446
+ amount,
447
+ action,
448
+ gasSponsorship: true,
449
+ ...spender && { spender }
450
+ });
451
+ }
365
452
  const eip712 = response.eip712;
366
453
  if (!eip712) {
367
454
  return jsonResponse({ error: "No EIP-712 data returned from API" }, 500);
@@ -389,7 +476,7 @@ async function handleTransferPrepare(client, body, config) {
389
476
  });
390
477
  }
391
478
  async function handleTransferExecute(client, body, config) {
392
- const { owner, chain = "base", eip712, signature } = body;
479
+ const { owner, chain = "base", eip712, signature, product } = body;
393
480
  const { gasSponsorPrivateKey, rpcUrls } = config;
394
481
  if (!owner || !eip712 || !signature) {
395
482
  return jsonResponse({ error: "Missing required parameters" }, 400);
@@ -418,18 +505,14 @@ async function handleTransferExecute(client, body, config) {
418
505
  chain: viemChain,
419
506
  transport: http(rpcUrl)
420
507
  });
421
- let response;
422
- try {
423
- response = await client.gasSponsorship.gasSponsorshipPrepare({
424
- chain,
425
- owner,
426
- sender: sponsorAccount.address,
427
- eip712,
428
- signature
429
- });
430
- } catch (prepareError) {
431
- throw prepareError;
432
- }
508
+ const response = await client.gasSponsorship.gasSponsorshipPrepare({
509
+ chain,
510
+ owner,
511
+ sender: sponsorAccount.address,
512
+ eip712,
513
+ signature,
514
+ ...product === "credit" && { product: "credit" }
515
+ });
433
516
  const transaction = response.transaction;
434
517
  if (!transaction) {
435
518
  return jsonResponse(
@@ -1360,6 +1443,7 @@ async function handleCreditTransfer(client, body) {
1360
1443
  chain,
1361
1444
  token,
1362
1445
  amount,
1446
+ action: "DEPOSIT",
1363
1447
  gasSponsorship: true
1364
1448
  });
1365
1449
  const eip712 = response.eip712;
@@ -1387,31 +1471,11 @@ async function handleCreditTransfer(client, body) {
1387
1471
  primaryType: eip712.primaryType
1388
1472
  });
1389
1473
  }
1390
- var SAFE_EXEC_ABI = [
1391
- {
1392
- name: "execTransaction",
1393
- type: "function",
1394
- inputs: [
1395
- { name: "to", type: "address" },
1396
- { name: "value", type: "uint256" },
1397
- { name: "data", type: "bytes" },
1398
- { name: "operation", type: "uint8" },
1399
- { name: "safeTxGas", type: "uint256" },
1400
- { name: "baseGas", type: "uint256" },
1401
- { name: "gasPrice", type: "uint256" },
1402
- { name: "gasToken", type: "address" },
1403
- { name: "refundReceiver", type: "address" },
1404
- { name: "signatures", type: "bytes" }
1405
- ],
1406
- outputs: [{ name: "success", type: "bool" }],
1407
- stateMutability: "payable"
1408
- }
1409
- ];
1410
- async function handleCreditExecute(body, config) {
1411
- const { eip712, signature, chain = "base", creditAccountAddress } = body;
1474
+ async function handleCreditExecute(client, body, config) {
1475
+ const { owner, eip712, signature, chain = "base" } = body;
1412
1476
  const { gasSponsorPrivateKey, rpcUrls } = config;
1413
- if (!eip712 || !signature || !creditAccountAddress) {
1414
- return jsonResponse({ error: "Missing required parameters (eip712, signature, creditAccountAddress)" }, 400);
1477
+ if (!owner || !eip712 || !signature) {
1478
+ return jsonResponse({ error: "Missing required parameters (owner, eip712, signature)" }, 400);
1415
1479
  }
1416
1480
  if (!gasSponsorPrivateKey) {
1417
1481
  return jsonResponse(
@@ -1437,41 +1501,33 @@ async function handleCreditExecute(body, config) {
1437
1501
  chain: viemChain,
1438
1502
  transport: http(rpcUrl)
1439
1503
  });
1440
- const msg = eip712.message;
1441
- const sigBytes = signature.startsWith("0x") ? signature : `0x${signature}`;
1442
- const execArgs = [
1443
- msg.to,
1444
- BigInt(msg.value),
1445
- msg.data,
1446
- Number(msg.operation),
1447
- BigInt(msg.safeTxGas),
1448
- BigInt(msg.baseGas),
1449
- BigInt(msg.gasPrice),
1450
- msg.gasToken,
1451
- msg.refundReceiver,
1452
- sigBytes
1453
- ];
1454
- await publicClient.simulateContract({
1455
- address: creditAccountAddress,
1456
- abi: SAFE_EXEC_ABI,
1457
- functionName: "execTransaction",
1458
- args: execArgs,
1459
- account: sponsorAccount
1504
+ const response = await client.gasSponsorship.gasSponsorshipPrepare({
1505
+ chain,
1506
+ owner,
1507
+ sender: sponsorAccount.address,
1508
+ eip712,
1509
+ signature,
1510
+ product: "credit"
1460
1511
  });
1461
- const gasEstimate = await publicClient.estimateContractGas({
1462
- address: creditAccountAddress,
1463
- abi: SAFE_EXEC_ABI,
1464
- functionName: "execTransaction",
1465
- args: execArgs,
1466
- account: sponsorAccount
1512
+ const transaction = response.transaction;
1513
+ if (!transaction) {
1514
+ return jsonResponse(
1515
+ { error: "No transaction returned from gas sponsorship prepare" },
1516
+ 500
1517
+ );
1518
+ }
1519
+ const txHash = await walletClient.sendTransaction({
1520
+ to: transaction.to,
1521
+ data: transaction.data,
1522
+ value: transaction.value ? BigInt(transaction.value) : 0n,
1523
+ gas: transaction.gas ? BigInt(transaction.gas) : void 0
1467
1524
  });
1468
- const txHash = await walletClient.writeContract({
1469
- address: creditAccountAddress,
1470
- abi: SAFE_EXEC_ABI,
1471
- functionName: "execTransaction",
1472
- args: execArgs,
1473
- gas: gasEstimate * 130n / 100n
1525
+ const receipt = await publicClient.waitForTransactionReceipt({
1526
+ hash: txHash
1474
1527
  });
1528
+ if (receipt.status === "reverted") {
1529
+ return jsonResponse({ error: "Transaction reverted" }, 500);
1530
+ }
1475
1531
  return jsonResponse({ txHash, success: true });
1476
1532
  }
1477
1533
  async function handleTxReceipt(params, config) {