# Broadcast Swap Transaction

Non-custodial EVM swaps, step 2 — submit the signature for the `txHashToSign` returned by Execute Swap.

## Overview

When [Execute Swap](https://docs.aureahub.com/docs/swap-execute.md) returns `requiresClientSigning: true`, sign `txHashToSign` with the wallet key. It is the keccak-256 hash of the unsigned EIP-1559 transaction. Send `transactionId`, `preparedTxId` and the signature here. You do not send a signed raw transaction: the API rebuilds the transaction it prepared, checks that the signature recovers to the wallet's address, and broadcasts it.

Before broadcasting, the API checks that the transaction record is a swap owned by you, is still `pending` and references this `preparedTxId`. It also checks that the quote window has not closed: `quoteExpiry` from Execute Swap, or 60 seconds after Execute Swap when none was sent. On success the record gets the real `txHash` and stays `pending`; poll [Get Tx Status](https://docs.aureahub.com/docs/tx-status.md) with `transactionId`.

- `v` is the recovery parity, `0` or `1`; `r` and `s` are `0x` + 64 hex characters.
- `fromToken`, `toToken`, `fromAmount` and `expectedToAmount` in the response echo the optional metadata you sent to Execute Swap (empty strings otherwise).
- The prepared transaction expires 5 minutes after Execute Swap (`expiresAt`). Rate limit: 10 requests per 10 seconds.

> ⚠️ The prepared transaction is marked as used **before** the signature is checked. After a `422`, the same `preparedTxId` returns `409` — call Execute Swap again, with a fresh quote if the window has closed.

## Endpoint

### `POST /v1/swap/broadcast`

Authentication: bearer token required.

Validates a client signature for a prepared swap transaction and broadcasts it.

**Request body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `transactionId` | string (uuid) | yes | `transactionId` from Execute Swap. |
| `preparedTxId` | string (uuid) | yes | `preparedTxId` from Execute Swap. |
| `v` | integer | yes | Signature recovery parity: `0` or `1`. |
| `r` | string | yes | `0x` + 64 hex characters. |
| `s` | string | yes | `0x` + 64 hex characters. |

**Responses**

`200` OK

```json
{
  "transactionId": "…",
  "txHash": "0x…",
  "status": "pending",
  "fromToken": "…",
  "toToken": "…",
  "fromAmount": "…",
  "expectedToAmount": "…"
}
```

`400` Quote Expired

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "QUOTE_EXPIRED: The LiFi quote has expired. Please request a new quote."
}
```

`422` Wrong Signer

```json
{
  "statusCode": 422,
  "error": "BlockchainError",
  "message": "Signature does not correspond to the expected wallet address",
  "code": "WRONG_SIGNER",
  "retryable": false,
  "details": { "code": "WRONG_SIGNER", "retryable": false }
}
```

## Errors

- `400` — `Request validation failed`; `Transaction is not a swap`; `Transaction is not in pending state (current: …)`; `preparedTxId does not match the transaction record`; `QUOTE_EXPIRED: …`; `Prepared transaction not found`.
- `404` `Transaction not found` — unknown `transactionId`, or another user's transaction. `404` `PREPARED_TX_NOT_FOUND` — the prepared transaction does not belong to the transaction's wallet.
- `409` `PREPARED_TX_ALREADY_USED` — already submitted.
- `410` `PREPARED_TX_EXPIRED` — more than 5 minutes after Execute Swap.
- `422` `SIGNATURE_DECODE_FAILED` — `v`, `r`, `s` could not be decoded. `422` `WRONG_SIGNER` — the signature does not recover to the wallet address.

## Implementation

```javascript
import { SigningKey } from 'ethers';

// exec = response of POST /v1/swap/execute with requiresClientSigning: true
async function broadcastSwap(token, exec, privateKey) {
  const sig = new SigningKey(privateKey).sign(exec.txHashToSign);

  const res = await fetch('https://api.aureahub.com/v1/swap/broadcast', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({
      transactionId: exec.transactionId,
      preparedTxId: exec.preparedTxId,
      v: sig.yParity, // 0 or 1
      r: sig.r,
      s: sig.s
    })
  });

  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.code ?? ''}: ${body.message}`);

  // { transactionId, txHash, status: 'pending', ... } — poll /v1/transactions/{id}/status
  return body;
}
```

---

Web version: https://docs.aureahub.com/#swap-broadcast
