Solana
DFlow supports token swaps and indicative quotes on Solana. Backup liquidity is available from Jupiter Aggregator.
You must create auctions for each token pair you want to support. Additionally, your auctions for a token pair must cover all order sizes that you want to support for that token pair. See Create Auction to learn how to create an auction.
Token Swaps
Step 1: Request an Endorsement
First, request an endorsement from your endorsement server.
- TypeScript
// Use your endorsement server's URL
const endorsementServerURL = "https://endorsements.dflow.net";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const endorsementURL = endorsementServerURL + "?retailTrader=" + walletAddress;
const endorsement = await (await fetch(endorsementURL)).json();
Step 2: Request a Firm Quote
Once your request is endorsed, you can request a firm quote. A firm quote includes a Solana transaction that contains the instructions needed to perform the token swap.
- TypeScript SDK
- TypeScript
import { FirmQuoteResponseCode, getFirmQuote } from "@dflow-protocol/signatory-client-lib/solana";
const signatoryServerURL = "https://signatory.dflow.net";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const firmQuote = await getFirmQuote(SIGNATORY_SERVER_URL, {
sendMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
receiveMint: "So11111111111111111111111111111111111111112",
sendQty: "5",
useNativeSOL: true,
orderFlowSource: "dflow1ejsafz9xy3ymkp6fmw8t299ag0wfl7drydj68n",
endorsement: endorsement,
retailTrader: walletAddress,
});
if (firmQuote.code !== FirmQuoteResponseCode.Ok) {
// Quote request failed.
}
const signatoryServerURL = "https://signatory.dflow.net";
const firmQuoteEndpoint = signatoryServerURL + "/api/solana/firmQuote";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const requestBody = JSON.stringify({
sendMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
receiveMint: "So11111111111111111111111111111111111111112",
sendQty: "5",
useNativeSOL: true,
orderFlowSource: "dflow1ejsafz9xy3ymkp6fmw8t299ag0wfl7drydj68n",
endorsement: endorsement,
retailTrader: walletAddress,
});
const response = await fetch(firmQuoteEndpoint, {
method: "POST",
headers: {
"content-type": "application/json",
"content-length": Buffer.byteLength(requestBody).toString(),
},
body: requestBody,
});
const firmQuote = await response.json();
if (firmQuote.code !== 0) {
// Quote request failed.
}
This is the response:
{
"code": 0,
"tx": "AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACql2AsCC3sGxwonmDS+MiTggf0o9TAKt3l1Nmg6TrMEvXBQHEKjJErsbRvhQQGn+KtUYpNS+bbHlW1UXih2XoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMABAoLd1EiR7xIwXGcMLfFgMqwV59vInB9tFI5Qd+4gTdzFNnUY5fiBgcuPOZhS+hFYyxUxq9CJuYqhGH6yVnMCivXDMdnyrUb5RQt+Y/aWhp3zirvTcT08a/bHrQr1WR1w/KFtsmZm5tVD330elTNoVK+tCz586Ulkg5JwgORt3s0rpJhKUqQTo8Zxi70fvC/bVrZzD+J1z0E2ZEu1PtfaSw2ta/ELj1MYBPRSkaHAoIuDExEQSy11AXVdbUoloxagD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAabiFf+q4GE+2h/Y0YYwDXaxDncGus7VZig8AAAAAABBqfVFxksXFEhjMlMPUrxf1ja7gibof1E49vZigAAAAAG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbmVE5/Jadms/F1fK99HET/4gMnV4tGlDHxokNB/dSq/BQYCAgE0AAAAAPAdHwAAAAAApQAAAAAAAAAG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqQkEAQcCCAEBCQMFAwIJA0BLTAAAAAAACQMEAQAJAwQfORUAAAAACQMBAgIBCQ==",
"txRequiredLamports": 2039280,
"lastValidBlockHeight": 7842,
"lastAllowedBlockHeight": 7817,
"receiveQty": "356065028", // 0.356065028 SOL
"quoteId": ...
"requestId": ...
}
Firm Quote Request Fields
Field | Type | Description |
---|---|---|
sendMint | string | The Base58-encoded SPL mint that the retail trader will send. |
receiveMint | string | The Base58-encoded SPL mint that the retail trader will receive. |
sendQty | string | The quantity of sendMint that the retail trader will send. |
useNativeSOL | boolean | Use native SOL instead of wrapped SOL when buying or selling SOL. Default is true . |
orderFlowSource | string | The order flow source's DFlow address. This is the address on the DFlow chain of the wallet that you use to create your auctions. |
endorsement | object | The endorsement received from the endorsement server. |
retailTrader | string | Base58-encoded address of the retail trader's Solana wallet. |
Firm Quote Response Fields
The response for a successful request contains the following fields:
Field | Type | Description |
---|---|---|
code | integer | The status code for the quote request. 0 indicates a successful request. |
receiveQty | string | The quantity of receiveMint that the retail trader will receive. Note that the receiveQty is specified with the fixed number of decimals used for the receive mint. For example, if the receiveMint is USDC, which has 6 decimals, and the receiveQty is 1500000, then the retail trader will receive 1.5 USDC. |
tx | string | The Base64-encoded swap transaction. If needed, the transaction will include an instructions to create the retail trader's associated token account for the receiveMint . |
txRequiredLamports | integer | The number of lamports required from the retail trader to execute the transaction. This number accounts for the rent required to create the retail trader's associated token account for the receiveMint , the rent required to create an ephemeral token account used to wrap or unwrap native SOL, and the Solana network fee. Note that rent for an ephemeral token account used to wrap or unwrap native SOL is returned to the retail trader at the end of the transaction. |
lastValidBlockHeight | integer | The last valid Solana block height at which the swap transaction may be processed on Solana. You can use this when checking for transaction confirmation using the Solana JSON RPC API. |
lastAllowedBlockHeight | integer | The last Solana block height at which the swap transaction may be sent to the DFlow node. If the transaction is sent to the DFlow node after this Solana block height, the DFlow node rejects the transaction, and you must request another quote. |
blockHeightQueryCommitment | string | The commitment level that the DFlow node will use when querying the Solana block height to determine whether the lastAllowedBlockHeight has passed. You should use this commitment level when querying and comparing the current Solana block height to the lastAllowedBlockHeight . |
quoteId | string | An identifier used by the market maker to identify its quotes. Must be included in the sendTransaction request. |
requestId | object | Request information used by the DFlow node when processing a sendTransaction request. |
Step 3: Send the Swap Transaction
Now that you have a swap transaction, you can sign it and then execute it by sending it via the DFlow network.
- TypeScript SDK
- TypeScript
import { sendTransaction, SendTransactionResponseCode } from "@dflow-protocol/signatory-client-lib/solana";
import { Transaction } from "@solana/web3.js";
const signatoryServerURL = "https://signatory.dflow.net";
const txBuffer = Buffer.from(firmQuote.tx, "base64");
const tx = Transaction.from(txBuffer);
const signedTx = await wallet.signTransaction(tx);
const serializedTx = signedTx.serialize({ requireAllSignatures: false }).toString("base64");
const result = await sendTransaction(SIGNATORY_SERVER_URL, {
tx: serializedTx,
lastValidBlockHeight: firmQuote.lastValidBlockHeight,
lastAllowedBlockHeight: firmQuote.lastAllowedBlockHeight,
endorsement: endorsement,
quoteId: firmQuote.quoteId,
requestId: firmQuote.requestId,
});
if (result.code !== SendTransactionResponseCode.Sent) {
// Transaction wasn't sent. Request another quote and retry.
}
import { Transaction } from "@solana/web3.js";
const signatoryServerURL = "https://signatory.dflow.net";
const sendTransactionEndpoint = signatoryServerURL + "/api/solana/sendTransaction";
const txBuffer = Buffer.from(firmQuote.tx, "base64");
const tx = Transaction.from(txBuffer);
const signedTx = await wallet.signTransaction(tx);
const serializedTx = signedTx.serialize({ requireAllSignatures: false }).toString("base64");
const requestBody = JSON.stringify({
tx: serializedTx,
lastValidBlockHeight: firmQuote.lastValidBlockHeight,
lastAllowedBlockHeight: firmQuote.lastAllowedBlockHeight,
endorsement: endorsement,
quoteId: firmQuote.quoteId,
requestId: firmQuote.requestId,
});
const response = await fetch(sendTransactionEndpoint, {
method: "POST",
headers: {
"content-type": "application/json",
"content-length": Buffer.byteLength(requestBody).toString(),
},
body: requestBody,
});
const result = await response.json();
if (result.code !== 0) {
// Transaction wasn't sent. Request another quote and retry.
}
This is the response:
{
"signature": "3dqTxisZH5fdKz9g5jQnCvVr7R6oMFCoR9ZUkhrWkE9DGbzLZ8ZkQk8kqd4ztdQRNDmyXjNbgi1h22BtXbvnja73"
}
Send Transaction Request Fields
Field | Type | Description |
---|---|---|
tx | string | The Base64-encoded swap transaction, signed by the retail trader. |
lastValidBlockHeight | integer | Taken from the firmQuote response. |
lastAllowedBlockHeight | integer | Taken from the firmQuote response. |
endorsement | object | The endorsement received from the endorsement server. |
quoteId | string | Taken from the firmQuote response. |
requestId | object | Taken from the firmQuote response. |
Send Transaction Response Fields
If the transaction was sent to Solana, the response will contain the following fields:
Field | Type | Description |
---|---|---|
signature | string | The transaction signature. You can use this to check for transaction confirmation using the Solana JSON RPC API. |
Indicative Quotes
An indicative quote contains pricing information from the market maker, but unlike a firm quote, it cannot be used to swap tokens at a set price. You should use indicative quotes to show pricing information when you don't have a wallet connected or don't intend to send an order.
Step 1: Request an Endorsement
First, request an endorsement from your endorsement server.
Note that if your application allows your users to see indicative prices before connecting their wallets, your endorsements for indicative quotes can be generic and omit the retailTrader
.
- TypeScript
// Use your endorsement server's URL
const endorsementServerURL = "https://endorsements.dflow.net";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const endorsementURL = endorsementServerURL + "?retailTrader=" + walletAddress;
const endorsement = await (await fetch(endorsementURL)).json();
Step 2: Request an Indicative Quote
Once your request is endorsed, you can request an indicative quote.
- TypeScript SDK
- TypeScript
import { getIndicativeQuote } from "@dflow-protocol/signatory-client-lib/solana";
const signatoryServerURL = "https://signatory.dflow.net";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const indicativeQuote = await getIndicativeQuote(SIGNATORY_SERVER_URL, {
sendMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
receiveMint: "So11111111111111111111111111111111111111112",
sendQty: "5",
orderFlowSource: "dflow1ejsafz9xy3ymkp6fmw8t299ag0wfl7drydj68n",
endorsement: endorsement,
retailTrader: walletAddress,
});
const signatoryServerURL = "https://signatory.dflow.net";
const indicativeQuoteEndpoint = signatoryServerURL + "/api/solana/indicativeQuote";
const walletAddress = "rtFMmRrBUKnz6hXm2KfEQBK7GLMq8ziHCt9yZrEMmF7";
const requestBody = JSON.stringify({
sendMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
receiveMint: "So11111111111111111111111111111111111111112",
sendQty: "5",
orderFlowSource: "dflow1ejsafz9xy3ymkp6fmw8t299ag0wfl7drydj68n",
endorsement: endorsement,
retailTrader: walletAddress,
});
const response = await fetch(indicativeQuoteEndpoint, {
method: "POST",
headers: {
"content-type": "application/json",
"content-length": Buffer.byteLength(requestBody).toString(),
},
body: requestBody,
});
const indicativeQuote = await response.json();
This is the response:
{
"fillPrice": "76640000" // 0.076640000 SOL per USDC
}
Indicative Quote Request Fields
Field | Type | Description |
---|---|---|
sendMint | string | The Base58-encoded SPL mint that the retail trader would send. |
receiveMint | string | The Base58-encoded SPL mint that the retail trader would receive. |
sendQty | string | The quantity of sendMint that the retail trader would send. |
orderFlowSource | string | The order flow source's DFlow address. This is the address on the DFlow chain of the wallet that you use to create your auctions. |
endorsement | object | The endorsement received from the endorsement server. |
retailTrader | string (optional) | Base58-encoded address of the retail trader's Solana wallet. Include this field if and only if the endorsement is specific to the retail trader. |
Indicative Quote Response Fields
The response for a successful request contains the following fields:
Field | Type | Description |
---|---|---|
fillPrice | string | The number of token units that the retail trader would receive per token sent. Note that the fillPrice is specified with the fixed number of decimals used for the receive mint. For example, if the receiveMint is USDC, which has 6 decimals, and the fillPrice is 1500000, then the retail trader would receive 1.5 USDC per sendMint token. |