agent402-tollbooth 0.10.1 → 0.10.2

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/tempo.js +18 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent402-tollbooth",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Agent402 tollbooth: open-source, self-hostable pay-per-crawl for x402 AND MPP (the sell side of Agentic Finance): charge AI bots per request (USDC via x402, or MPP - the Machine Payments Protocol - through the same settlement, or USDG on Robinhood Chain, or free proof-of-work) while humans browse free. Drop-in Express middleware or reverse proxy that settles both wires from env alone - no Cloudflare, no Stripe, no signup.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -44,7 +44,7 @@
44
44
  "node": ">=20"
45
45
  },
46
46
  "scripts": {
47
- "test": "node test.js && node edge.test.js && node features.test.js",
47
+ "test": "node test.js && node edge.test.js && node features.test.js && node tempo.test.js",
48
48
  "demo": "node demo.js",
49
49
  "example": "node example.js"
50
50
  },
package/tempo.js CHANGED
@@ -323,12 +323,25 @@ export function candidateTxIds(signedTx) {
323
323
  return out;
324
324
  }
325
325
 
326
- const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
326
+ // TIP-20 TransferWithMemo(address indexed from, address indexed to, uint256 amount, bytes32 indexed memo)
327
+ const TRANSFER_WITH_MEMO_TOPIC = keccak256Hex(Buffer.from("TransferWithMemo(address,address,uint256,bytes32)"));
328
+ const MPP_TAG = keccak256Hex(Buffer.from("mpp")).slice(2, 10);
329
+
330
+ /** Is this bytes32 memo an MPP attribution memo bound to `challengeId`?
331
+ * Mirrors mppx tempo/Attribution.js verifyChallengeBinding: TAG, version
332
+ * 0x01, and bytes 25..31 = keccak256(challengeId)[0..6]. */
333
+ export function memoBoundToChallenge(memo, challengeId) {
334
+ const hex = String(memo || "").toLowerCase().replace(/^0x/, "");
335
+ if (hex.length !== 64 || typeof challengeId !== "string" || !challengeId) return false;
336
+ if (hex.slice(0, 8) !== MPP_TAG || hex.slice(8, 10) !== "01") return false;
337
+ return hex.slice(50, 64) === keccak256Hex(Buffer.from(challengeId)).slice(2, 16);
338
+ }
327
339
 
328
340
  /** Did this credential's transaction settle despite the relay's verdict?
329
341
  * Returns { txId } when a candidate receipt exists, succeeded (status 0x1),
330
- * and carries the challenge's transfer (currency + recipient + >= amount) —
331
- * else null. Polls briefly (a just-mined tx may not be indexed when the
342
+ * and carries the challenge's transfer (currency + recipient + >= amount) as
343
+ * a TransferWithMemo whose MPP memo is bound to THIS challenge's id - else
344
+ * null. Polls briefly (a just-mined tx may not be indexed when the
332
345
  * relay answers). Never throws. */
333
346
  export async function confirmTempoSettlement(credential, { rpcUrl, fetchImpl = globalThis.fetch, attempts = 4, delayMs = 2000 } = {}) {
334
347
  try {
@@ -357,8 +370,9 @@ export async function confirmTempoSettlement(credential, { rpcUrl, fetchImpl = g
357
370
  if (!receipt || receipt.status !== "0x1") continue;
358
371
  for (const log of receipt.logs || []) {
359
372
  if (String(log.address || "").toLowerCase() !== currency) continue;
360
- if ((log.topics || [])[0] !== TRANSFER_TOPIC) continue;
373
+ if ((log.topics || [])[0] !== TRANSFER_WITH_MEMO_TOPIC) continue;
361
374
  if (`0x${String(log.topics[2] || "").slice(-40)}`.toLowerCase() !== recipient) continue;
375
+ if (!memoBoundToChallenge(log.topics[3], credential?.challenge?.id)) continue;
362
376
  let value;
363
377
  try { value = BigInt(log.data); } catch { continue; }
364
378
  if (value >= minAmount) return { txId };