GetSwapInstructions
curl --request GET \
--url https://api.hular.dev/json/v1/quotes/{quote_hash}/instructionsimport requests
url = "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"routerAddress": "<string>",
"srcToken": "<string>",
"amountIn": "<string>",
"lockToken": "<string>",
"minLockAmount": "<string>",
"evm": {
"aggregator": "<string>",
"swapCalldata": "<string>",
"value": "<string>"
},
"svm": {
"setup": [
{
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
}
],
"swap": {
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
},
"cleanup": [
{
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
}
],
"lookupTables": [
{
"address": "<string>",
"addresses": [
"<string>"
]
}
],
"swapAuthorityLamports": "<string>"
},
"sui": {
"fragment": "<string>",
"coinOutCommand": 1,
"coinOutResult": 1
}
}{
"code": 123,
"message": "<string>",
"details": [
{}
]
}Reference
GetSwapInstructions
Aggregator routing for a quote whose deposit swaps on the source chain.
GET
/
v1
/
quotes
/
{quote_hash}
/
instructions
GetSwapInstructions
curl --request GET \
--url https://api.hular.dev/json/v1/quotes/{quote_hash}/instructionsimport requests
url = "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hular.dev/json/v1/quotes/{quote_hash}/instructions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"routerAddress": "<string>",
"srcToken": "<string>",
"amountIn": "<string>",
"lockToken": "<string>",
"minLockAmount": "<string>",
"evm": {
"aggregator": "<string>",
"swapCalldata": "<string>",
"value": "<string>"
},
"svm": {
"setup": [
{
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
}
],
"swap": {
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
},
"cleanup": [
{
"programId": "<string>",
"accounts": [
{
"pubkey": "<string>",
"isSigner": true,
"isWritable": true
}
],
"data": "<string>"
}
],
"lookupTables": [
{
"address": "<string>",
"addresses": [
"<string>"
]
}
],
"swapAuthorityLamports": "<string>"
},
"sui": {
"fragment": "<string>",
"coinOutCommand": 1,
"coinOutResult": 1
}
}{
"code": 123,
"message": "<string>",
"details": [
{}
]
}rpc GetSwapInstructions(SwapInstructionsRequest) returns (SwapInstructions)
order_params.src_token differs from order_params.lock_token. Call it immediately before sending the deposit; routing reflects pool state at build time.
Sui quotes have no instructions: the user opens a swap intent and the relayer builds and runs the route itself. See Deposit on Sui.
Request
string
required
Hash returned by
GetQuote.Response
string
Contract the deposit call goes to: the
BlinkRouter on EVM chains, the program id on Solana.string
Input token.
string
Amount the depositor sends, base units: the quote’s
expected_amount_in. For a Token-2022 source with a transfer fee, the route swaps this amount less the fee.string
Output token of the swap. For a cross-chain quote this is the asset that bridges; for a same-chain quote it is the destination token.
string
Floor the swap output is checked against on chain.
payload is a oneof matching the source chain’s VM.
EVM: evm
string
Aggregator the router will call. Allowlisted on the contract.
string
0x hex calldata to pass through.string
Transaction value to attach. Non-zero only when the source token is the chain’s native asset.
Solana: svm
SvmInstruction[]
Instructions to run before the swap. These only create the swap authority’s associated token accounts, with the depositor paying the rent.
SvmInstruction
The aggregator swap instruction. The deposit wraps it in the program’s
deposit_swap instruction, which runs the swap and checks the output against min_lock_amount.SvmInstruction[]
Instructions to run after the swap. Empty for Solana source swaps; the depositor’s transaction closes the swap authority’s token accounts itself.
SvmLookupTable[]
Address lookup tables to attach to the versioned transaction.
string
Lamports the depositor transfers to the swap authority before
deposit_swap, for venues on the route that pay rent from the swap authority. The API measures it by simulating the deposit, up to the chain’s configured limit. deposit_swap returns whatever the route did not spend in the same instruction. Zero means no transfer. The SDK refuses more than 4,000,000 lamports (0.004 SOL).SvmInstruction carries program_id, accounts (pubkey, is_signer, is_writable), and base64 data.
On Solana the route is built for the swap authority, a program-derived address with seeds swap_authority, the depositor, and the quote hash. The depositor is the quote’s depositor, or its refund_address when the quote request named none; a deposit signed by any other wallet derives a different swap authority and cannot execute the route. The depositor never signs anything the aggregator built: the transaction moves exactly amount_in from the depositor into the swap authority’s token account and swap_authority_lamports into the swap authority, the program signs the swap with the authority’s seeds, returns the authority’s unspent lamports, and closes the authority’s token accounts back to the depositor. The program rejects a deposit_swap whose swap is handed the depositor’s key, so a route cannot reach the depositor’s other balances. See Deposit on Solana for the full layout.
Using it
On EVM, map the fields straight onto the router call:const swap = await blink.getSwapInstructions({ quoteHash });
if (swap.payload.case !== "evm") throw new Error("expected evm instructions");
const evm = swap.payload.value;
await wallet.sendTransaction({
to: swap.routerAddress,
value: BigInt(evm.value),
data: encodeFunctionData({
abi: routerAbi,
functionName: "depositWithSwap",
args: [
quoteHash,
swap.srcToken,
BigInt(swap.amountIn),
evm.aggregator,
evm.swapCalldata,
swap.lockToken,
BigInt(swap.minLockAmount),
],
}),
});
buildSvmSwapDeposit in the SDK, which checks the payload and assembles the versioned transaction; the depositor is its only signer. Same-chain quotes on EVM use the same instructions with swapAndForward instead; see Same-chain swaps.
Errors
| Code | Message | When |
|---|---|---|
INVALID_ARGUMENT | quote is not a source swap | The quote’s source token is already the bridge token. No instructions exist. |
INVALID_ARGUMENT | invalid quote hash | Not a 32-byte hex hash. |
INVALID_ARGUMENT | a sui source swap is opened with open_source_swap and executed by the relayer | The quote’s source chain is Sui. Open a swap intent instead. |
NOT_FOUND | quote not found or expired | No stored quote with that hash. |
UNAVAILABLE | aggregator error | The aggregator failed or returned no route. |
UNAVAILABLE | source route output below committed minimum | The live route no longer clears the floor committed in the quote. Re-quote. |
Headers
Optional. Without a key the request is rate limited per client IP and quotes carry extra spread.