---
title: "Prepare a swap — returns UNSIGNED transactions for your wallet to sign"
method: POST
path: "/v1/swaps"
tags: ["positions"]
---

# Prepare a swap — returns UNSIGNED transactions for your wallet to sign

`POST /v1/swaps`

## Request

- Base URL: `https://yield.trdefi.com`
- URL: `https://yield.trdefi.com/v1/swaps`
- Auth: HTTP bearer

Swap against a maker position that is already shipped. The **0.05% protocol fee rides inside the maker's order**, so every swap that executes pays it without anything extra being asked of you.

**Preflight first.** The swap is simulated on-chain. If it cannot succeed — a gated strategy, an amount the maker cannot fill, an insufficient balance — the response says which, and returns **no transactions at all**. A wallet is never handed a transaction that is certain to revert.

**Approval only when needed.** The `approve` step appears only if your live allowance is short, so the same request is sometimes one transaction and sometimes two.

**Networks.** `ethereum`, `base`, `arbitrum`, `optimism`, `polygon`, `arc`. The Base Sepolia demo has its own user-signed flow on the app and is deliberately not served here.

**The client signs.** We never sign, never broadcast and never hold a key.

## Request body

- SwapRequest
  - `chain` 'ethereum' | 'base' | 'arbitrum' | 'optimism' | 'polygon' | 'arc', required: Network key.
  - `wallet` string, required: The address that will sign. It must be yours.
  - `strategyHash` string, required: The maker position to swap against.
  - `amount` string, required: Input amount in base units, as an integer string.
  - `direction` 'aToB' | 'bToA': Ignored when `tokenIn` and `tokenOut` are given.
  - `tokenIn` string: Optional explicit input token.
  - `tokenOut` string: Optional explicit output token.

## Response `200`

Prepared swap. Inert: nothing has been signed or broadcast.

- PreparedSwap
  - `data` object, required
    - `status` 'prepared', required
    - `chain` string
    - `chainId` integer
    - `wallet` string
    - `strategyHash` string, required
    - `pair` string, nullable
    - `tokenIn` string, required
    - `tokenOut` string, required
    - `amountIn` string
    - `expected` object: A simulation at the current block, not a promise. Add your own threshold in the transaction data if you need protection against it moving before you sign.
      - `amountIn` string
      - `amountOut` string
      - `orderHash` string
      - `mode` 'simulated'
    - `allowance` string: Live allowance of the input token to the engine, or the string `unknown`.
    - `transactions` PreparedTransaction[], required: Ordered. The `approve` step appears only if the live allowance is short.
      - `step` integer, required: 1-based. Execute in this order.
      - `purpose` 'approve' | 'ship' | 'swap' | 'dock', required: What this transaction does.
      - `to` string, required: Target contract.
      - `value` string, required: Native value in wei, as a string. Almost always `0`.
      - `data` string, required: Calldata. This is the payload you sign.
      - `description` string: Plain-language explanation of this step, suitable for showing to a human approver.
    - `fee` FeeBreakdown
      - `protocolFeeBps` 5000, required: The protocol fee in the protocol's 1e7 scale. 5000 = 0.05%. A constant, not a parameter.
      - `protocolFeePercent` '0.05', required
      - `makerFeeBps` integer: The integrator's own commission, as supplied.
      - `makerFeePercent` string
      - `totalFeePercent` string
      - `embeddedIn` 'signed_order_data': The fee lives inside the order data you sign. It is not a separate transfer or an invoice.
      - `removable` false, required: Always false. No parameter, tier or agreement waives the protocol fee.
      - `explanation` string
  - `meta` Meta, required
    - `requestId` string, required
    - `generatedAt` string, date-time, required
    - `apiVersion` string, required
    - `freshnessSeconds` integer, nullable: How old the underlying roll-up is, in seconds. Null when not derived.
    - `cached` boolean: True when this response was served from our short-lived cache.

## Other responses

- `400`: The request was refused and NO payment was taken. Validation runs before settlement, so a rejected body costs nothing. `charged` is always false here and no PAYMENT-RESPONSE header is returned.
- `401`: Missing, malformed, revoked or out-of-scope key. The envelope never says which — a wrong key and a revoked key are indistinguishable.
- `402`: No credential was presented, so the endpoint answered with its x402 payment terms instead of an error. The body carries `accepts` (price, network, asset, payTo) and a `bazaar` extension describing this route. Pay with your own wallet, then retry the same request with the base64 payload in the `X-PAYMENT` header. An API key works instead and costs nothing.
- `403`: Origin or scope not allowed — the endpoint is restricted to TRDEFI pages, or the key does not carry the `prepare` scope.
- `404`: No such position — or it belongs to a different key. The two are deliberately indistinguishable.
- `422`: The request was refused and NO payment was taken. Validation runs before settlement, so a rejected body costs nothing. `charged` is always false here and no PAYMENT-RESPONSE header is returned.
- `429`: Rate limit exceeded. `Retry-After` is always present.

---

[API](https://skmtc.dev/trdefi/apis/trdefi-liquidity-api.md) · [All operations](https://skmtc.dev/trdefi/apis/trdefi-liquidity-api/llms.txt) · [OpenAPI document](https://skmtc.dev/trdefi/apis/trdefi-liquidity-api/revisions/0175596aa1cb?raw)
