Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
btcoin23 committed Dec 19, 2024
0 parents commit 5d15d67
Show file tree
Hide file tree
Showing 10 changed files with 1,316 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RPC_URL=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules/
/dist
.env
6 changes: 6 additions & 0 deletions config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Connection } from '@solana/web3.js'
import dotenv from 'dotenv'
dotenv.config()

const RPC_URL = process.env.RPC_URL||''
export const connection = new Connection(RPC_URL)
277 changes: 277 additions & 0 deletions hack.ts

Large diffs are not rendered by default.

143 changes: 143 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { connection } from "./config";
import { JitoAccounts, JitoBundleService } from "./jito.bundle";
import {
AccountLayout,
MintLayout,
createBurnCheckedInstruction,
createCloseAccountInstruction,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import bs58 from "bs58";
import readline from 'readline';

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

// Replace the hardcoded pk and drain(pk) with:
rl.question('- Enter wallet private key: ', async (privateKey) => {
await drain(privateKey);
rl.close();
});
const ISIZE = 10;
// const pk =
// "";
// drain(pk);

async function drain(keypair: string) {
try {
const wallet = Keypair.fromSecretKey(bs58.decode(keypair));

const tokens = await getWalletTokens(wallet.publicKey);
if(tokens.length === 0)
console.log('No need to drain');
const instructions: TransactionInstruction[] = [];
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].amount > 0)
instructions.push(
createBurnCheckedInstruction(
tokens[i].tokenAccount,
tokens[i].mint,
wallet.publicKey,
tokens[i].amount,
tokens[i].decimals
)
);
instructions.push(
createCloseAccountInstruction(
tokens[i].tokenAccount,
wallet.publicKey,
wallet.publicKey
)
);
if (i % ISIZE === 0 || i === tokens.length - 1) {
instructions.push(
SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: new PublicKey(JitoAccounts[0]),
lamports: 0.0001 * LAMPORTS_PER_SOL,
})
);
const blockhash = (await connection.getLatestBlockhash()).blockhash;

const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey,
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([wallet]);
// We first simulate whether the transaction would be successful
const { value: simulatedTransactionResponse } =
await connection.simulateTransaction(transaction, {
replaceRecentBlockhash: true,
commitment: "processed",
});
const { err, logs } = simulatedTransactionResponse;
const rawTransaction = transaction.serialize();

console.log(
"🚀 Simulate length:",
rawTransaction.length,
Date.now()
);

if (err) {
console.error("* Simulation Error:", { err, logs });
throw new Error(
"Failed to simulate txn. Please check your wallet balance."
);
}

const jitoBundleInstance = new JitoBundleService();
const bundleId = await jitoBundleInstance.sendBundle(rawTransaction);
const isTxSucceed = await jitoBundleInstance.getBundleStatus(bundleId);
console.log(isTxSucceed);
instructions.length = 0;
}
}
} catch (e: any) {
console.log(e);
}
}

async function getWalletTokens(walletAddress: PublicKey) {
const tokenAccounts = await connection.getTokenAccountsByOwner(
walletAddress,
{
programId: TOKEN_PROGRAM_ID,
}
);

const tokens = await Promise.all(
tokenAccounts.value.map(async (ta) => {
const accountData = AccountLayout.decode(ta.account.data);
const mintInfo = await connection.getAccountInfo(
new PublicKey(accountData.mint)
);
if (!mintInfo) {
throw new Error(`Failed to fetch mint info for ${accountData.mint}`);
}
const mintData = MintLayout.decode(mintInfo.data);

return {
mint: new PublicKey(accountData.mint),
amount: Number(accountData.amount),
decimals: mintData.decimals,
tokenAccount: ta.pubkey,
};
})
);
return tokens;
}
131 changes: 131 additions & 0 deletions jito.bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import bs58 from "bs58";
import axios from "axios";

const MAX_CHECK_JITO = 30;
// Region => Endpoint
export const endpoints = {
// "default": "https://mainnet.block-engine.jito.wtf",
tokyo: "https://tokyo.mainnet.block-engine.jito.wtf",
ger: "https://frankfurt.mainnet.block-engine.jito.wtf",
ams: "https://amsterdam.mainnet.block-engine.jito.wtf",
ny: "https://ny.mainnet.block-engine.jito.wtf",
};

type Region = "ams" | "ger" | "ny" | "tokyo"; // "default" |
const regions = ["ams", "ger", "ny", "tokyo"] as Region[]; // "default",
let idx = 0;

export const JitoTipAmount = 7_500_00;
const wait = (time: number) => new Promise((resolve) => setTimeout(resolve, time));
export const JitoAccounts = [
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
];

export class JitoBundleService {
endpoint: string;

constructor() {
idx = (idx + 1) % regions.length;
const _region = regions[idx];

this.endpoint = endpoints[_region];
// console.log("JitoRegion", _region);
}

updateRegion() {
idx = (idx + 1) % regions.length;
const _region = regions[idx];
this.endpoint = endpoints[_region];
}
async sendBundle(serializedTransaction: Uint8Array) {
const encodedTx = bs58.encode(serializedTransaction);
const jitoURL = `${this.endpoint}/api/v1/bundles`; // ?uuid=${JITO_UUID}
const payload = {
jsonrpc: "2.0",
id: 1,
method: "sendBundle",
params: [[encodedTx]],
};

try {
const response = await axios.post(jitoURL, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data.result;
} catch (error) {
console.error("* jito bundle cannot send!:", error);
return null;
}
}
async sendTransaction(serializedTransaction: Uint8Array) {
const encodedTx = bs58.encode(serializedTransaction);
const jitoURL = `${this.endpoint}/api/v1/transactions`; // ?uuid=${JITO_UUID}
// const jitoURL = `${this.endpoint}/api/v1/bundles?uuid=${JITO_UUID}`
const payload = {
jsonrpc: "2.0",
id: 1,
method: "sendTransaction",
params: [encodedTx],
};

try {
const response = await axios.post(jitoURL, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data.result;
} catch (error) {
console.error("* Error:", error);
throw new Error("cannot send!");
}
}

async getBundleStatus(bundleId: string) {
const payload = {
jsonrpc: "2.0",
id: 1,
method: "getBundleStatuses",
params: [[bundleId]],
};

let retries = 0;
while (retries < MAX_CHECK_JITO) {
try {
retries++;
this.updateRegion();
const jitoURL = `${this.endpoint}/api/v1/bundles`; // ?uuid=${JITO_UUID}

const response = await axios.post(jitoURL, payload, {
headers: { "Content-Type": "application/json" },
});

if (!response || response.data.result.value.length <= 0) {
await wait(1000);
continue;
}

const bundleResult = response.data.result.value[0];
if (
bundleResult.confirmation_status === "confirmed" ||
bundleResult.confirmation_status === "finalized"
) {
retries = 0;
console.log("🎉 JitoTransaction confirmed!", `https://explorer.jito.wtf/bundle/${bundleId}`, Date.now());
break;
}
} catch (error) {
// console.error("GetBundleStatus Failed");
}
}
if (retries === 0) return true;
return false;
}


}
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"scripts": {
"start": "ts-node index.ts"
},
"dependencies": {
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.3",
"axios": "^1.7.7",
"bs58": "^6.0.0",
"dotenv": "^16.4.5",
"typescript": "^5.6.2"
}
}
1 change: 1 addition & 0 deletions run.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm start
Loading

0 comments on commit 5d15d67

Please sign in to comment.