0xEunum / 0xEunum/DoloX

Potential tx-to-log binding issue: submitted txHash only selects a block for Transfer search

Abierto
#1 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Solidity
Estrellas
0
Forks
0
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

Hi, I noticed a possible transaction-to-log binding issue in the `X-PAYMENT` verification path.

In `keeper/src/core/x402Server.ts`, the payment header is parsed as `txHash::paymentId`:

```ts
94: private async verifyPaymentHeader(paymentHeader: string): Promise {
96: // Format: "txHash::paymentId" - double colon avoids collision with 0x hex
97: const separatorIdx = paymentHeader.indexOf("::");
100: const txHash = paymentHeader.slice(0, separatorIdx);
101: const paymentId = paymentHeader.slice(separatorIdx + 2);
105: // Replay protection
106: if (this.verifiedPayments.has(paymentId)) return false;
```

The code fetches the receipt for the submitted transaction hash and uses its block number:

```ts
108: // Verify on-chain receipt
109: const receipt = await this.client.getTransactionReceipt({
110: hash: txHash as `0x${string}`,
111: });
113: if (!receipt || receipt.status !== "success") return false;
```

But the accepted Transfer log search is over the whole receipt block, filtered by token address and recipient:

```ts
115: // Retry getLogs up to 5x - guards against RPC indexing lag
116: let transferLogs: any[] = [];
117: for (let attempt = 0; attempt < 5; attempt++) {
118: transferLogs = await this.client.getLogs({
119: address: this.usdcAddress,
120: event: USDC_ABI[0],
121: args: { to: this.paymentAddress },
122: fromBlock: receipt.blockNumber,
123: toBlock: receipt.blockNumber,
124: });
```

The payment is accepted if any log in that block has enough value:

```ts
129: const validTransfer = transferLogs.some(
130: (log: any) => BigInt(log.args.value) >= this.pricePerCall,
131: );
133: if (!validTransfer) return false;
135: this.verifiedPayments.add(paymentId);
```

The protected price endpoint then runs behind this middleware:

```ts
67: this.app.post("/v1/price", this.x402.middleware(), async (_, res) => {
69: const signal = await this.oracle.getPriceSignal();
75: res.json({
76: action: signal.action,
77: price: signal.price,
78: twapPrice: signal.twapPrice,
79: confidence: signal.confidence,
```

There is also an adjacent free signal endpoint:

```ts
109: this.app.get("/v1/signal", async (_, res) => {
111: const signal = await this.oracle.getPriceSignal();
112: res.json({
113: action: signal.action,
114: price: signal.price,
115: confidence: signal.confidence,
```

The key verification path appears to be:

```text
submitted txHash -> receipt.blockNumber -> any matching USDC Transfer in that block -> paymentId marked verified
```

I did not see a check that `log.transactionHash` equals the submitted `txHash`, or that the payer/paymentId is bound to the accepted Transfer log.

A safer design would inspect the logs from the submitted receipt itself, or require `log.transactionHash === txHash` when querying logs, and bind the accepted payment to a nonce/paymentId. I am reporting this as a potential issue rather than a confirmed exploit; the amount check exists, but the log appears block-scoped rather than transaction-scoped.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

Examine the payment verification logic in keeper/src/core/x402Server.ts, focusing on the verifyPaymentHeader method. The issue is that the code searches for USDC Transfer logs across an entire block instead of the specific transaction. To understand the fix, review the USDC_ABI, the getLogs call, and how transaction receipts are structured. A solution would involve filtering logs by transactionHash or inspecting the receipt's logs directly. Testing requires setting up a local environment with mock transactions to verify the binding.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
solidity, typescript
Área
backend, security
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
30/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.