Skip to content

Instantly share code, notes, and snippets.

@NotoriousPyro
Last active May 28, 2024 17:21
Show Gist options
  • Save NotoriousPyro/fca4c7ab8fe4bf740785f8869ad534dd to your computer and use it in GitHub Desktop.
Save NotoriousPyro/fca4c7ab8fe4bf740785f8869ad534dd to your computer and use it in GitHub Desktop.
Quicknode /markets implementation (for adding new markets)
// define a couple of fetchers, one for /quote and one for everything else
// if the endpoint url is a QN endpoint, use the custom fetcher that appends the param useQNMarketCache=true
export const quoteFetcher = createJupiterApiClient({
basePath: config.jupiter.endpoint,
fetchApi: config.jupiter.endpoint.search(/https:\/\/jupiter-swap-api.quiknode.pro\/[A-Z0-9]*/) !== -1
? QuicknodeQuoteFetcherWithCustomAgent(agent)
: FetcherWithCustomAgent(agent),
})
export const swapInstructionFetcher = createJupiterApiClient({
basePath: config.jupiter.endpoint,
fetchApi: FetcherWithCustomAgent(agent),
})
// define some customised fetchers (the last one is most important).
import * as nodeFetch from 'node-fetch';
import fetch from 'fetch-retry'
import https from 'https';
const _fetch = fetch(nodeFetch.default);
type FetchFn = typeof nodeFetch.default;
export type Fetcher = (...args: Parameters<FetchFn>) => ReturnType<FetchFn>;
export const RetryFetcher: Fetcher = (...args) => {
const [url, init] = args;
return _fetch(url, {
...init,
retries: 3,
retryDelay: 1000,
retryOn: function (attempt, error, response) {
if (attempt > 3) return false;
if (response?.status === 429) {
return false;
}
if (error) {
return true;
}
return false;
}
});
}
export type FetcherWithCustomAgent = (agent: https.Agent) => Fetcher;
export const FetcherWithCustomAgent: FetcherWithCustomAgent = (agent: https.Agent) => {
const fetcher: Fetcher = (...args) => {
const [url, init] = args;
return RetryFetcher(url, {
...init,
agent
})
}
return fetcher;
}
/**
* Workaround for quicknode market cache. Appends useQNMarketCache=true to the url
* @param agent https.Agent
* @returns a fetcher with custom agent and url modified to append useQNMarketCache=true to the url
*/
export const QuicknodeQuoteFetcherWithCustomAgent: FetcherWithCustomAgent = (agent: https.Agent) => {
const fetcher: Fetcher = (...args) => {
const [url, init] = args;
return RetryFetcher(`${url}&useQNMarketCache=true`, {
...init,
agent
})
}
return fetcher;
}
// outside the function, something to hold all the created markets so we don't keep trying within a short window:
const createdMarkets = new Set<string>();
// get your quotes and catch any exceptions:
try {
quoteFetcher.quoteGet(...);
} catch (e) {
if (e instanceof ResponseError) {
const response: Response = e.response
const respText = await response.text();
try {
const parsed = JSON.parse(respText);
switch (parsed?.error) {
case "Could not find any route":
logger.error(e.message, token, parsed.error);
return;
}
switch (parsed?.errorCode) {
case "MARKET_NOT_FOUND": {
const market = (parsed.error as string).match(/Market (.*) not found/)?.[1];
if (!createdMarkets.has(market)) {
RetryFetcher(`${config.jupiter.endpoint}/markets`, {
method: "POST",
body: JSON.stringify({
"poolAddress": market
})
}).then(() => createdMarkets.add(market));
}
return;
}
}
logger.error(e.message, token, parsed);
return;
}
catch (e) {
logger.error("failed to parse response error", e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment