Skip to content

Commit

Permalink
Update index.ts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cryptopavelsan authored Dec 12, 2022
1 parent b490a95 commit 5e9d0b6
Showing 1 changed file with 107 additions and 33 deletions.
140 changes: 107 additions & 33 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,27 @@ import { Jupiter, TOKEN_LIST_URL, SwapMode } from "@jup-ag/core";
import { PublicKey, Connection } from "@solana/web3.js";
import * as cron from "node-cron";
import cronstrue from "cronstrue";
import {Token, MINT_ADDRESSES, USER_KEYPAIR, SOLANA_RPC_ENDPOINT, WRAP_UNWRAP_SOL} from "./constants";
import {Token, MINT_ADDRESSES, USER_KEYPAIR, SOLANA_RPC_ENDPOINT, WRAP_UNWRAP_SOL, tradingEnabled, tradingRetries} from "./constants";
import { dcaconfig } from './dcaconfig'
import JSBI from 'jsbi';

// Simple delay function
function delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}

// Date time logging object
function ptst() {
let timestsmp: String = new Date().toLocaleString();
return timestsmp;
}

// Add colour to console text
function setcolour(ctxt: string, colnum: number) {
return ('\x1b['+colnum+'m'+ctxt+'\x1b[0m');
}

// Jupiter swap code
const jupiterSwap = async ({
jupiter,
inputToken,
Expand Down Expand Up @@ -47,45 +64,95 @@ const jupiterSwap = async ({
: null;

if (tradingEnabled){
if (routes && routes.routesInfos) {
// Prepare execute exchange
const { execute } = await jupiter.exchange({
routeInfo: routes!.routesInfos[0],
});
// Execute swap
// Force any to ignore TS misidentifying SwapResult type
const swapResult: any = await execute();
if (swapResult.error) {
console.log(swapResult.error);
} else {
// trying to keep these on one line
process.stdout.write(
`${swapResult.inputAmount / (10 ** inputToken.decimals)} `
);
process.stdout.write(`${inputToken.symbol} -> `);
process.stdout.write(
`${swapResult.outputAmount / (10 ** outputToken.decimals)} `
);
process.stdout.write(`${outputToken.symbol}: `);
console.log(`https://solscan.io/tx/${swapResult.txid}`);
}
} else {
console.log("Error during jupiter.computeRoutes().");
}

// handle transaction retries
let i: number = 0;

do {
process.stdout.write( await ptst() + " - recurring DCA Swap Attempt #" + (i+1) )
i++;

try {

const routes = inputToken && outputToken
? await jupiter.computeRoutes({
inputMint: new PublicKey(inputToken.address),
outputMint: new PublicKey(outputToken.address),
amount: inputAmountInSmallestUnits,
slippageBps: slippage,
feeBps: 0,
forceFetch: true,
onlyDirectRoutes: false,
filterTopNResult: 1,
enforceSingleTx: false,
swapMode: SwapMode.ExactIn,
})
: null;

if (routes && routes.routesInfos) {

console.log(" - " + routes.routesInfos.length + ' routes found');

const { execute } = await jupiter.exchange({
routeInfo: routes!.routesInfos[0],
});
// Execute swap
// Force any to ignore TS misidentifying SwapResult type
const swapResult: any = await execute();

if (swapResult.error) {
//console.log(swapResult.error);
let swaperr = String(swapResult.error);
let simpleerror = setcolour(swaperr.split('\n',1)[0],33);
console.log(await ptst() + " - " + simpleerror);
} else {
// trying to keep these on one line
process.stdout.write(await ptst() + " - ");

process.stdout.write(
setcolour(`${swapResult.inputAmount / (10 ** inputToken.decimals)} `,32)
);
process.stdout.write(`${inputToken.symbol} -> `);
process.stdout.write(
setcolour(`${swapResult.outputAmount / (10 ** outputToken.decimals)} `,32)
);
process.stdout.write(`${outputToken.symbol}: `);
console.log(`https://solscan.io/tx/${swapResult.txid}`);
break; // exit retry loop
}

} else {
console.log(await ptst() + " - Error during jupiter.computeRoutes().");
}

} catch (error) {
console.log('Failure in route loop lookup.');
throw error;
}

await delay(5000); // wait for 5 second between attempts

} while ( i< tradingRetries)



} else {
console.log("Trading not enabled. You need to enable it in the .env for swaps to take place.");
}


} catch (error) {

} catch (error) {
console.log('Throw error check on tokens');
throw error;
}
};

};


const main = async () => {
try {
console.log("Starting Jupiter DCA Bot");

console.log(setcolour("Starting Jupiter V3 DCA Bot",92));
console.log("The bot will retry "+String(tradingRetries)+" times if the swap fails for each scheduled period.");

const cluster = "mainnet-beta"; // Force mainnet, as this uses Jupiter which is not deployed on devnet/testnet
const connection = new Connection(SOLANA_RPC_ENDPOINT);
const jupiter = await Jupiter.load({
Expand Down Expand Up @@ -127,6 +194,10 @@ const main = async () => {
console.log("- invalid cron expression");
console.log("- inputToken or outputToken does not exist in MINT_ADDRESSES");
console.log("Validating dcaconfig.ts ...");

// separator
console.log('-----------------------------');

const filteredJobs = dcaconfig.filter(job => {
return (cron.validate(job.cron)
&& job.inputToken in MINT_ADDRESSES
Expand All @@ -136,8 +207,11 @@ const main = async () => {

console.log("Scheduling swaps:");
filteredJobs.map(job => {
console.log(`${job.amount} ${job.inputToken} for ${job.outputToken} ${cronstrue.toString(job.cron)}`);
console.log(setcolour(String(job.amount),32) + ` ${job.inputToken} for ${job.outputToken} ${cronstrue.toString(job.cron)}`);
});

// separator
console.log('-----------------------------');

filteredJobs.forEach(job => {
const inputToken = tokens.find((t) =>
Expand Down

0 comments on commit 5e9d0b6

Please sign in to comment.