Skip to content

Instantly share code, notes, and snippets.

@YogaSakti
Last active January 4, 2025 16:28
Show Gist options
  • Save YogaSakti/d800776fe28414cb2a07c82adf070d8e to your computer and use it in GitHub Desktop.
Save YogaSakti/d800776fe28414cb2a07c82adf070d8e to your computer and use it in GitHub Desktop.
auto check pengu using curl impersonate
/* eslint-disable max-lines-per-function */
/* eslint-disable no-sync */
const solanaWeb3 = require('@solana/web3.js');
const { Connection, Keypair, PublicKey, sendAndConfirmTransaction, LAMPORTS_PER_SOL, SystemProgram, Transaction, VersionedMessage, VersionedTransaction } = solanaWeb3
const { sign } = require('tweetnacl');
const { decodeUTF8 } = require('tweetnacl-util');
const bs58 = require('bs58');
const bip39 = require('bip39');
const { derivePath } = require('ed25519-hd-key')
const fs = require('fs');
const delay = require('delay');
const { fetcher } = require('./fetcher')
const key = ''
const rpcUrl = `https://rpc.helius.xyz?api-key=${key}`
const connection = new Connection(rpcUrl, 'confirmed');
// get message using fetcher
const getMessage = () => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/auth/message?', 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
})
const doAuth = (signature, signingDate, wallet) => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/auth/token?', 'POST', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'content-type': 'application/json',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, {
signature,
signingDate,
'type': 'solana',
wallet
})
const cekElig = (address) => fetcher(`https://api.clusters.xyz/v0.1/airdrops/pengu/eligibility/${address}?`, 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}).then((res) => {
if (res.total == 0) return false
return true
})
// sign message example
// claim.pudgypenguins.com wants you to
// sign in with your Solana account:
// drnTmZNNyGocHnmkLKGIEk4Qv5aF8RC
// mPGk3U9Dh8tQy
// getClaim Data
const getClaim = (address) => fetcher(`https://api.clusters.xyz/v0.1/airdrops/pengu/claim/${address}?`, 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
})
// bulk eligibility check
const cekEligBulk = (addresses) => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/eligibility', 'POST', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'content-type': 'application/json',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, addresses)
// generate account from private key or mnemonic
const generateAccount = (accountData, isPk = false) => {
let keypair = null
if (isPk) {
keypair = Keypair.fromSecretKey(bs58.decode(accountData))
} else {
const seed = bip39.mnemonicToSeedSync(accountData);
const path = 'm/44\'/501\'/0\'/0\'';
const derivedSeed = derivePath(path, seed.toString('hex')).key;
keypair = Keypair.fromSeed(derivedSeed)
}
if (!keypair) throw new Error('Invalid secret key')
return {
publicKey: keypair.publicKey,
secretKey: keypair.secretKey,
// aditional data below
address: keypair.publicKey.toBase58(),
privateKey: bs58.encode(keypair.secretKey),
}
};
const signMessage = (message, account) => {
const { address } = account
const bufSecretKey = account.secretKey
const messageBytes = decodeUTF8(message);
const signatureRaw = sign.detached(messageBytes, bufSecretKey);
const buffer = Buffer.from(signatureRaw);
const hexString = buffer.toString('hex');
const signature = `0x${hexString}`
return { address, signature, message }
};
const saveGroupToFile = (fileName, groupData) => {
const existingData = fs.existsSync(fileName) ? JSON.parse(fs.readFileSync(fileName, 'utf8')) : [];
existingData.push(groupData);
fs.writeFileSync(fileName, JSON.stringify(existingData, null, 2));
};
// Save data to file ensuring uniqueness
const saveGroupToFileUniqe = (fileName, groupData) => {
const existingData = fs.existsSync(fileName)
? JSON.parse(fs.readFileSync(fileName, 'utf8'))
: [];
if (existingData.find((item) => item.address === groupData.address)) return;
existingData.push(groupData);
fs.writeFileSync(fileName, JSON.stringify(existingData, null, 2));
};
// Authenticate wallet and retrieve token
const getToken = async (wallet) => {
try {
const { message: messageContent, signingDate } = await getMessage();
const account =
bip39.validateMnemonic(wallet)
? generateAccount(wallet, false)
: generateAccount(wallet, true);
const { address, signature } = signMessage(messageContent, account);
const authResult = await doAuth(signature, signingDate, address);
return { token: authResult.token, address };
} catch (error) {
console.error('Error in getToken:', error);
return null;
}
};
const isMnemonicOrPk = (data) => {
try {
// Check if data contains spaces (likely a mnemonic)
if (typeof data === 'string' && data.includes(' ')) {
try {
bip39.validateMnemonic(data);
return 'mnemonic';
} catch {
// Invalid mnemonic
console.log(`Invalid mnemonic: ${data}`);
}
}
// Check if data is Base58 PrivateKey
if (typeof data === 'string' && !data.includes(' ') && !data.includes(',') && bs58.decode(data).length > 0) {
try {
Keypair.fromSecretKey(bs58.decode(data));
return 'privateKey';
} catch {
// Invalid Base58 private key
console.log(`Invalid Base58 private key: ${data}`);
}
}
// Check if data is a stringified array (secret key)
if (typeof data === 'string' && data.startsWith('[') && data.endsWith(']')) {
try {
const parsedArray = JSON.parse(data);
Keypair.fromSecretKey(Uint8Array.from(parsedArray));
return 'secretKey';
} catch {
// Invalid secret key
console.log(`Invalid secret key: ${data}`);
}
}
// If none match, return null
return null;
} catch (error) {
console.error('Error in isMnemonicOrPk:', error);
return null;
}
};
const createGroupedData = async (wallets, groupFile, totalPerGroup) => {
let currentGroup = [];
let groupIndex = 1;
// for with index
for (const wallet of wallets) {
// check every key
const keys = Object.keys(wallet);
for (const key of keys) {
let data = wallet[key];
// data value must longer than 50
if (data.length < 50) continue;
const type = isMnemonicOrPk(data);
if (type === 'mnemonic' || type === 'privateKey' || type === 'secretKey') {
wallet[type] = data;
break;
}
}
const { token, address } = await getToken(wallet.mnemonic || wallet.privateKey);
await delay(1000);
let isEligible = await cekEligBulk([token]);
isEligible = isEligible.totalUnclaimed > 0;
await delay(1000);
if (isEligible) {
currentGroup.push(wallet.mnemonic || wallet.privateKey);
console.log(`Wallet ${address || address} is eligible.`);
saveGroupToFileUniqe('claimPinguin-eligible.json', { ...wallet, token });
} else {
console.log(`Wallet ${address || address} is not eligible.`);
continue
// saveGroupToFile('claimPinguin-shadow-auth.json', { ...wallet, token });
}
// if current group reaches totalPerGroup, finalize and reset the group
if (currentGroup.length === totalPerGroup) {
const [parent] = currentGroup; // first wallet as parent
const children = currentGroup.slice(1); // remaining as children
const groupData = { parent, children };
// save current group to JSON file
saveGroupToFile(groupFile, groupData);
console.log(`Group ${groupIndex} saved.`);
groupIndex += 1;
currentGroup = []; // reset group
}
await delay(500);
}
// add remaining wallets if any, even if the group is incomplete
if (currentGroup.length > 0) {
const [parent] = currentGroup;
const children = currentGroup.slice(1);
const groupData = { parent, children };
saveGroupToFile(groupFile, groupData);
// const parentAddress = wallets.find((obj) => obj.mnemonic === parent || obj.Mnemonic === parent);
// saveParentToFile(parentFile, parentAddress.privateKey);
console.log(`Group ${groupIndex} saved.`);
}
};
(async () => {
let wallets = JSON.parse(fs.readFileSync('noMnemonic.json', 'utf8'));
const totalPerGroup = 10; // total mnemonic per grup contoh: 50 = 1 parent + 49 child
// INI NAMA FILE HASILNYA
const groupFile = 'claimPinguin-walletGroup.json';
// create grouped data and save to files incrementally
await createGroupedData(wallets, groupFile, totalPerGroup);
})();
/* eslint-disable max-lines-per-function */
/* eslint-disable no-sync */
/* eslint-disable no-continue */
/* eslint-disable no-undefined */
/* eslint-disable max-lines */
const solanaWeb3 = require('@solana/web3.js');
// eslint-disable-next-line no-unused-vars
const { Connection, ComputeBudgetProgram, Keypair, PublicKey, Transaction, LAMPORTS_PER_SOL, SystemProgram, TransactionMessage, VersionedMessage, VersionedTransaction } = solanaWeb3
const { sign } = require('tweetnacl');
const { decodeUTF8 } = require('tweetnacl-util');
const bs58 = require('bs58');
const bip39 = require('bip39');
const { derivePath } = require('ed25519-hd-key')
const fs = require('fs');
const delay = require('delay');
const { fetcher } = require('./utils/index')
const readline = require('readline');
const SplToken = require('@solana/spl-token');
const { getOrCreateAssociatedTokenAccount } = SplToken
const PENGU_TOKEN_ADDRESS = new PublicKey('2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv');
const askQuestion = (query) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question(query, (answer) => {
rl.close();
resolve(answer);
});
});
}
// const key = ''
// const rpcUrl = `https://rpc.helius.xyz?api-key=${key}`
const rpcUrl = `https://cold-hanni-fast-mainnet.helius-rpc.com/`
const connection = new Connection(rpcUrl, 'confirmed');
// get message using fetcher
const getMessageApi = () => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/auth/message?', 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
})
function getMessage() {
const signingDate = new Date().toISOString();
return {
message: `$PENGU Claim\n\nThis is a gasless signature to prove ownership of your wallet address.\n\nAlways ensure you are on the correct website URL: claim.pudgypenguins.com.\n\n${signingDate}`,
signingDate: signingDate
};
}
const doAuth = (signature, signingDate, wallet) => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/auth/token?', 'POST', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'content-type': 'application/json',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, {
signature,
signingDate,
'type': 'solana',
wallet
})
// ENDPOINTS DEAD
const cekElig = (address) => fetcher(`https://api.clusters.xyz/v0.1/airdrops/pengu/eligibility/${address}?`, 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
})
// bulk eligibility check using token? so dumb
const cekEligBulk = (addresses) => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/eligibility', 'POST', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'content-type': 'application/json',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, addresses)
// get txData by server
const getClaim = (address) => fetcher(`https://api.clusters.xyz/v0.1/airdrops/pengu/claim/${address}?`, 'GET', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
})
// link child wallet to parent wallet
const linkChildToParent = (authorization, addressParent) => fetcher('https://api.clusters.xyz/v0.1/airdrops/pengu/link?', 'POST', {
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'authorization': `Bearer ${authorization}`,
'content-type': 'application/json',
'priority': 'u=1, i',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'Referer': 'https://claim.pudgypenguins.com/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, {
'address': addressParent,
'type': 'solana',
'isPrivate': false
})
const isMnemonicOrPk = (data) => {
try {
// Check if data contains spaces (likely a mnemonic)
if (typeof data === 'string' && data.includes(' ')) {
try {
bip39.validateMnemonic(data);
return 'mnemonic';
} catch {
// Invalid mnemonic
console.log(`Invalid mnemonic: ${data}`);
}
}
// Check if data is Base58 PrivateKey
if (typeof data === 'string' && !data.includes(' ') && !data.includes(',') && bs58.decode(data).length > 0) {
try {
Keypair.fromSecretKey(bs58.decode(data));
return 'privateKey';
} catch {
// Invalid Base58 private key
console.log(`Invalid Base58 private key: ${data}`);
}
}
// Check if data is a stringified array (secret key)
if (typeof data === 'string' && data.startsWith('[') && data.endsWith(']')) {
try {
const parsedArray = JSON.parse(data);
Keypair.fromSecretKey(Uint8Array.from(parsedArray));
return 'secretKey';
} catch {
// Invalid secret key
console.log(`Invalid secret key: ${data}`);
}
}
// If none match, return null
return null;
} catch (error) {
console.error('Error in isMnemonicOrPk:', error);
return null;
}
};
const generateAccount = (accountData) => {
let keypair = null;
// Determine the type of the input data
const type = isMnemonicOrPk(accountData);
// Generate Keypair based on the type
if (type === 'mnemonic') {
const seed = bip39.mnemonicToSeedSync(accountData);
const path = 'm/44\'/501\'/0\'/0\'';
const derivedSeed = derivePath(path, seed.toString('hex')).key;
keypair = Keypair.fromSeed(derivedSeed);
} else if (type === 'privateKey') {
keypair = Keypair.fromSecretKey(bs58.decode(accountData));
} else if (type === 'secretKey') {
const parsedArray = JSON.parse(accountData);
keypair = Keypair.fromSecretKey(Uint8Array.from(parsedArray));
} else {
throw new Error('Invalid account data');
}
if (!keypair) {
throw new Error('Failed to generate keypair');
}
// Construct account object
const account = {
publicKey: keypair.publicKey,
secretKey: keypair.secretKey,
// Additional data
address: keypair.publicKey.toBase58(),
privateKey: bs58.encode(keypair.secretKey),
};
return account;
};
// sign message
const signMessage = (message, account) => {
const { address } = account
const bufSecretKey = account.secretKey
const messageBytes = decodeUTF8(message);
const signatureRaw = sign.detached(messageBytes, bufSecretKey);
const buffer = Buffer.from(signatureRaw);
const hexString = buffer.toString('hex');
const signature = `0x${hexString}`
return { address, signature, message }
};
// get token from wallet
const getToken = async (wallet) => {
const message = await getMessage();
const { message: messageContent, signingDate } = message;
const account = generateAccount(wallet);
const { address } = account;
const { signature } = signMessage(messageContent, account);
const authResult = await doAuth(signature, signingDate, address);
return { token: authResult.token, address };
};
// get SPL token balance
const getTokenBalance = async (address) => {
const parsedTokenAccounts = await connection.getParsedTokenAccountsByOwner(new PublicKey(address), { mint: new PublicKey(PENGU_TOKEN_ADDRESS) });
if (parsedTokenAccounts.value.length === 0) {
return 0;
}
return parsedTokenAccounts.value[0].account.data.parsed.info.tokenAmount.uiAmount;
};
// send SPL token
const sendToken = async (from, to, amount) => {
const TokenAddress = PENGU_TOKEN_ADDRESS
const recipientAddress = new PublicKey(to);
const senderKeypair = Keypair.fromSecretKey(from.secretKey);
const recipient = await getOrCreateAssociatedTokenAccount(
connection,
senderKeypair,
TokenAddress,
recipientAddress
);
const sender = await getOrCreateAssociatedTokenAccount(
connection,
senderKeypair,
TokenAddress,
senderKeypair.publicKey
);
const instructions = SplToken.createTransferInstruction(
sender.address,
recipient.address,
senderKeypair.publicKey,
amount,
[],
SplToken.TOKEN_PROGRAM_ID
)
// add compute budget instruction for priority fee
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 0.00015 * LAMPORTS_PER_SOL
});
await delay(500)
const blockhash = await connection.getLatestBlockhash('confirmed').then(res => res.blockhash);
await delay(500)
const messageV0 = new TransactionMessage({
payerKey: senderKeypair.publicKey,
recentBlockhash: blockhash,
instructions: [computeBudgetIx, instructions]
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([senderKeypair]);
const txid = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: !1,
preflightCommitment: 'confirmed'
})
await connection.getLatestBlockhash('confirmed').then(({ blockhash, lastValidBlockHeight }) => connection.confirmTransaction({
blockhash,
lastValidBlockHeight,
signature: txid
}, 'confirmed')) // wait for confirmation
return txid;
};
// send SOL
const sendSol = async (from, to, amount) => {
try {
const blockhash = await connection.getLatestBlockhash('confirmed').then(res => res.blockhash);
const messageV0 = new TransactionMessage({
payerKey: from.publicKey,
recentBlockhash: blockhash,
instructions: [SystemProgram.transfer({
fromPubkey: from.publicKey,
toPubkey: new PublicKey(to),
lamports: BigInt(Math.round(amount * LAMPORTS_PER_SOL))
})],
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([from]);
const signature = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: !1,
maxRetries: 5,
preflightCommitment: 'confirmed'
});
await connection.getLatestBlockhash('confirmed').then(({ blockhash, lastValidBlockHeight }) => connection.confirmTransaction({
blockhash,
lastValidBlockHeight,
signature: signature
}, 'confirmed')) // wait for confirmation
return signature;
} catch (error) {
console.error('Error sending SOL:', error);
return null;
}
};
// get transaction fee
const getTransactionFee = async (pkFee) => {
try {
const feeAccountChecker = generateAccount(pkFee);
const recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
const transaction = new Transaction();
transaction.recentBlockhash = recentBlockhash;
transaction.feePayer = feeAccountChecker.publicKey;
transaction.add(SystemProgram.transfer({
fromPubkey: feeAccountChecker.publicKey,
toPubkey: new PublicKey('MfDuWeqSHEqTFVYZ7LoexgAK9dxk7cy4DFJWjWMGVWa'), // transferring to itself just to simulate
lamports: 10
}));
const message = transaction.compileMessage();
const response = await connection.getFeeForMessage(
message,
'confirmed'
);
const feeInLamports = response.value;
return feeInLamports;
} catch (error) {
console.error(error);
}
};
// collect token and sol
const collectTokenAndSol = async (accountFrom, addressTo, amountToken = null) => {
try {
const tokenBalance = await getTokenBalance(accountFrom.address);
if (tokenBalance !== 0) {
// eslint-disable-next-line prefer-exponentiation-operator
let amountToSend = amountToken == tokenBalance * 1000000 ? amountToken : tokenBalance * 1000000; // $PENGU 6 decimal
const sendTokenResult = await sendToken(accountFrom, addressTo, amountToSend);
if (sendTokenResult) console.log(`[>] ${amountToSend / 1000000} $PENGU Has Been Sent To Banker! https://solscan.io/tx/${sendTokenResult}`);
} else {
console.log(`[!] ${accountFrom.address} | No token to collect!`);
}
const transferFee = await getTransactionFee(accountFrom.privateKey);
const remainingBalance = await connection.getBalance(new PublicKey(accountFrom.address));
const minimumBalanceLamports = transferFee;
if (remainingBalance > minimumBalanceLamports) {
let amountToSend = (remainingBalance - minimumBalanceLamports) / LAMPORTS_PER_SOL;
console.log(`[>] Sending remaining ${amountToSend.toFixed(4)} SOL back to main wallet...`);
const returnSig = await sendSol(accountFrom, addressTo, amountToSend);
if (returnSig) console.log(`[>] Success! TxHash: https://solscan.io/tx/${returnSig}`);
} else {
console.log(`[!] ${accountFrom.address} | No SOL to collect!`);
}
} catch (error) {
console.error('Error collecting token and SOL:', error.message);
}
}
const sendTokenAndSolUsingFeePayer = async (source, target, feePayer, amountToken = 0) => {
try {
const TokenAddress = PENGU_TOKEN_ADDRESS;
let recipientAddress = new PublicKey(target);
const sourceKeypair = Keypair.fromSecretKey(source.secretKey);
const feePayerKeypair = Keypair.fromSecretKey(feePayer.secretKey);
// Initialize instructions
const instructions = [];
const computeUnitPrice = 0.005 * LAMPORTS_PER_SOL;
// Add token transfer instruction if token balance exists
const tokenBalance = await getTokenBalance(source.publicKey);
let amountToSend = amountToken == tokenBalance * 1000000 ? amountToken : tokenBalance * 1000000; // $PENGU 6 decimal
if (tokenBalance > 0) {
const recipientTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
sourceKeypair,
TokenAddress,
recipientAddress
);
const senderTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
sourceKeypair,
TokenAddress,
sourceKeypair.publicKey
);
instructions.push(
SplToken.createTransferInstruction(
senderTokenAccount.address,
recipientTokenAccount.address,
sourceKeypair.publicKey,
Math.floor(tokenBalance * 10 ** 6), // Assuming token has 6 decimals
[],
SplToken.TOKEN_PROGRAM_ID
)
);
// Add Compute Budget Program instruction (for priority fee)
instructions.push(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: computeUnitPrice,
})
);
console.log(`[>] ${tokenBalance} $PENGU prepared for transfer...`);
}
// Add SOL transfer instruction if balance exists
const senderBalance = await connection.getBalance(sourceKeypair.publicKey, 'confirmed');
if (senderBalance > 0) {
instructions.push(
SystemProgram.transfer({
fromPubkey: sourceKeypair.publicKey,
toPubkey: recipientAddress,
lamports: senderBalance, // Send all available SOL
})
);
console.log(`[>] ${(senderBalance / LAMPORTS_PER_SOL).toFixed(4)} $SOL prepared for transfer...`);
}
if (instructions.length === 0) {
console.log('[!] No tokens or SOL to transfer.');
return null;
}
// Get latest blockhash
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
// Create transaction message
const message = new TransactionMessage({
payerKey: feePayerKeypair.publicKey, // Fee payer handles fees
recentBlockhash: blockhash,
instructions,
}).compileToV0Message();
// Create and sign transaction
const transaction = new VersionedTransaction(message);
transaction.sign([sourceKeypair, feePayerKeypair]); // Both source and fee-payer must sign
const txid = await connection.sendTransaction(transaction, {
skipPreflight: false,
maxRetries: 3,
preflightCommitment: 'confirmed',
});
// console.log(`[>] Transaction Sent! TxHash: https://solscan.io/tx/${txid}`);
// Confirm the transaction
const confirmation = await connection.confirmTransaction(
{
blockhash,
lastValidBlockHeight,
signature: txid,
},
'confirmed'
);
if (confirmation.value.err) {
console.error(`[!] Transaction failed:`);
console.log(confirmation.value.err)
} else {
console.log(
`[>] Transaction Confirmed! TxHash: https://solscan.io/tx/${txid}`
);
}
return txid;
} catch (error) {
console.error('Transaction failed:');
console.error(error.message);
if (error.logs) {
console.error('Logs:', error.logs);
} else {
console.error('No logs available.');
}
return null;
}
};
// CONFIG ===========================================================================
// PK utama untuk mengirim SOL // INI BOLEH SAMA DENGAN pkFeePayer
const pkBank = 'ini pk'
const bankAccount = generateAccount(pkBank);
// Fee Payer untuk mengirim token dan SOL (biaya pengiriman) // INI BOLEH SAMA DENGAN pkBank
const pkFeePayer = 'ini pk'
const feePayer = generateAccount(pkFeePayer);
const SEND_AMOUNT_SOL = 0.01;
// wallet list
let wallets = JSON.parse(fs.readFileSync('./claimPinguin-mnemonic.json', 'utf8'));
// first 50 only
// wallets = wallets.slice(50);
// END CONFIG ===========================================================================
(async () => {
for (let i = 0; i < wallets.length; i++) {
console.log('==================================================================================================================')
const walletParent = wallets[i].parent;
let walletChild = wallets[i].children;
// load parent wallet
const accountParent = generateAccount(walletParent);
const addressParent = accountParent.address;
// login to parent wallet
let loginParent;
try {
loginParent = await getToken(walletParent);
} catch (err) {
console.error(`[${i + 1}] Error logging in to parent wallet: ${err.message}`);
continue;
}
if (!loginParent.token) {
console.error(`[${i + 1}] Error logging in to parent wallet!`);
continue;
}
console.log(`[${i + 1}] ${addressParent} | Logged in to parent wallet!`);
let eligibilityCheckParent;
try {
eligibilityCheckParent = await cekEligBulk([loginParent.token]);
} catch (err) {
console.error(`[${i + 1}] Error checking eligibility for parent wallet: ${err.message}`);
continue;
}
if (eligibilityCheckParent.total === 0) {
console.log(`[${i + 1}] Not eligible!`);
await delay(2000);
continue;
} else if (eligibilityCheckParent.totalUnclaimed === 0) {
console.log(`[${i + 1}] ${addressParent} | Already claimed ${eligibilityCheckParent.total} $PENGU!`);
await sendTokenAndSolUsingFeePayer(accountParent, bankAccount.address, bankAccount);
await delay(1000);
continue;
} else {
console.log(`[${i + 1}] Parent Eligible! Total: ${eligibilityCheckParent.total} $PENGU | Unclaimed: ${eligibilityCheckParent.totalUnclaimed} $PENGU`);
}
await delay(1000);
// get token for children
const childrenTokens = [];
for (const [index, childMnemonic] of walletChild.entries()) {
// get address from child mnemonic
const childAccount = generateAccount(childMnemonic);
const childAddress = childAccount.address;
// if child address is already linked to parent, skip
if (eligibilityCheckParent.addresses.includes(childAddress)) {
console.log(`[${i + 1} | ${index + 1}] ${childAddress} | Already linked to parent!`);
continue;
}
let tokenData = {};
try {
tokenData = await getToken(childMnemonic);
} catch (err) {
console.error(`[${i + 1} | ${index + 1}] Error getting token for child ${index + 1}: ${err.message}`);
}
await delay(250);
if (tokenData.token) {
const linkResult = await linkChildToParent(tokenData.token, addressParent).catch((err) => {
console.error(`[${i + 1} | ${index + 1}] Error linking child ${index + 1} to parent: ${err.message}`);
return null;
});
if (linkResult === 201) {
console.log(`[${i + 1} | ${index + 1}] Success Load & Link Child ${index + 1} to Parent!`);
}
}
childrenTokens.push(tokenData);
await delay(250);
}
await delay(1000);
try {
eligibilityCheckParent = await cekEligBulk([loginParent.token]);
} catch (err) {
console.error(`[${i + 1}] Error re-checking eligibility for parent wallet: ${err.message}`);
}
// Claim transaction
try {
const claimParent = await getClaim(addressParent);
if (claimParent.error) {
console.error(`[${i + 1}] Error getting claim data for parent: ${claimParent.error}`);
if (claimParent.error === 'no claims left') {
await sendTokenAndSolUsingFeePayer(accountParent, bankAccount.address, bankAccount);
}
continue;
}
// Balance and transaction handling for parent wallet
try {
const balance = await connection.getBalance(new PublicKey(addressParent));
if (balance < SEND_AMOUNT_SOL * LAMPORTS_PER_SOL) {
console.log(`[!] Address ${addressParent} doesn't have enough balance! | Sending SOL to parent wallet...`);
const sendSolResult = await sendSol(bankAccount, addressParent, SEND_AMOUNT_SOL);
if (!sendSolResult) {
console.error(`[${i + 1}] Failed to send SOL to parent wallet`);
continue;
}
console.log(`[>] SOL sent! Tx: https://solscan.io/tx/${sendSolResult}`);
let parentBalance = 0;
do {
parentBalance = await connection.getBalance(new PublicKey(addressParent));
if (parentBalance >= SEND_AMOUNT_SOL * LAMPORTS_PER_SOL) {
break;
}
await delay(500);
} while (parentBalance <= SEND_AMOUNT_SOL * LAMPORTS_PER_SOL);
await delay(500);
}
} catch (error) {
console.error('Error sending SOL:', error.message);
}
// Send claim transaction
const accountParentKeypair = Keypair.fromSecretKey(accountParent.secretKey);
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
for (let x = 0; x < claimParent.length; x++) {
const { data, signatures } = claimParent[x];
const txMessage = VersionedMessage.deserialize(Buffer.from(data, 'base64'));
const transaction = new VersionedTransaction(txMessage);
transaction.signatures = signatures.map((y) => Buffer.from(y, 'base64'));
transaction.sign([accountParentKeypair]);
const sendTx = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: false,
preflightCommitment: 'confirmed'
});
const confirmation = await connection.confirmTransaction(
{
blockhash,
lastValidBlockHeight,
signature: sendTx,
},
'confirmed'
);
if (confirmation.value.err) {
console.error(`[${i + 1}] Transaction ${x + 1} failed: ${confirmation.value.err}`);
} else {
console.log(`[${i + 1}] Transaction ${x + 1} confirmed: https://solscan.io/tx/${sendTx}`);
}
await delay(1000);
}
} catch (error) {
console.error('Error sending claim transaction:', error.message);
}
// Collect token and SOL
try {
let unclaim = parseInt(eligibilityCheckParent.totalUnclaimed);
let tokenBalance = 0;
do {
tokenBalance = await getTokenBalance(addressParent);
if (tokenBalance >= unclaim) break;
await delay(1000);
} while (tokenBalance < unclaim);
const amountToSend = tokenBalance * 1000000;
await sendTokenAndSolUsingFeePayer(accountParent, bankAccount.address, feePayer, amountToSend);
} catch (error) {
console.error('Error sending token:', error.message);
}
await delay(3000);
console.log(`[${i + 1}] Done!`);
}
})();
/* eslint-disable no-prototype-builtins */
/* eslint-disable init-declarations */
/* eslint-disable new-cap */
/* eslint-disable max-params */
/* eslint-disable no-param-reassign */
const fetch = require('node-fetch');
const { CurlGenerator } = require('curl-generator')
const shell = require('shelljs');
const baseProgram = `${process.cwd()}/utils/curl_impersonate -sS`
const Uas = require('user-agents');
const userAgent = new Uas({ deviceCategory: 'desktop' });
const isJsonString = (str) => {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
const fetcher = (url, method, headers, body) => new Promise((resolve, reject) => {
const params = {
url,
method,
headers,
redirect: 'follow'
}
if (body) params.body = body
const generatedCommand = CurlGenerator(params, { silent: true, compressed: true })
const result = shell.exec(`${baseProgram} ${generatedCommand}`, { async: false, silent: true }).stdout;
if (isJsonString(result)) {
resolve(JSON.parse(result))
} else {
console.log(params);
console.log(result);
reject(result)
}
})
module.exports = {
fetcher,
isJsonString
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment