This tutorial walks you through the steps to test your StableFX integration as a
taker.
Prerequisites
Before you begin, ensure that you have:
- Obtained a sandbox API key for StableFX from Circle
- Obtained testnet USDC in a supported wallet on Arc
- Installed cURL on your development machine
This guide provides API requests in cURL format, along with example responses.
Note: This guide uses the Circle sandbox environment to test StableFX. The
base URL is https://api-sandbox.circle.com/v1/exchange/stablefx. When you are
ready to launch in production, you should update the base URL to the production
URL.
Part 1: Create a trade
To test your integration, you need to create at least one trade in the sandbox
environment. Use the following steps to create a trade:
1.1. Request a quote
Request a quote for a trade from USDC to EURC using the
create a quote endpoint. You should
provide a value for the amount parameter in either the from or to fields,
but not both. The following is an example request for a quote:
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": "10"
},
"to": {
"currency": "EURC"
},
"tenor": "instant"
}
'
Response
{
"id": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"rate": 0.915,
"to": {
"currency": "EURC",
"amount": "9.15"
},
"from": {
"currency": "USDC",
"amount": "10"
},
"timestamp": "2023-01-01T12:04:05Z",
"expiry": "2023-01-01T12:04:05Z",
"fee": {
"currency": "USDC",
"amount": "0.01"
}
}
1.2. Confirm the quote
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.
The following is an example request for a trade:
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": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}
'
Response
{
"id": "c2558cd1-98b5-4ccd-90b8-96891512af20",
"contractTradeId": "b4cdae0f-9285-48bf-8abf-109ae0177621",
"status": "pending",
"rate": 0.915,
"from": {
"currency": "USDC",
"amount": "10"
},
"to": {
"currency": "EURC",
"amount": "9.15"
},
"createDate": "2025-01-01T12:04:05Z",
"updateDate": "2025-01-01T12:04:05Z",
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}
Part 2: Register your signature
Once the trade is created, you need to register your signature with the StableFX
API.
2.1. Get the typed data for the trade
Using the trade ID, get the typed data for the trade using the
generate trade presign data
endpoint. On the taker side, you must provide the recipientAddress parameter.
The following is an example request for the typed data:
curl --request GET \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/signatures/presign/taker/c2558cd1-98b5-4ccd-90b8-96891512af20?recipientAddress=0xabc123... \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
"consideration": {
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"base": "0x...",
"quote": "0x...",
"baseAmount": "10",
"quoteAmount": "9.15",
"maturity": 1752148800
},
"recipient": "0xabc123...",
"deadline": 1752149700,
"fee": "100000"
}
2.2. Sign the typed data
Using your wallet, sign the typed data returned from the previous step.
2.3. Submit the signed data
Submit the signed data to the
submit a trade signature
endpoint. The following is an example request for the signed data:
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": "c2558cd1-98b5-4ccd-90b8-96891512af20",
"type": "taker",
"address": "0xabc123...",
"details": {
"consideration": {
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5",
"base": "0x...",
"quote": "0x...",
"baseAmount": "10",
"quoteAmount": "9.15",
"maturity": 1752148800
},
"recipient": "0xabc123...",
"deadline": 1752149700,
"fee": "100000"
},
"signature": "0xsignature"
}
If the signed data is accepted, the API returns a blank 200 response.
Part 3: Fund the trade
Use the following steps to fund the trade onchain.
3.1. Get trades that are ready for funding
Before you send funds onchain, you should confirm that the trade is ready for
funding. To do this, call the
get all trades endpoint. You should
filter the response by the pending_settlement status. Your trade ID should be
listed in the response.
curl --request GET \
--url https://api-sandbox.circle.com/v1/exchange/stablefx/trades?type=taker&status=pending_settlement \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ${YOUR_API_KEY}'
Response
{
"id": "c2558cd1-98b5-4ccd-90b8-96891512af20",
"contractTradeId": "24",
"status": "pending_settlement",
"rate": 0.915,
"from": {
"currency": "USDC",
"amount": ""
},
"to": {
"currency": "USDC",
"amount": ""
},
"createDate": "2023-01-01T12:04:05Z",
"updateDate": "2023-01-01T12:04:05Z",
"quoteId": "c4d1da72-111e-4d52-bdbf-2e74a2d803d5"
}
3.2. Get the funding signature data
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:
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
{
"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"
}
}
}
}
3.3. Sign the funding data
Using a Permit2-compliant EIP-712 compatible wallet or application, sign the
data from the typedData field returned in the previous step.
3.4. Fund the trade with the StableFX API
The StableFX API can handle the onchain transaction for you. You must submit a
permit2-compliant data object along with the signature to the
fund trades 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:
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": "10"
},
"spender": "0xTOKEN1",
"nonce": "123456",
"deadline": 1752149700,
"witness": {
"id": "123456"
}
}
}
'
If the signed data is accepted, the API returns a blank 200 response.
Note: It’s not required to submit the funding transaction through the
StableFX API. You can submit the transaction onchain using your own web3
provider or wallet.
Testing trade batches
You can repeat parts 1 and 2 to create multiple trades. This can be useful for
testing a batch integration where you fund multiple trades at once. If you are
testing batching, you need to get the contractTradeId for each trade that
requires funding. You can do this by calling the
get all trades endpoint. You can
filter the response by the pending_settlement status.
Once you have the array of IDs, you can use the same
fund trades endpoint to fund multiple
trades by submitting the permit2 data for each trade along with the appropriate
signatures.