Skip to content

Commit

Permalink
Merge pull request #73 from quiknode-labs/adding-raydium-swap-example
Browse files Browse the repository at this point in the history
Adding raydium swap example
  • Loading branch information
Sahilsen authored Aug 16, 2024
2 parents 7a61561 + e2a0eb6 commit e2edf2f
Show file tree
Hide file tree
Showing 4 changed files with 618 additions and 0 deletions.
109 changes: 109 additions & 0 deletions solana/raydium-swap-ts/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import dotenv from 'dotenv';
import https from 'https';
dotenv.config();

if (!process.env.QUICKNODE_URL) {
throw new Error('QUICKNODE_URL is not set in the environment variables');
}

if (!process.env.WALLET_SECRET_KEY) {
throw new Error('WALLET_SECRET_KEY is not set in the environment variables');
}

interface PriorityFeeResponse {
jsonrpc: string;
result: {
per_compute_unit: {
extreme: number;
medium: number;
};
};
id: number;
}

function httpsRequest(url: string, options: https.RequestOptions, data: string): Promise<string> {
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk.toString());
res.on('end', () => resolve(body));
});
req.on('error', reject);
req.write(data);
req.end();
});
}

async function fetchPriorityFee(): Promise<number> {
if (!process.env.QUICKNODE_URL) {
throw new Error('QUICKNODE_URL is not set in the environment variables');
}

const url = new URL(process.env.QUICKNODE_URL);
const options: https.RequestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
};

const requestBody = JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'qn_estimatePriorityFees',
params: {
last_n_blocks: 100,
account: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
}
});

const response = await httpsRequest(url.href, options, requestBody);
const data: unknown = JSON.parse(response);

if (!isPriorityFeeResponse(data)) {
throw new Error('Unexpected response format from priority fee API');
}

// Using the 'extreme' priority fee from 'per_compute_unit'
const extremePriorityFeePerCU = data.result.per_compute_unit.extreme;

// Estimate compute units for the transaction (this is an approximation)
const estimatedComputeUnits = 300000; // Adjust this based on your typical transaction

// Calculate total priority fee in micro-lamports
const totalPriorityFeeInMicroLamports = extremePriorityFeePerCU * estimatedComputeUnits;

// Convert to SOL (1 SOL = 1e9 lamports = 1e15 micro-lamports)
const priorityFeeInSOL = totalPriorityFeeInMicroLamports / 1e15;

// Ensure the fee is not less than 0.000001 SOL (minimum fee)
return Math.max(priorityFeeInSOL, 0.000001);
}

function isPriorityFeeResponse(data: unknown): data is PriorityFeeResponse {
return (
typeof data === 'object' &&
data !== null &&
'jsonrpc' in data &&
'result' in data &&
typeof data.result === 'object' &&
data.result !== null &&
'per_compute_unit' in data.result &&
typeof data.result.per_compute_unit === 'object' &&
data.result.per_compute_unit !== null &&
'extreme' in data.result.per_compute_unit &&
typeof data.result.per_compute_unit.extreme === 'number'
);
}

export const CONFIG = {
RPC_URL: process.env.QUICKNODE_URL,
WALLET_SECRET_KEY: process.env.WALLET_SECRET_KEY,
BASE_MINT: 'So11111111111111111111111111111111111111112', // SOLANA mint address
QUOTE_MINT: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263', // BONK mint address
TOKEN_A_AMOUNT: 0.000001,
EXECUTE_SWAP: true,
USE_VERSIONED_TRANSACTION: false,
SLIPPAGE: 5,
getPriorityFee: fetchPriorityFee,
};
2 changes: 2 additions & 0 deletions solana/raydium-swap-ts/src/example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
QUICKNODE_URL=
WALLET_SECRET_KEY=
119 changes: 119 additions & 0 deletions solana/raydium-swap-ts/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { RaydiumSwap } from './raydium-swap';
import { CONFIG } from './config';
import {
PublicKey,
LAMPORTS_PER_SOL,
Transaction,
VersionedTransaction,
} from '@solana/web3.js';

async function getTokenBalance(raydiumSwap: RaydiumSwap, mint: string): Promise<number> {
const userTokenAccounts = await raydiumSwap.getOwnerTokenAccounts();
const tokenAccount = userTokenAccounts.find(account =>
account.accountInfo.mint.equals(new PublicKey(mint))
);
if (tokenAccount) {
const balance = await raydiumSwap.connection.getTokenAccountBalance(tokenAccount.pubkey);
return balance.value.uiAmount || 0;
}
return 0;
}

async function swap() {
console.log('Starting swap process...');
const raydiumSwap = new RaydiumSwap(CONFIG.RPC_URL, CONFIG.WALLET_SECRET_KEY);

await raydiumSwap.loadPoolKeys();
let poolInfo = raydiumSwap.findPoolInfoForTokens(CONFIG.BASE_MINT, CONFIG.QUOTE_MINT)
|| await raydiumSwap.findRaydiumPoolInfo(CONFIG.BASE_MINT, CONFIG.QUOTE_MINT);

if (!poolInfo) {
throw new Error("Couldn't find the pool info");
}

await raydiumSwap.createWrappedSolAccountInstruction(CONFIG.TOKEN_A_AMOUNT);

console.log('Fetching current priority fee...');
const priorityFee = await CONFIG.getPriorityFee();
console.log(`Current priority fee: ${priorityFee} SOL`);

console.log('Creating swap transaction...');
const swapTx = await raydiumSwap.getSwapTransaction(
CONFIG.QUOTE_MINT,
CONFIG.TOKEN_A_AMOUNT,
poolInfo,
CONFIG.USE_VERSIONED_TRANSACTION,
CONFIG.SLIPPAGE
);

console.log(`Using priority fee: ${priorityFee} SOL`);
console.log(`Transaction signed with payer: ${raydiumSwap.wallet.publicKey.toBase58()}`);

console.log(`Swapping ${CONFIG.TOKEN_A_AMOUNT} SOL for BONK`);

if (CONFIG.EXECUTE_SWAP) {
try {
let txid: string;
if (CONFIG.USE_VERSIONED_TRANSACTION) {
if (!(swapTx instanceof VersionedTransaction)) {
throw new Error('Expected a VersionedTransaction but received a different type');
}
const latestBlockhash = await raydiumSwap.connection.getLatestBlockhash();
txid = await raydiumSwap.sendVersionedTransaction(
swapTx,
latestBlockhash.blockhash,
latestBlockhash.lastValidBlockHeight
);
} else {
if (!(swapTx instanceof Transaction)) {
throw new Error('Expected a Transaction but received a different type');
}
txid = await raydiumSwap.sendLegacyTransaction(swapTx);
}
console.log(`Transaction sent, signature: ${txid}`);
console.log(`Transaction executed: https://explorer.solana.com/tx/${txid}`);

console.log('Transaction confirmed successfully');

// Fetch and display token balances
const solBalance = await raydiumSwap.connection.getBalance(raydiumSwap.wallet.publicKey) / LAMPORTS_PER_SOL;
const bonkBalance = await getTokenBalance(raydiumSwap, CONFIG.QUOTE_MINT);

console.log('\nToken Balances After Swap:');
console.log(`SOL: ${solBalance.toFixed(6)} SOL`);
console.log(`BONK: ${bonkBalance.toFixed(2)} BONK`);
} catch (error) {
console.error('Error executing transaction:', error);
}
} else {
console.log('Simulating transaction (dry run)');
try {
let simulationResult;
if (CONFIG.USE_VERSIONED_TRANSACTION) {
if (!(swapTx instanceof VersionedTransaction)) {
throw new Error('Expected a VersionedTransaction but received a different type');
}
simulationResult = await raydiumSwap.simulateVersionedTransaction(swapTx);
} else {
if (!(swapTx instanceof Transaction)) {
throw new Error('Expected a Transaction but received a different type');
}
simulationResult = await raydiumSwap.simulateLegacyTransaction(swapTx);
}
console.log('Simulation successful');
console.log('Simulated transaction details:');
console.log(`Logs:`, simulationResult.logs);
console.log(`Units consumed:`, simulationResult.unitsConsumed);
if (simulationResult.returnData) {
console.log(`Return data:`, simulationResult.returnData);
}
} catch (error) {
console.error('Error simulating transaction:', error);
}
}
}

swap().catch((error) => {
console.error('An error occurred during the swap process:');
console.error(error);
});
Loading

0 comments on commit e2edf2f

Please sign in to comment.