SEP: 0038
Title: Anchor RFQ API
Author: Jake Urban <@jakeurban> and Leigh McCulloch <@leighmcculloch>
Track: Standard
Status: Draft
Created: 2021-04-09
Updated: 2022-05-23
Discussion: https://github.com/stellar/stellar-protocol/issues/901
Version 2.0.0
This protocol enables anchors to accept off-chain assets in exchange for different on-chain assets, and vice versa. Specifically, it enables anchors to provide quotes that can be referenced within the context of existing Stellar Ecosystem Proposals. How the exchange of assets is facilitated is outside the scope of this document.
Anchoring an asset and issuing an asset are distinct functions that have different business and technical requirements. However, issuing an asset has often been a prerequisite for anchoring an asset. This protocol enables anchors to transfer value on and off the Stellar network for their clients regardless of whether a one-for-one reserve-backed Stellar asset exists for the off-chain asset held by the anchor's clients.
Removing this requirement for anchors also provides downstream benefits to ecosystem participants generally. Enabling anchors to accept any Stellar asset will naturally decrease liquidity fragmentation on the decentralized exchange, leading to greater market depth and tighter spreads between trading pairs.
An anchor must define the location of their ANCHOR_QUOTE_SERVER
in their stellar.toml
. This is how a wallet knows where to find the anchor's server.
This protocol's endpoints use authentication in the form of a SEP-10 JSON Web Token (JWT) in the Authorization
header of the request.
Authentication is mandatory for POST /quote
and GET /quote/:id
, but optional for the remaining endpoints. When optional, if authentication is provided it can be used to personalize the respective responses.
Authorization: Bearer <jwt>
All endpoints accept in requests the following Content-Type
:
application/json
All endpoints respond with content type:
application/json
If an error occurs when calling any endpoint, an appropriate HTTP status code will be returned along with an error response.
Common HTTP status codes may be returned for a server. In particular the following are expected:
Status Code | Name | Reason |
---|---|---|
400 |
Bad Request | The request is invalid in any way. |
403 |
Permission Denied | No Authorization header has been provided or the contents of the header are not accepted as valid. |
The response body must contain a human-readable description of the reason for error:
{
"error": "The requested asset is not supported. See GET /prices for supported assets."
}
This protocol can be used to provide quotes for any class of asset in exchange for a Stellar asset. The following format is used to identify an asset in API requests and responses.
<scheme>:<identifer>
The currently accepted scheme
values are:
Name | Description |
---|---|
stellar |
Used for Stellar assets. |
iso4217 |
Used for fiat currencies. |
stellar
Identifier Format
The SEP-11 asset format.
iso4217
Identifier Format
The ISO 4217 three-character currency code for the fiat currency.
For example, Stellar USDC would be identified as:
stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
Fiat USD would be identified as:
iso4217:USD
This endpoint describes the supported Stellar assets and off-chain assets available for trading. Note that the anchor may not support a trading pair between every Stellar asset and off-chain asset listed. Use the GET /prices
endpoint to see which pairs are supported.
Optional SEP-10
. If authentication is provided it can be used to personalize the response.
No request arguments required.
A 200 Success
status code should be returned for successful requests.
Name | Type | Description |
---|---|---|
assets |
array | An array of objects describing the assets available in exchange for one or more of the other assets listed. |
assets
Object Schema
Name | Type | Description |
---|---|---|
asset |
string | The Asset Identification Format value. |
sell_delivery_methods |
array | (optional) Only for non-Stellar assets. An array of objects describing the methods a client can use to sell/deliver funds to the anchor. The method of delivery may affect the expiration and/or price provided in a POST /quote response. If the delivery method is not necessary for providing accurate quotes and expirations, the server can omit this attribute. |
buy_delivery_methods |
array | (optional) Only for non-Stellar assets. An array of objects describing the methods a client can use to buy/retrieve funds from the anchor. The method of delivery may affect the expiration and/or price provided in a POST /quote response. If the delivery method is not necessary for providing accurate quotes and expirations, the server can omit this attribute. |
country_codes |
array | (optional) Only for fiat assets. A list of ISO 3166-1 alpha-3 codes of the countries where the Anchor operates for fiat transactions. |
Object Schema for sell_delivery_methods
and buy_delivery_methods
.
Name | Type | Description |
---|---|---|
name |
string | The value to use when making POST /quote requests. |
description |
string | A human readable description of the method identified by name . |
GET /info
{
"assets": [
{
"asset": "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
},
{
"asset": "stellar:BRL:GDVKY2GU2DRXWTBEYJJWSFXIGBZV6AZNBVVSUHEPZI54LIS6BA7DVVSP"
},
{
"asset": "iso4217:BRL",
"country_codes": ["BRA"],
"sell_delivery_methods": [
{
"name": "cash",
"description": "Deposit cash BRL at one of our agent locations."
},
{
"name": "ACH",
"description": "Send BRL directly to the Anchor's bank account."
},
{
"name": "PIX",
"description": "Send BRL directly to the Anchor's bank account."
}
],
"buy_delivery_methods": [
{
"name": "cash",
"description": "Pick up cash BRL at one of our payout locations."
},
{
"name": "ACH",
"description": "Have BRL sent directly to your bank account."
},
{
"name": "PIX",
"description": "Have BRL sent directly to the account of your choice."
}
]
}
]
}
This endpoint can be used to fetch the indicative prices of available off-chain assets in exchange for a Stellar asset and vice versa.
These prices are indicative. The actual price will be calculated at conversion time once the Anchor receives the funds from a User.
The prices returned include any margin that the provider may keep as a service fee, and this margin may vary depending on the directional flow of funds, amount, delivery method, country code, or other factors.
Optional SEP-10
. If authentication is provided it can be used to personalize the response.
Name | Type | Description |
---|---|---|
sell_asset |
string | The asset you want to sell, using the Asset Identification Format. |
sell_amount |
string | The amount of sell_asset the client would exchange for each of the buy_assets . |
sell_delivery_method |
string | (optional) One of the name values specified by the sell_delivery_methods array for the associated asset returned from GET /info . Can be provided if the user is delivering an off-chain asset to the anchor but is not strictly required. |
buy_delivery_method |
string | (optional) One of the name values specified by the buy_delivery_methods array for the associated asset returned from GET /info . Can be provided if the user intends to receive an off-chain asset from the anchor but is not strictly required. |
country_code |
string | (optional) The ISO 3166-1 alpha-3 code of the user's current address. Should be provided if there are two or more country codes available for the desired asset in GET /info . |
A 200 Success
status code should be returned for successful requests.
Name | Type | Description |
---|---|---|
buy_assets |
array | An array of objects containing information on the assets that the client will receive when they provide sell_asset . |
buy_assets
Object Schema
Name | Type | Description |
---|---|---|
asset |
string | The Asset Identification Format value. |
price |
string | The price offered by the anchor for one unit of asset in terms of sell_asset . In traditional finance, asset would be referred to as the base asset and sell_asset as the counter asset. |
decimals |
integer | The number of decimals needed to represent asset . |
GET /prices?sell_asset=stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&sell_amount=100&country_code=BRA&buy_delivery_method=ACH
{
"buy_assets": [
{
"asset": "iso4217:BRL",
"price": "0.18",
"decimals": 2
}
]
}
GET /prices?sell_asset=iso4217:BRL&sell_amount=500&country_code=BRA&sell_delivery_method=PIX
{
"buy_assets": [
{
"asset": "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"price": "5.42",
"decimals": 7
}
]
}
This endpoint can be used to fetch the indicative price for a given asset pair.
The client must provide either sell_amount
or buy_amount
, but not both.
These prices are indicative. The actual price will be calculated at conversion time once the Anchor receives the funds from a User.
Fees can be collected by adding a margin to the price, and/or by deducting an amount from the funds provided by or delivered to the client. Both the margin and the fees may vary depending on the directional flow of funds, amount, delivery method, country code, context or other factors.
Optional SEP-10
. If authentication is provided it can be used to personalize the response.
Name | Type | Description |
---|---|---|
sell_asset |
string | The asset the client would like to sell. Ex. USDC:G... , iso4217:ARS |
buy_asset |
string | The asset the client would like to exchange for sell_asset . |
sell_amount |
string | The amount of sell_asset the client would like to exchange for buy_asset . |
buy_amount |
string | The amount of buy_asset the client would like to purchase with sell_asset . |
sell_delivery_method |
string | (optional) One of the name values specified by the sell_delivery_methods array for the associated asset returned from GET /info . Can be provided if the user is delivering an off-chain asset to the anchor but is not strictly required. |
buy_delivery_method |
string | (optional) One of the name values specified by the buy_delivery_methods array for the associated asset returned from GET /info . Can be provided if the user intends to receive an off-chain asset from the anchor but is not strictly required. |
country_code |
string | (optional) The ISO 3166-1 alpha-3 code of the user's current address. Should be provided if there are two or more country codes available for the desired asset in GET /info . |
context |
string | The context for what this quote will be used for. Must be one of sep6 or sep31 . |
A 200 Success
status code should be returned for successful requests.
Name | Type | Description |
---|---|---|
total_price |
string | The total conversion price offered by the anchor for one unit of buy_asset in terms of sell_asset , including fees. In traditional finance, buy_asset would be referred to as the base asset and sell_asset as the counter asset. |
price |
string | The conversion price offered by the anchor for one unit of buy_asset in terms of sell_asset , without including fees. In traditional finance, buy_asset would be referred to as the base asset and sell_asset as the counter asset. |
sell_amount |
string | The amount of sell_asset the anchor will exchange for buy_asset . It could be different from the sell_amount provided in the request, depending on how fees are applied by the Anchor. |
buy_amount |
string | The amount of buy_asset the anchor will provide with sell_asset . It could be different from the buy_amount provided in the request, depending on how fees are applied by the Anchor. |
fee |
object | An object describing the fee used to calculate the conversion price. This can be used to datail the price components for the end-user. |
To better understand the relation between the *price
, *amount
and fee
fields, please refer to the Price Formulas section.
Object Schema for fee
:
Name | Type | Description |
---|---|---|
total |
string | The total amount of fee applied. |
asset |
string | The asset in which the fee is applied, represented through the Asset Identification Format. |
details |
array | (optional) An array of objects detailing the fees that were used to calculate the conversion price. This can be used to datail the price components for the end-user. |
Object Schema for fee.details
:
Name | Type | Description |
---|---|---|
name |
string | The name of the fee, for example ACH fee , Brazilian conciliation fee , Service fee , etc. |
description |
string | (optional) A text describing the fee. |
amount |
string | The amount of asset applied. If fee.details is provided, sum(fee.details.amount) should be equals fee.total . |
GET /price?sell_asset=iso4217:BRL&buy_asset=stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&sell_amount=500&sell_delivery_method=PIX&country_code=BRA&context=sep6
{
"total_price": "5.42", // 542/100
"price": "5.00", // (542-42)/100
"sell_amount": "542",
"buy_amount": "100",
"fee": {
"total": "42.00",
"asset": "iso4217:BRL"
}
}
GET /price?sell_asset=iso4217:BRL&buy_asset=stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&buy_amount=100&sell_delivery_method=PIX&country_code=BRA&context=sep31
{
"total_price": "5.42", // 542/100
"price": "5.00", // 542/(100+8.40)
"sell_amount": "542",
"buy_amount": "100",
"fee": {
"total": "8.40",
"asset": "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"details": [
{
"name": "Service fee",
"amount": "8.40"
}
]
}
}
GET /price?sell_asset=stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&buy_asset=iso4217:BRL&sell_amount=90&buy_delivery_method=PIX&country_code=BRA&context=sep6
{
"total_price": "0.20", // 100/500
"price": "0.18", // 100/(500+55.5556)
"sell_amount": "100",
"buy_amount": "500",
"fee": {
"total": "55.5556",
"asset": "iso4217:BRL",
"details": [
{
"name": "PIX fee",
"description": "Fee charged in order to process the outgoing PIX transaction.",
"amount": "55.5556"
}
]
}
}
GET /price?sell_asset=stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&buy_asset=iso4217:BRL&buy_amount=500&buy_delivery_method=PIX&country_code=BRA&context=sep31
{
"total_price": "0.20", // 100/500
"price": "0.18", // (100-10)/500
"sell_amount": "100",
"buy_amount": "500",
"fee": {
"total": "10.00",
"asset": "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"details": [
{
"name": "Service fee",
"amount": "5.00"
},
{
"name": "PIX fee",
"description": "Fee charged in order to process the outgoing BRL PIX transaction.",
"amount": "5.00"
}
]
}
}
The following should hold true for all prices and quotes returned in the SEP-38 responses.
sell_amount = total_price * buy_amount
sell_amount - fee = price * buy_amount // when `fee` is in `sell_asset`
- or -
sell_amount = price * (buy_amount + fee) // when `fee` is in `buy_asset`
This endpoint can be used to request a firm quote for a Stellar asset and off-chain asset pair.
In contrast with the GET /price
and GET /prices
endpoints, the amount requested must be held in reserve and not used in calculations of subsequent quotes until the expiration provided in the response.
To protect against bad actor clients reserving all available capital without following through with the requested trades, servers should only accept requests for quotes from Stellar accounts that belong to entities or individuals that have been properly KYC'ed, either via SEP-12 or some other mechanism.
Servers may deny access to the API if misuse is detected.
Fees can be collected by adding a margin to the price, and/or by deducting an amount from the funds provided by or delivered to the client. Both the margin and the fees may vary depending on the directional flow of funds, amount, delivery method, country code, context or other factors.
If the client requests some amount of an off-chain asset for providing some amount of a Stellar asset, the client will submit a Stellar transaction delievering funds to the anchor before the expiration included in the response, paying a fee as determined by state of the Stellar network.
In the reverse scenario, the anchor will submit a Stellar transaction to deliver funds to the client as long as the client delivered off-chain funds to the anchor before the expiration. In this case, the anchor will likely increase their margin to cover the cost of submitting the transaction.
The client must provide either sell_amount
or buy_amount
, but not both.
Unless the list included in the GET /info
response is empty or missing for the associated off-chain asset, the client must also provide either sell_delivery_method
or buy_delivery_method
, but not both.
Name | Type | Description |
---|---|---|
sell_asset |
string | Same as the definition of sell_asset in GET /price . |
buy_asset |
string | Same as the definition of buy_asset in GET /price . |
sell_amount |
string | Same as the definition of sell_amount in GET /price . |
buy_amount |
string | The same definition of buy_amount in GET /price . |
expire_after |
UTC ISO 8601 string | (optional) The client's desired expires_at date and time for the quote. Anchors may choose an expires_at that occurs after the expire_after . Anchors should return 400 Bad Request if the an expiration on or after the requested value cannot be provided. |
sell_delivery_method |
string | (optional) One of the name values specified by the sell_delivery_methods array for the associated asset returned from GET /info . Must be provided if the user is delivering an off-chain asset to the anchor unless no more than one method is specified in the GET /info response. |
buy_delivery_method |
string | (optional) One of the name values specified by the buy_delivery_methods array for the associated asset returned from GET /info . Must be provided if the user intends to receive an off-chain asset from the anchor unless no more than one method is specified in the GET /info response. |
country_code |
string | (optional) The ISO 3166-1 alpha-3 code of the user's current address. Should be provided if there are two or more country codes available for the desired asset in GET /info . |
context |
string | The context for what this quote will be used for. Must be one of sep6 or sep31 . |
A 201 Created
status code should be returned for successful requests.
Name | Type | Description |
---|---|---|
id |
string | The unique identifier for the quote to be used in other Stellar Ecosystem Proposals (SEPs). |
expires_at |
UTC ISO 8601 string | The date and time by which the anchor must receive funds from the client. |
total_price |
string | The total conversion price offered by the anchor for one unit of buy_asset in terms of sell_asset , including fees. In traditional finance, buy_asset would be referred to as the base asset and sell_asset as the counter asset. |
price |
string | The conversion price offered by the anchor for one unit of buy_asset in terms of sell_asset , without including fees. In traditional finance, buy_asset would be referred to as the base asset and sell_asset as the counter asset. |
sell_asset |
string | The asset the client would like to sell. Ex. USDC:G... , iso4217:ARS |
sell_amount |
string | The amount of sell_asset to be exchanged for buy_asset . It could be different from the sell_amount provided in the request, depending on how fees are applied by the Anchor. |
buy_asset |
string | The asset the client would like to exchange for sell_asset . |
buy_amount |
string | The amount of buy_asset to be exchanged for sell_asset . It could be different from the buy_amount provided in the request, depending on how fees are applied by the Anchor. price * buy_amount = sell_amount must be true up to the number of decimals required for buy_asset . |
fee |
object | An object describing the fee used to calculate the conversion price. This can be used to datail the price components for the end-user. |
To better understand the relation between the *price
, *amount
and fee
fields, please refer to the Price Formulas section.
This endpoint can be used to fetch a previously-provided firm quote. Quotes referenced in other protocols must be available at this endpoint past the expires_at
expiration for the quote.
Name | Type | Description |
---|---|---|
id |
string | The unique identifier for the quote. Same as the id returned in the POST /quote response. |
This response body should match the response from POST /quote.
Name | Type | Description |
---|---|---|
id |
string | The id specified in the request. |
expires_at |
UTC ISO 8601 string | The date and time by which the anchor must receive funds from the client. |
price |
string | The price offered by the anchor for one unit of buy_asset in terms of sell_asset . In traditional finance, buy_asset would be referred to as the base asset and sell_asset as the counter asset. |
sell_asset |
string | The asset the client would like to sell. Ex. USDC:G... , iso4217:ARS |
sell_amount |
string | The amount of sell_asset to be exchanged for buy_asset . |
buy_asset |
string | The asset the client would like to exchange for sell_asset . |
buy_amount |
string | The amount of buy_asset to be exchanged for sell_asset . price * buy_amount = sell_amount must be true up to the number of decimals required for buy_asset . |
fee |
object | An object describing the fee used to calculate the conversion price. This can be used to datail the price components for the end-user. |
GET /quote/de762cda-a193-4961-861e-57b31fed6eb3
{
"id": "de762cda-a193-4961-861e-57b31fed6eb3",
"expires_at": "2021-04-30T07:42:23",
"total_price": "5.42", // 542/100
"price": "5.00", // (542-42)/100
"sell_asset": "iso4217:BRL",
"sell_amount": "542",
"buy_asset": "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN&",
"buy_amount": "100",
"fee": {
"total": "42.00",
"asset": "iso4217:BRL",
"details": [
{
"name": "PIX fee",
"description": "Fee charged in order to process the outgoing PIX transaction.",
"amount": "12.00"
},
{
"name": "Brazilian conciliation fee",
"description": "Fee charged in order to process conciliation costs with intermediary banks.",
"amount": "15.00"
},
{
"name": "Service fee",
"amount": "15.00"
}
]
}
}
v2.0.0
: (i) addcontext
to the request body andfee
+total_price
to the response body of thePOST /quote
andGET /price
requests; (ii) addfee
to the response body ofGET /quote/:id
; and (iii) allowsell_amount
andbuy_amount
in the responses to be different from the ones sent in the request. (#1204)v1.6.1
: Add note aboutexpires_at
andexpires_after
and their relationship. (#1147)v1.6.0
: MakeSEP-10
authentication token optional for theGET /info
,GET /price
andGET /prices
endpoints. (#1144)