Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 4
docs: add LI.FI Intents endpoints under a Solve RFQ section#222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
alexmmueller
wants to merge
5
commits into
mainChoose a base branch
from
docs/221-lifi-intents-endpoints
base:main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Uh oh!
There was an error while loading. Please reload this page.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
61df1aa
docs: add LI.FI Intents endpoints under a Solve RFQ section
alexmmueller e16b023
docs: route integrator flows through the RFQ endpoint
alexmmueller 358478a
docs: consolidate the Sprinter Liquidity section
alexmmueller 87da64a
docs: add a Pricing & Credit Facilities section
alexmmueller 29c0a1e
docs: give Solve RFQ an entry point in the Documentation tab
alexmmueller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| --- | ||
| title: "Overview" | ||
| description: "Request a firm, liquidity-backed quote through LI.FI Intents, or route a swap against public on-chain liquidity" | ||
| --- | ||
| Solve RFQ covers the two ways to get an executable price out of Sprinter. | ||
| | | **LI.FI Intents** | **Swap API** | | ||
| |---|---|---| | ||
| | **Liquidity** | Sprinter Liquidity — underwritten, reserved for your quote | Public on-chain AMM liquidity | | ||
| | **Price** | Firm. Reserved for 15s and filled at exactly that price | Indicative, subject to slippage | | ||
| | **Use for** | Redemptions, subscriptions, and any flow where the user is promised a price | Generic token swaps, and as a fallback when no Sprinter route exists | | ||
| | **Requires** | Asset onboarded and route configured with Sprinter | Nothing — any supported pair | | ||
| | **Base URL** | `https://api.sprinter.tech` | `https://swaps.sprinter.tech/{network}` | | ||
| | **Auth** | `X-Auth-Token` header | HTTP Basic | | ||
| <Tip> | ||
| **Default to LI.FI Intents.** It is the path that draws on Sprinter Liquidity, which is what makes an instant fill at a promised price possible. Reach for the Swap API when the pair is not onboarded with Sprinter, or as a fallback when a quote does not return. | ||
| </Tip> | ||
| ## LI.FI Intents | ||
| Two calls. The first prices the order and holds the liquidity; the second turns that into a transaction the user signs. | ||
| ``` | ||
| GET /lifi-intents/rfq → quote (liquidity reserved, 15s) | ||
| POST /lifi-intents/transaction → same quote + unsigned open() calldata | ||
| → user sends it; Sprinter fills | ||
| ``` | ||
| | Endpoint | Description | | ||
| |----------|-------------| | ||
| | [`GET /lifi-intents/rfq`](/api-reference/sprinter/lifi-intents/rfq) | Firm quote backed by reserved Sprinter liquidity | | ||
| | [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) | Escrow `open` transaction for a quote | | ||
| Authenticate the RFQ call with your API key in the `X-Auth-Token` header. The transaction call carries the quote itself, so it needs no header. | ||
| The responses follow LI.FI's own [intents API](https://docs.li.fi/lifi-intents/intents-api/request-quote) shapes, with one difference worth coding for: when Sprinter cannot serve a request it returns **`404`**, not a `200` with an empty `quotes` array. | ||
| <Note> | ||
| Quotes only return for assets Sprinter has onboarded — underwritten, allocated liquidity to, and configured routes for. See the [Asset Issuer quickstart](/quickstart/asset-issuer) for what onboarding involves. | ||
| </Note> | ||
| ## Swap API | ||
| Route a swap against public on-chain liquidity and get back executable call data. No onboarding, no reservation, no firm price. | ||
| | Endpoint | Description | | ||
| |----------|-------------| | ||
| | [`GET /v1/route`](/api-reference/solve/get-v1route) | Optimal swap route and execution call data | | ||
| See the [Swap API overview](/api-reference/solve/overview) for base URLs, authentication, and the response format. | ||
| ## Not this: the Liquidity API | ||
| [Sprinter Liquidity](/api-reference/sprinter/liquidity/overview) exposes the borrow-quote and signing endpoints directly. That surface is for **crosschain solvers** running their own fill infrastructure — it hands you a borrow authorization, not a transaction, and expects you to settle the intent yourself. | ||
| If you are an asset issuer, wallet, or application asking Sprinter for a price, use LI.FI Intents. The RFQ endpoint runs the same pricing and reservation pipeline and returns something you can sign. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| --- | ||
| title: "Request a LI.FI Intents Quote" | ||
| sidebarTitle: "RFQ" | ||
| openapi: get /lifi-intents/rfq | ||
| --- | ||
| Prices an order against Sprinter liquidity and **reserves that liquidity** for the quote's validity window. The response is shaped like a single element of LI.FI's [request-quote](https://docs.li.fi/lifi-intents/intents-api/request-quote) response, so an existing LI.FI intents client can consume it unchanged. | ||
| <Warning> | ||
| A quote is valid for **15 seconds**. The reservation expires with it. Call [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) immediately — do not cache a quote or show it to a user for confirmation before converting it. | ||
| </Warning> | ||
| ## Behaviour | ||
| | | | | ||
| |---|---| | ||
| | **Exclusivity** | Always exclusive to Sprinter. `metadata.exclusiveFor` returns the filler address | | ||
| | **Protocol** | Always `lifi-escrow`. Your API key must be provisioned for it | | ||
| | **Quotes returned** | Exactly one, or an error. There is no empty `quotes` array | | ||
| | **Amount basis** | `type=ExactInput` (default) reads `amount` as the input; `ExactOutput` reads it as the output | | ||
| | **Token defaults** | `srcToken` defaults to the same token symbol as `token`, resolved on the source chain | | ||
| ## Errors | ||
| | Status | Meaning | Retry? | | ||
| |--------|---------|--------| | ||
| | `400` | No route configured for this chain/token pair, or invalid parameters | No — fix the request or ask about onboarding the route | | ||
| | `404` | Route exists, but no pool can serve it right now — capacity is reserved by other in-flight quotes, or pricing is unavailable | Yes, after a short backoff. Reservations expire in 15s, so pressure clears quickly | | ||
| | `401` | Missing or unrecognized `X-Auth-Token` | No | | ||
| <Tip> | ||
| `404` is a capacity answer, not a validity answer. Fall back to your own path if it persists, but a single retry a few seconds later often succeeds. | ||
| </Tip> | ||
| <RequestExample> | ||
| ```bash cURL | ||
| curl --request GET \ | ||
| --url 'https://api.sprinter.tech/lifi-intents/rfq?srcChain=eip155:8453&dstChain=eip155:42161&amount=100000000&token=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&user=0x1F98431c8aD98523631AE4a59f267346ea31F984' \ | ||
| --header 'X-Auth-Token: YOUR_API_KEY' | ||
| ``` | ||
| ```python Python | ||
| import requests | ||
| response = requests.get( | ||
| "https://api.sprinter.tech/lifi-intents/rfq", | ||
| params={ | ||
| "srcChain": "eip155:8453", | ||
| "dstChain": "eip155:42161", | ||
| "amount": "100000000", | ||
| "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", | ||
| "user": "0x1F98431c8aD98523631AE4a59f267346ea31F984", | ||
| }, | ||
| headers={"X-Auth-Token": "YOUR_API_KEY"}, | ||
| ) | ||
| quote = response.json()["quotes"][0] | ||
| ``` | ||
| ```javascript JavaScript | ||
| const params = new URLSearchParams({ | ||
| srcChain: "eip155:8453", | ||
| dstChain: "eip155:42161", | ||
| amount: "100000000", | ||
| token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", | ||
| user: "0x1F98431c8aD98523631AE4a59f267346ea31F984", | ||
| }); | ||
| const response = await fetch( | ||
| `https://api.sprinter.tech/lifi-intents/rfq?${params}`, | ||
| { headers: { "X-Auth-Token": "YOUR_API_KEY" } } | ||
| ); | ||
| const { quotes } = await response.json(); | ||
| const quote = quotes[0]; | ||
| ``` | ||
| ```go Go | ||
| package main | ||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| ) | ||
| func main() { | ||
| url := "https://api.sprinter.tech/lifi-intents/rfq" + | ||
| "?srcChain=eip155:8453&dstChain=eip155:42161&amount=100000000" + | ||
| "&token=0xaf88d065e77c8cC2239327C5EDb3A432268e5831" + | ||
| "&user=0x1F98431c8aD98523631AE4a59f267346ea31F984" | ||
| req, _ := http.NewRequest("GET", url, nil) | ||
| req.Header.Set("X-Auth-Token", "YOUR_API_KEY") | ||
| resp, _ := http.DefaultClient.Do(req) | ||
| defer resp.Body.Close() | ||
| body, _ := io.ReadAll(resp.Body) | ||
| fmt.Println(string(body)) | ||
| } | ||
| ``` | ||
| </RequestExample> | ||
| <ResponseExample> | ||
| ```json 200 | ||
| { | ||
| "quotes": [ | ||
| { | ||
| "validUntil": 1754481615, | ||
| "eta": 45, | ||
| "quoteId": "3f8a1c72-95e4-4d6b-b0a1-2c7e9f4d8a13", | ||
| "provider": "sprinter", | ||
| "preview": { | ||
| "inputs": [ | ||
| { | ||
| "user": "0x00010000022105141f98431c8ad98523631ae4a59f267346ea31f984", | ||
| "asset": "0x0001000002210514833589fcd6edb6e08f4c7c32d4f71b54bda02913", | ||
| "amount": "100000000" | ||
| } | ||
| ], | ||
| "outputs": [ | ||
| { | ||
| "receiver": "0x0001000002a4b1141f98431c8ad98523631ae4a59f267346ea31f984", | ||
| "asset": "0x0001000002a4b114af88d065e77c8cc2239327c5edb3a432268e5831", | ||
| "amount": "99850000" | ||
| } | ||
| ] | ||
| }, | ||
| "metadata": { | ||
| "exclusiveFor": "0x4c4A2f8c81640e47606d3fd77B353E87Ba015584" | ||
| }, | ||
| "failureHandling": "refund-automatic" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| ```json 404 | ||
| { | ||
| "error": "no available pools for request" | ||
| } | ||
| ``` | ||
| </ResponseExample> | ||
| ## Address format | ||
| `user`, `receiver` and `asset` inside `preview` are [ERC-7930](https://eips.ethereum.org/EIPS/eip-7930) interoperable addresses — chain id and account packed into one byte string: | ||
| ``` | ||
| version(2) | chainType(2) | chainRefLen(1) | chainRef(n) | addrLen(1) | addr(20) | ||
| ``` | ||
| `0x0001000002210514833589fc...2913` decodes to USDC on Base (`0x2105` = 8453). | ||
| `metadata.exclusiveFor` is a plain 20-byte hex address, not ERC-7930. | ||
| ## Next step | ||
| Pass the quote object straight to [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) to get the transaction that opens the order. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| --- | ||
| title: "Build the Escrow Open Transaction" | ||
| sidebarTitle: "Transaction" | ||
| openapi: post /lifi-intents/transaction | ||
| --- | ||
| Turns a quote into an unsigned `open` call on the LI.FI input settler escrow. POST the quote object you received from [`GET /lifi-intents/rfq`](/api-reference/sprinter/lifi-intents/rfq); you get the same object back with `transactionRequest` populated. | ||
| Sending that transaction escrows the inputs on the origin chain and broadcasts the order to solvers. | ||
| <Warning> | ||
| Include the `quoteId` from the RFQ response. Without it the endpoint still returns valid calldata, but **no liquidity is reserved** and the order is not guaranteed a Sprinter fill — you get no error saying so. | ||
| </Warning> | ||
| ## What the endpoint fills in | ||
| You supply `preview`; everything else is derived: | ||
| | Field | Derived as | | ||
| |---|---| | ||
| | `nonce` | Generated per request | | ||
| | `fillDeadline` | ~6 minutes out — 1 minute exclusive to Sprinter, then 5 minutes open to any solver | | ||
| | `expires` | `fillDeadline` + a settlement buffer: ~13 minutes for same-chain orders, ~12 hours for cross-chain | | ||
| | `inputOracle` / output oracle | From Sprinter's configuration | | ||
| | Exclusivity | Encoded per output; defaults to Sprinter's filler address | | ||
| | `value` | Sum of native-token inputs, hex encoded. `0x0` for ERC-20-only orders | | ||
| You can override `nonce`, `expires`, `fillDeadline` and `inputOracle` by setting them in the `order` object on the request, and override the exclusive filler with `metadata.exclusiveFor`. Leave them unset unless you have a specific reason — the defaults are what the reservation is priced against. | ||
| ## Constraints | ||
| - Every entry in `preview.inputs` must be on the same origin chain. Mixed origins are rejected with `400`. | ||
| - The returned transaction must be sent by `transactionRequest.from` — the payer named in the first input. | ||
| - Send it promptly. Converting the quote extends the reservation by one minute; after that the liquidity is released. | ||
| <RequestExample> | ||
| ```bash cURL | ||
| curl --request POST \ | ||
| --url 'https://api.sprinter.tech/lifi-intents/transaction' \ | ||
| --header 'Content-Type: application/json' \ | ||
| --data @quote.json | ||
| ``` | ||
| ```python Python | ||
| import requests | ||
| # `quote` is quotes[0] from the RFQ response, passed through unchanged | ||
| response = requests.post( | ||
| "https://api.sprinter.tech/lifi-intents/transaction", | ||
| json=quote, | ||
| ) | ||
| tx = response.json()["transactionRequest"] | ||
| ``` | ||
| ```javascript JavaScript | ||
| // `quote` is quotes[0] from the RFQ response, passed through unchanged | ||
| const response = await fetch( | ||
| "https://api.sprinter.tech/lifi-intents/transaction", | ||
| { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify(quote), | ||
| } | ||
| ); | ||
| const { transactionRequest } = await response.json(); | ||
| // send it from the user's wallet | ||
| const hash = await walletClient.sendTransaction({ | ||
| to: transactionRequest.to, | ||
| data: transactionRequest.data, | ||
| value: BigInt(transactionRequest.value), | ||
| chainId: transactionRequest.chainId, | ||
| }); | ||
| ``` | ||
| ```go Go | ||
| package main | ||
| import ( | ||
| "bytes" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| ) | ||
| func main() { | ||
| // quoteJSON is quotes[0] from the RFQ response, passed through unchanged | ||
| resp, _ := http.Post( | ||
| "https://api.sprinter.tech/lifi-intents/transaction", | ||
| "application/json", | ||
| bytes.NewReader(quoteJSON), | ||
| ) | ||
| defer resp.Body.Close() | ||
| body, _ := io.ReadAll(resp.Body) | ||
| fmt.Println(string(body)) | ||
| } | ||
| ``` | ||
| </RequestExample> | ||
| <ResponseExample> | ||
| ```json 200 | ||
| { | ||
| "validUntil": 1754481615, | ||
| "eta": 45, | ||
| "quoteId": "3f8a1c72-95e4-4d6b-b0a1-2c7e9f4d8a13", | ||
| "provider": "sprinter", | ||
| "preview": { | ||
| "inputs": [ | ||
| { | ||
| "user": "0x00010000022105141f98431c8ad98523631ae4a59f267346ea31f984", | ||
| "asset": "0x0001000002210514833589fcd6edb6e08f4c7c32d4f71b54bda02913", | ||
| "amount": "100000000" | ||
| } | ||
| ], | ||
| "outputs": [ | ||
| { | ||
| "receiver": "0x0001000002a4b1141f98431c8ad98523631ae4a59f267346ea31f984", | ||
| "asset": "0x0001000002a4b114af88d065e77c8cc2239327c5edb3a432268e5831", | ||
| "amount": "99850000" | ||
| } | ||
| ] | ||
| }, | ||
| "metadata": { | ||
| "exclusiveFor": "0x4c4A2f8c81640e47606d3fd77B353E87Ba015584" | ||
| }, | ||
| "failureHandling": "refund-automatic", | ||
| "transactionRequest": { | ||
| "from": "0x1F98431c8aD98523631AE4a59f267346ea31F984", | ||
| "to": "0x6E9a1b3F0c5D2a8B4e7C1f9A3d6B0e5C8f2A4d71", | ||
| "chainId": 8453, | ||
| "data": "0xff2b0c2b0000000000000000000000000000000000000000000000000000000000000020", | ||
| "value": "0x0" | ||
| } | ||
| } | ||
| ``` | ||
| </ResponseExample> | ||
| ## After the fill | ||
| Sprinter fills on the destination chain within the exclusivity window and is repaid when the escrow settles. If nobody fills before `fillDeadline`, the order is reclaimable on the escrow contract — `failureHandling` is `refund-automatic`, so nothing is stranded. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the /rfq endpoint ( quote) , we should mention here, visible the reservation time. It is 60 seconds. For an existing rfq quoteId the new reservation will be refreshed to 60 seconds from NOW when using in /transaction or directly 60 seconds from now if not used with a quoteId. Also keep in mind that this behavior is shared among pool with other solvers ... so if someone plays with /transaction for testing purpose it can reserve a lot of liquidity for 60 seconds