StableFX

Quickstart: Create an FX trade as a taker

This guide walks you through the steps to create and execute a trade from the taker side on StableFX.

Before you begin this quickstart, ensure you have:

  • Obtained an API key for StableFX from Circle
  • Set up a web3 provider or wallet that supports EIP-712 signatures and Permit2
  • Granted a token allowance to the Permit2 contract. See How-to: Grant USDC Allowance to Permit2 for more information.
  • Installed cURL on your development machine

This quickstart provides API request examples in cURL format, along with example responses.

Request a quote for a USDC to EURC trade using the create a quote endpoint. You should provide a value for the amount parameter in either the to or from fields, but not both.

Shell
curl --request POST \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/quotes \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
  --header 'Content-Type: application/json' \
  --data '
{
  "from": {
    "currency": "USDC",
    "amount": "1000.00"
  },
  "to": {
    "currency": "EURC"
  },
  "tenor": "instant"
}
'

Response

JSON
{
  "id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
  "rate": "0.9150",
  "from": {
    "currency": "USDC",
    "amount": "1000.00"
  },
  "to": {
    "currency": "EURC",
    "amount": "915.00"
  },
  "timestamp": "2025-08-07T11:00:00Z",
  "expiry": "2025-08-07T11:05:00Z",
  "fee": {
    "currency": "USDC",
    "amount": "1.50"
  }
}

Accept the quote and create a trade on StableFX using the create a trade endpoint. You need to provide the quote ID and a randomly generated idempotency key in UUIDv4 format.

Shell
curl --request POST \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/trades \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
  --header 'Content-Type: application/json' \
  --data '
  {
    "idempotencyKey": "${randomUUID}",
    "quoteId": "${stablefx_quote_id}"
  }
'

Response

JSON
{
  "id": "uuid-trade",
  "from": {
    "currency": "USDC",
    "amount": "1000.00"
  },
  "to": {
    "currency": "EURC",
    "amount": "915.00"
  },
  "status": "created",
  "createDate": "2025-08-07T11:01:00Z",
  "updateDate": "2025-08-07T11:01:00Z",
  "quoteId": "uuid-quote",
  "rate": "0.9150"
}

Confirm your trade intent through the StableFX API before submitting the trade on the blockchain.

To submit the trade, you must first sign the trade intent typed data with an EIP-712 signature. To get the data to sign, call the generate trade presign data endpoint. Your request must include the ID of the trade in a confirmed status and the address of the recipient of the trade.

Shell
curl --request GET \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures/presign/taker/${stablefx_trade_id}?recipientAddress=${recipient_address} \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
'

Response

JSON
{
  "data": {
    "typedData": {
      "domain": {
        "name": "FxEscrow",
        "version": "1",
        "chainId": 5042002,
        "verifyingContract": "0x1f91886c7028986ad885ffcee0e40b75c9cd5ac1"
      },
      "types": {
        "EIP712Domain": [
          {
            "name": "name",
            "type": "string"
          },
          {
            "name": "version",
            "type": "string"
          },
          {
            "name": "chainId",
            "type": "uint256"
          },
          {
            "name": "verifyingContract",
            "type": "address"
          }
        ],
        "Consideration": [
          {
            "name": "quoteId",
            "type": "bytes32"
          },
          {
            "name": "base",
            "type": "address"
          },
          {
            "name": "quote",
            "type": "address"
          },
          {
            "name": "baseAmount",
            "type": "uint256"
          },
          {
            "name": "quoteAmount",
            "type": "uint256"
          },
          {
            "name": "maturity",
            "type": "uint256"
          }
        ],
        "TakerDetails": [
          {
            "name": "consideration",
            "type": "Consideration"
          },
          {
            "name": "recipient",
            "type": "address"
          },
          {
            "name": "fee",
            "type": "uint256"
          },
          {
            "name": "nonce",
            "type": "uint256"
          },
          {
            "name": "deadline",
            "type": "uint256"
          }
        ]
      },
      "primaryType": "TakerDetails",
      "message": {
        "consideration": {
          "quoteId": "0x00000000000000000000000000000000f054a0d91c1747f3be7885d5c38dd0f1",
          "base": "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
          "quote": "0x3600000000000000000000000000000000000000",
          "quoteAmount": 1000000000,
          "baseAmount": 908250000,
          "maturity": 1762189673
        },
        "fee": 181650,
        "nonce": 25426119,
        "deadline": 1762188474,
        "recipient": "0x379c868f6064d9c0564df05dcca170d64f8aa5e3"
      }
    }
  }
}

Using a Permit2-compliant EIP-712 compatible wallet or application, sign the data returned in the previous step.

Confirm your trade intent by submitting the signed data to the register trade signature endpoint. This request must include the trade ID, the side of the trade that you are taking, the address of the wallet that signed the typed data, the message of the typed data, and the taker's signature.

Shell
curl --request POST \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
  --header 'Content-Type: application/json' \
  --data '
{
  "tradeId": "${stablefx_trade_id}",
  "type": "taker",
  "address": "0xabc123...",
  "details": {
    "consideration": {
      "quoteId": 1234,
      "base": "0x...",
      "quote": "0x...",
      "baseAmount": "100000000",
      "quoteAmount": "90000000",
      "maturity": 1752148800
    },
    "recipient": "0xabc123...",
    "nonce": "123456",
    "deadline": 1752149700,
    "fee": "100000"
  },
  "signature": "0xsignature"
}
'

If the signed data is accepted, the API returns a blank 200 response.

Before you submit funds onchain, you should confirm that the trade is ready for funding. To do this, call the get a trade endpoint. A trade is ready for funding when the status is pending_settlement.

Shell
curl --request GET \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/trades/${stablefx_trade_id}?type=taker&status=pending_settlement \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}'

Response

JSON
{
  "id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
  "contractTradeId": "24",
  "status": "pending_settlement",
  "rate": 0.915,
  "from": {
    "currency": "USDC",
    "amount": "1000"
  },
  "to": {
    "currency": "EURC",
    "amount": "915"
  },
  "createDate": "2023-01-01T12:04:05Z",
  "updateDate": "2023-01-01T12:04:05Z",
  "quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}

Next, you need to deliver the funds onchain to complete the trade. The StableFX API can handle the onchain transaction for you using the Permit2 contract.

To use the StableFX API to deliver the funds onchain, you must first sign the funding typed data with an EIP-712 signature. To get the data to sign, call the generate funding presign data endpoint. Your request must include the contract ID of the trade and the side of the trade that you are taking. The following an example request:

Shell
curl --request POST \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures/funding/presign \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
  --header 'Content-Type: application/json' \
  --data '
{
  "contractTradeIds": ["${stablefx_contract_trade_id}"],
  "type": "taker"
}
'

Response

JSON
{
  "typedData": {
    "domain": {
      "name": "Permit2",
      "chainId": 11155111,
      "verifyingContract": "0xffd21ca8F0876DaFAD7de09404E0c1f868bbf1AE"
    },
    "types": {
      "EIP712Domain": [
        {
          "name": "name",
          "type": "string"
        },

        {
          "name": "chainId",
          "type": "uint256"
        },

        {
          "name": "verifyingContract",
          "type": "address"
        }
      ],
      "TokenPermissions": [
        {
          "name": "token",
          "type": "address"
        },

        {
          "name": "amount",
          "type": "uint256"
        }
      ],
      "SingleTradeWitness": [
        {
          "name": "id",
          "type": "uint256"
        }
      ],
      "PermitWitnessTransferFrom": [
        {
          "name": "permitted",
          "type": "TokenPermissions"
        },

        {
          "name": "spender",
          "type": "address"
        },

        {
          "name": "nonce",
          "type": "uint256"
        },

        {
          "name": "deadline",
          "type": "uint256"
        },

        {
          "name": "witness",
          "type": "SingleTradeWitness"
        }
      ]
    },
    "primaryType": "PermitWitnessTransferFrom",
    "message": {
      "permitted": {
        "token": "0xTOKEN",
        "amount": "1000"
      },
      "spender": "0xffd21ca8F0876DaFAD7de09404E0c1f868bbf1AE",
      "nonce": "42",
      "deadline": "1735689600",
      "witness": {
        "id": "10"
      }
    }
  }
}

Using a Permit2-compliant EIP-712 compatible wallet or application, sign the data from the typedData field returned in the previous step.

To deliver the funds onchain, you must submit the taker-specific Permit2 data along with your signature to the fund trade endpoint. The endpoint allows you to submit either a single object or a batch of objects along with the required signatures.

The following is an example request for a single trade:

Shell
curl --request POST \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/fund \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}' \
  --header 'Content-Type: application/json' \
  --data '
{
  "type": "taker",
  "signature": "0xsignature",
  "permit2": {
    "permitted": {
      "token": "0xTOKEN1",
      "amount": "1000"
    },
    "spender": "0xTOKEN1",
    "nonce": "123456",
    "deadline": 1752149700,
    "witness": {
      "id": "123456"
    }
  }
}
'

If the signed data is accepted, the API returns a blank 200 response.

To confirm that the trade is funded, call the get a trade endpoint. A trade is funded when the status is taker_funded.

Shell
curl --request GET \
  --url https://api-sandbox.circle.com/v1/exchange/stablefx/trades/${stablefx_trade_id}?type=taker&status=completed \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer ${YOUR_API_KEY}'

Response

JSON
{
  "id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
  "contractTradeId": "24",
  "status": "taker_funded",
  "rate": 0.915,
  "from": {
    "currency": "USDC",
    "amount": "1000"
  },
  "to": {
    "currency": "EURC",
    "amount": "915"
  },
  "createDate": "2023-01-01T12:04:05Z",
  "updateDate": "2023-01-01T12:04:05Z",
  "quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}
Did this page help you?
© 2023-2025 Circle Technology Services, LLC. All rights reserved.