Skip to content

Commit 5e9d0b6

Browse files
Update index.ts
Added in the new transaction retry logic (v1) that simply pauses, fetches a new route list and tries again (top route) up to the max set in the .env variable. Mixing this logic with using existing route #1 and #2 may be a good middle ground. Also added some styling and a few more bonuses here to make it better. Also limited the Jupiter top result to 1 to add some efficiency with the list.
1 parent b490a95 commit 5e9d0b6

File tree

1 file changed

+107
-33
lines changed

1 file changed

+107
-33
lines changed

src/index.ts

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,27 @@ import { Jupiter, TOKEN_LIST_URL, SwapMode } from "@jup-ag/core";
55
import { PublicKey, Connection } from "@solana/web3.js";
66
import * as cron from "node-cron";
77
import cronstrue from "cronstrue";
8-
import {Token, MINT_ADDRESSES, USER_KEYPAIR, SOLANA_RPC_ENDPOINT, WRAP_UNWRAP_SOL} from "./constants";
8+
import {Token, MINT_ADDRESSES, USER_KEYPAIR, SOLANA_RPC_ENDPOINT, WRAP_UNWRAP_SOL, tradingEnabled, tradingRetries} from "./constants";
99
import { dcaconfig } from './dcaconfig'
1010
import JSBI from 'jsbi';
1111

12+
// Simple delay function
13+
function delay(ms: number) {
14+
return new Promise( resolve => setTimeout(resolve, ms) );
15+
}
16+
17+
// Date time logging object
18+
function ptst() {
19+
let timestsmp: String = new Date().toLocaleString();
20+
return timestsmp;
21+
}
22+
23+
// Add colour to console text
24+
function setcolour(ctxt: string, colnum: number) {
25+
return ('\x1b['+colnum+'m'+ctxt+'\x1b[0m');
26+
}
27+
28+
// Jupiter swap code
1229
const jupiterSwap = async ({
1330
jupiter,
1431
inputToken,
@@ -47,45 +64,95 @@ const jupiterSwap = async ({
4764
: null;
4865

4966
if (tradingEnabled){
50-
if (routes && routes.routesInfos) {
51-
// Prepare execute exchange
52-
const { execute } = await jupiter.exchange({
53-
routeInfo: routes!.routesInfos[0],
54-
});
55-
// Execute swap
56-
// Force any to ignore TS misidentifying SwapResult type
57-
const swapResult: any = await execute();
58-
if (swapResult.error) {
59-
console.log(swapResult.error);
60-
} else {
61-
// trying to keep these on one line
62-
process.stdout.write(
63-
`${swapResult.inputAmount / (10 ** inputToken.decimals)} `
64-
);
65-
process.stdout.write(`${inputToken.symbol} -> `);
66-
process.stdout.write(
67-
`${swapResult.outputAmount / (10 ** outputToken.decimals)} `
68-
);
69-
process.stdout.write(`${outputToken.symbol}: `);
70-
console.log(`https://solscan.io/tx/${swapResult.txid}`);
71-
}
72-
} else {
73-
console.log("Error during jupiter.computeRoutes().");
74-
}
67+
68+
// handle transaction retries
69+
let i: number = 0;
70+
71+
do {
72+
process.stdout.write( await ptst() + " - recurring DCA Swap Attempt #" + (i+1) )
73+
i++;
74+
75+
try {
76+
77+
const routes = inputToken && outputToken
78+
? await jupiter.computeRoutes({
79+
inputMint: new PublicKey(inputToken.address),
80+
outputMint: new PublicKey(outputToken.address),
81+
amount: inputAmountInSmallestUnits,
82+
slippageBps: slippage,
83+
feeBps: 0,
84+
forceFetch: true,
85+
onlyDirectRoutes: false,
86+
filterTopNResult: 1,
87+
enforceSingleTx: false,
88+
swapMode: SwapMode.ExactIn,
89+
})
90+
: null;
91+
92+
if (routes && routes.routesInfos) {
93+
94+
console.log(" - " + routes.routesInfos.length + ' routes found');
95+
96+
const { execute } = await jupiter.exchange({
97+
routeInfo: routes!.routesInfos[0],
98+
});
99+
// Execute swap
100+
// Force any to ignore TS misidentifying SwapResult type
101+
const swapResult: any = await execute();
102+
103+
if (swapResult.error) {
104+
//console.log(swapResult.error);
105+
let swaperr = String(swapResult.error);
106+
let simpleerror = setcolour(swaperr.split('\n',1)[0],33);
107+
console.log(await ptst() + " - " + simpleerror);
108+
} else {
109+
// trying to keep these on one line
110+
process.stdout.write(await ptst() + " - ");
111+
112+
process.stdout.write(
113+
setcolour(`${swapResult.inputAmount / (10 ** inputToken.decimals)} `,32)
114+
);
115+
process.stdout.write(`${inputToken.symbol} -> `);
116+
process.stdout.write(
117+
setcolour(`${swapResult.outputAmount / (10 ** outputToken.decimals)} `,32)
118+
);
119+
process.stdout.write(`${outputToken.symbol}: `);
120+
console.log(`https://solscan.io/tx/${swapResult.txid}`);
121+
break; // exit retry loop
122+
}
123+
124+
} else {
125+
console.log(await ptst() + " - Error during jupiter.computeRoutes().");
126+
}
127+
128+
} catch (error) {
129+
console.log('Failure in route loop lookup.');
130+
throw error;
131+
}
132+
133+
await delay(5000); // wait for 5 second between attempts
134+
135+
} while ( i< tradingRetries)
136+
137+
138+
75139
} else {
76140
console.log("Trading not enabled. You need to enable it in the .env for swaps to take place.");
77141
}
78142

79-
80-
} catch (error) {
143+
144+
} catch (error) {
145+
console.log('Throw error check on tokens');
81146
throw error;
82147
}
83-
};
84-
148+
};
149+
150+
85151
const main = async () => {
86152
try {
87-
console.log("Starting Jupiter DCA Bot");
88-
153+
console.log(setcolour("Starting Jupiter V3 DCA Bot",92));
154+
console.log("The bot will retry "+String(tradingRetries)+" times if the swap fails for each scheduled period.");
155+
89156
const cluster = "mainnet-beta"; // Force mainnet, as this uses Jupiter which is not deployed on devnet/testnet
90157
const connection = new Connection(SOLANA_RPC_ENDPOINT);
91158
const jupiter = await Jupiter.load({
@@ -127,6 +194,10 @@ const main = async () => {
127194
console.log("- invalid cron expression");
128195
console.log("- inputToken or outputToken does not exist in MINT_ADDRESSES");
129196
console.log("Validating dcaconfig.ts ...");
197+
198+
// separator
199+
console.log('-----------------------------');
200+
130201
const filteredJobs = dcaconfig.filter(job => {
131202
return (cron.validate(job.cron)
132203
&& job.inputToken in MINT_ADDRESSES
@@ -136,8 +207,11 @@ const main = async () => {
136207

137208
console.log("Scheduling swaps:");
138209
filteredJobs.map(job => {
139-
console.log(`${job.amount} ${job.inputToken} for ${job.outputToken} ${cronstrue.toString(job.cron)}`);
210+
console.log(setcolour(String(job.amount),32) + ` ${job.inputToken} for ${job.outputToken} ${cronstrue.toString(job.cron)}`);
140211
});
212+
213+
// separator
214+
console.log('-----------------------------');
141215

142216
filteredJobs.forEach(job => {
143217
const inputToken = tokens.find((t) =>

0 commit comments

Comments
 (0)