mirror of
https://github.com/fdundjer/solana-sniper-bot.git
synced 2025-11-09 20:12:06 +10:00
Compare commits
2 Commits
570a8341f6
...
afef9e468c
| Author | SHA1 | Date | |
|---|---|---|---|
| afef9e468c | |||
| 4fcf5165d5 |
@ -1,5 +1,6 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120
|
||||
"printWidth": 120,
|
||||
"useTabs": true
|
||||
}
|
||||
340
bot.js
Normal file
340
bot.js
Normal file
@ -0,0 +1,340 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Bot = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const spl_token_1 = require("@solana/spl-token");
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const cache_1 = require("./cache");
|
||||
const filters_1 = require("./filters");
|
||||
const helpers_1 = require("./helpers");
|
||||
const async_mutex_1 = require("async-mutex");
|
||||
const bn_js_1 = __importDefault(require("bn.js"));
|
||||
const warp_transaction_executor_1 = require("./transactions/warp-transaction-executor");
|
||||
const jito_rpc_transaction_executor_1 = require("./transactions/jito-rpc-transaction-executor");
|
||||
class Bot {
|
||||
constructor(connection, marketStorage, poolStorage, txExecutor, config) {
|
||||
this.connection = connection;
|
||||
this.marketStorage = marketStorage;
|
||||
this.poolStorage = poolStorage;
|
||||
this.txExecutor = txExecutor;
|
||||
this.config = config;
|
||||
this.sellExecutionCount = 0;
|
||||
this.stopLoss = new Map();
|
||||
this.isWarp = false;
|
||||
this.isJito = false;
|
||||
this.isWarp = txExecutor instanceof warp_transaction_executor_1.WarpTransactionExecutor;
|
||||
this.isJito = txExecutor instanceof jito_rpc_transaction_executor_1.JitoTransactionExecutor;
|
||||
this.semaphore = new async_mutex_1.Semaphore(config.maxTokensAtTheTime);
|
||||
if (this.config.useSnipeList) {
|
||||
this.snipeListCache = new cache_1.SnipeListCache();
|
||||
this.snipeListCache.init();
|
||||
}
|
||||
}
|
||||
validate() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
yield (0, spl_token_1.getAccount)(this.connection, this.config.quoteAta, this.connection.commitment);
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.error(`${this.config.quoteToken.symbol} token account not found in wallet: ${this.config.wallet.publicKey.toString()}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
buy(accountId, poolState) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
helpers_1.logger.trace({ mint: poolState.baseMint }, `Processing new pool...`);
|
||||
if (this.config.useSnipeList && !((_a = this.snipeListCache) === null || _a === void 0 ? void 0 : _a.isInList(poolState.baseMint.toString()))) {
|
||||
helpers_1.logger.debug({ mint: poolState.baseMint.toString() }, `Skipping buy because token is not in a snipe list`);
|
||||
return;
|
||||
}
|
||||
if (this.config.autoBuyDelay > 0) {
|
||||
helpers_1.logger.debug({ mint: poolState.baseMint }, `Waiting for ${this.config.autoBuyDelay} ms before buy`);
|
||||
yield (0, helpers_1.sleep)(this.config.autoBuyDelay);
|
||||
}
|
||||
const numberOfActionsBeingProcessed = this.config.maxTokensAtTheTime - this.semaphore.getValue() + this.sellExecutionCount;
|
||||
if (this.semaphore.isLocked() || numberOfActionsBeingProcessed >= this.config.maxTokensAtTheTime) {
|
||||
helpers_1.logger.debug({ mint: poolState.baseMint.toString() }, `Skipping buy because max tokens to process at the same time is ${this.config.maxTokensAtTheTime} and currently ${numberOfActionsBeingProcessed} tokens is being processed`);
|
||||
return;
|
||||
}
|
||||
yield this.semaphore.acquire();
|
||||
try {
|
||||
const [market, mintAta] = yield Promise.all([
|
||||
this.marketStorage.get(poolState.marketId.toString()),
|
||||
(0, spl_token_1.getAssociatedTokenAddress)(poolState.baseMint, this.config.wallet.publicKey),
|
||||
]);
|
||||
const poolKeys = (0, helpers_1.createPoolKeys)(accountId, poolState, market);
|
||||
if (!this.config.useSnipeList) {
|
||||
const match = yield this.filterMatch(poolKeys);
|
||||
if (!match) {
|
||||
helpers_1.logger.trace({ mint: poolKeys.baseMint.toString() }, `Skipping buy because pool doesn't match filters`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < this.config.maxBuyRetries; i++) {
|
||||
try {
|
||||
helpers_1.logger.info({ mint: poolState.baseMint.toString() }, `Send buy transaction attempt: ${i + 1}/${this.config.maxBuyRetries}`);
|
||||
const tokenOut = new raydium_sdk_1.Token(spl_token_1.TOKEN_PROGRAM_ID, poolKeys.baseMint, poolKeys.baseDecimals);
|
||||
const result = yield this.swap(poolKeys, this.config.quoteAta, mintAta, this.config.quoteToken, tokenOut, this.config.quoteAmount, this.config.buySlippage, this.config.wallet, 'buy');
|
||||
if (result.confirmed) {
|
||||
helpers_1.logger.info({
|
||||
mint: poolState.baseMint.toString(),
|
||||
signature: result.signature,
|
||||
url: `https://solscan.io/tx/${result.signature}?cluster=${helpers_1.NETWORK}`,
|
||||
}, `Confirmed buy tx`);
|
||||
break;
|
||||
}
|
||||
helpers_1.logger.info({
|
||||
mint: poolState.baseMint.toString(),
|
||||
signature: result.signature,
|
||||
error: result.error,
|
||||
}, `Error confirming buy tx`);
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.debug({ mint: poolState.baseMint.toString(), error }, `Error confirming buy transaction`);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.error({ mint: poolState.baseMint.toString(), error }, `Failed to buy token`);
|
||||
}
|
||||
finally {
|
||||
this.semaphore.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
sell(accountId, rawAccount) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
this.sellExecutionCount++;
|
||||
try {
|
||||
helpers_1.logger.trace({ mint: rawAccount.mint }, `Processing new token...`);
|
||||
const poolData = yield this.poolStorage.get(rawAccount.mint.toString());
|
||||
if (!poolData) {
|
||||
helpers_1.logger.trace({ mint: rawAccount.mint.toString() }, `Token pool data is not found, can't sell`);
|
||||
return;
|
||||
}
|
||||
const tokenIn = new raydium_sdk_1.Token(spl_token_1.TOKEN_PROGRAM_ID, poolData.state.baseMint, poolData.state.baseDecimal.toNumber());
|
||||
const tokenAmountIn = new raydium_sdk_1.TokenAmount(tokenIn, rawAccount.amount, true);
|
||||
if (tokenAmountIn.isZero()) {
|
||||
helpers_1.logger.info({ mint: rawAccount.mint.toString() }, `Empty balance, can't sell`);
|
||||
return;
|
||||
}
|
||||
if (this.config.autoSellDelay > 0) {
|
||||
helpers_1.logger.debug({ mint: rawAccount.mint }, `Waiting for ${this.config.autoSellDelay} ms before sell`);
|
||||
yield (0, helpers_1.sleep)(this.config.autoSellDelay);
|
||||
}
|
||||
const market = yield this.marketStorage.get(poolData.state.marketId.toString());
|
||||
const poolKeys = (0, helpers_1.createPoolKeys)(new web3_js_1.PublicKey(poolData.id), poolData.state, market);
|
||||
for (let i = 0; i < this.config.maxSellRetries; i++) {
|
||||
try {
|
||||
const shouldSell = yield this.waitForSellSignal(tokenAmountIn, poolKeys);
|
||||
if (!shouldSell) {
|
||||
return;
|
||||
}
|
||||
helpers_1.logger.info({ mint: rawAccount.mint }, `Send sell transaction attempt: ${i + 1}/${this.config.maxSellRetries}`);
|
||||
const result = yield this.swap(poolKeys, accountId, this.config.quoteAta, tokenIn, this.config.quoteToken, tokenAmountIn, this.config.sellSlippage, this.config.wallet, 'sell');
|
||||
if (result.confirmed) {
|
||||
helpers_1.logger.info({
|
||||
dex: `https://dexscreener.com/solana/${rawAccount.mint.toString()}?maker=${this.config.wallet.publicKey}`,
|
||||
mint: rawAccount.mint.toString(),
|
||||
signature: result.signature,
|
||||
url: `https://solscan.io/tx/${result.signature}?cluster=${helpers_1.NETWORK}`,
|
||||
}, `Confirmed sell tx`);
|
||||
break;
|
||||
}
|
||||
helpers_1.logger.info({
|
||||
mint: rawAccount.mint.toString(),
|
||||
signature: result.signature,
|
||||
error: result.error,
|
||||
}, `Error confirming sell tx`);
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.debug({ mint: rawAccount.mint.toString(), error }, `Error confirming sell transaction`);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.error({ mint: rawAccount.mint.toString(), error }, `Failed to sell token`);
|
||||
}
|
||||
finally {
|
||||
this.sellExecutionCount--;
|
||||
}
|
||||
});
|
||||
}
|
||||
// noinspection JSUnusedLocalSymbols
|
||||
swap(poolKeys, ataIn, ataOut, tokenIn, tokenOut, amountIn, slippage, wallet, direction) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const slippagePercent = new raydium_sdk_1.Percent(slippage, 100);
|
||||
const poolInfo = yield raydium_sdk_1.Liquidity.fetchInfo({
|
||||
connection: this.connection,
|
||||
poolKeys,
|
||||
});
|
||||
const computedAmountOut = raydium_sdk_1.Liquidity.computeAmountOut({
|
||||
poolKeys,
|
||||
poolInfo,
|
||||
amountIn,
|
||||
currencyOut: tokenOut,
|
||||
slippage: slippagePercent,
|
||||
});
|
||||
const latestBlockhash = yield this.connection.getLatestBlockhash();
|
||||
const { innerTransaction } = raydium_sdk_1.Liquidity.makeSwapFixedInInstruction({
|
||||
poolKeys: poolKeys,
|
||||
userKeys: {
|
||||
tokenAccountIn: ataIn,
|
||||
tokenAccountOut: ataOut,
|
||||
owner: wallet.publicKey,
|
||||
},
|
||||
amountIn: amountIn.raw,
|
||||
minAmountOut: computedAmountOut.minAmountOut.raw,
|
||||
}, poolKeys.version);
|
||||
const messageV0 = new web3_js_1.TransactionMessage({
|
||||
payerKey: wallet.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
...(this.isWarp || this.isJito
|
||||
? []
|
||||
: [
|
||||
web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: this.config.unitPrice }),
|
||||
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: this.config.unitLimit }),
|
||||
]),
|
||||
...(direction === 'buy'
|
||||
? [
|
||||
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, ataOut, wallet.publicKey, tokenOut.mint),
|
||||
]
|
||||
: []),
|
||||
...innerTransaction.instructions,
|
||||
...(direction === 'sell' ? [(0, spl_token_1.createCloseAccountInstruction)(ataIn, wallet.publicKey, wallet.publicKey)] : []),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
const transaction = new web3_js_1.VersionedTransaction(messageV0);
|
||||
transaction.sign([wallet, ...innerTransaction.signers]);
|
||||
return this.txExecutor.executeAndConfirm(transaction, wallet, latestBlockhash);
|
||||
});
|
||||
}
|
||||
filterMatch(poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.config.filterCheckInterval === 0 || this.config.filterCheckDuration === 0) {
|
||||
return true;
|
||||
}
|
||||
const filters = new filters_1.PoolFilters(this.connection, {
|
||||
quoteToken: this.config.quoteToken,
|
||||
minPoolSize: this.config.minPoolSize,
|
||||
maxPoolSize: this.config.maxPoolSize,
|
||||
});
|
||||
const timesToCheck = this.config.filterCheckDuration / this.config.filterCheckInterval;
|
||||
let timesChecked = 0;
|
||||
let matchCount = 0;
|
||||
do {
|
||||
try {
|
||||
const shouldBuy = yield filters.execute(poolKeys);
|
||||
if (shouldBuy) {
|
||||
matchCount++;
|
||||
if (this.config.consecutiveMatchCount <= matchCount) {
|
||||
helpers_1.logger.debug({ mint: poolKeys.baseMint.toString() }, `Filter match ${matchCount}/${this.config.consecutiveMatchCount}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
matchCount = 0;
|
||||
}
|
||||
yield (0, helpers_1.sleep)(this.config.filterCheckInterval);
|
||||
}
|
||||
finally {
|
||||
timesChecked++;
|
||||
}
|
||||
} while (timesChecked < timesToCheck);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
waitForSellSignal(amountIn, poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.config.priceCheckDuration === 0 || this.config.priceCheckInterval === 0) {
|
||||
return true;
|
||||
}
|
||||
const timesToCheck = this.config.priceCheckDuration / this.config.priceCheckInterval;
|
||||
const profitFraction = this.config.quoteAmount.mul(this.config.takeProfit).numerator.div(new bn_js_1.default(100));
|
||||
const profitAmount = new raydium_sdk_1.TokenAmount(this.config.quoteToken, profitFraction, true);
|
||||
const takeProfit = this.config.quoteAmount.add(profitAmount);
|
||||
let stopLoss;
|
||||
if (!this.stopLoss.get(poolKeys.baseMint.toString())) {
|
||||
const lossFraction = this.config.quoteAmount.mul(this.config.stopLoss).numerator.div(new bn_js_1.default(100));
|
||||
const lossAmount = new raydium_sdk_1.TokenAmount(this.config.quoteToken, lossFraction, true);
|
||||
stopLoss = this.config.quoteAmount.subtract(lossAmount);
|
||||
this.stopLoss.set(poolKeys.baseMint.toString(), stopLoss);
|
||||
}
|
||||
else {
|
||||
stopLoss = this.stopLoss.get(poolKeys.baseMint.toString());
|
||||
}
|
||||
const slippage = new raydium_sdk_1.Percent(this.config.sellSlippage, 100);
|
||||
let timesChecked = 0;
|
||||
do {
|
||||
try {
|
||||
const poolInfo = yield raydium_sdk_1.Liquidity.fetchInfo({
|
||||
connection: this.connection,
|
||||
poolKeys,
|
||||
});
|
||||
const amountOut = raydium_sdk_1.Liquidity.computeAmountOut({
|
||||
poolKeys,
|
||||
poolInfo,
|
||||
amountIn: amountIn,
|
||||
currencyOut: this.config.quoteToken,
|
||||
slippage,
|
||||
}).amountOut;
|
||||
if (this.config.trailingStopLoss) {
|
||||
const trailingLossFraction = amountOut.mul(this.config.stopLoss).numerator.div(new bn_js_1.default(100));
|
||||
const trailingLossAmount = new raydium_sdk_1.TokenAmount(this.config.quoteToken, trailingLossFraction, true);
|
||||
const trailingStopLoss = amountOut.subtract(trailingLossAmount);
|
||||
if (trailingStopLoss.gt(stopLoss)) {
|
||||
helpers_1.logger.trace({ mint: poolKeys.baseMint.toString() }, `Updating trailing stop loss from ${stopLoss.toFixed()} to ${trailingStopLoss.toFixed()}`);
|
||||
this.stopLoss.set(poolKeys.baseMint.toString(), trailingStopLoss);
|
||||
stopLoss = trailingStopLoss;
|
||||
}
|
||||
}
|
||||
if (this.config.skipSellingIfLostMoreThan > 0) {
|
||||
const stopSellingFraction = this.config.quoteAmount
|
||||
.mul(this.config.skipSellingIfLostMoreThan)
|
||||
.numerator.div(new bn_js_1.default(100));
|
||||
const stopSellingAmount = new raydium_sdk_1.TokenAmount(this.config.quoteToken, stopSellingFraction, true);
|
||||
if (amountOut.lt(stopSellingAmount)) {
|
||||
helpers_1.logger.debug({ mint: poolKeys.baseMint.toString() }, `Token dropped more than ${this.config.skipSellingIfLostMoreThan}%, sell stopped. Initial: ${this.config.quoteAmount.toFixed()} | Current: ${amountOut.toFixed()}`);
|
||||
this.stopLoss.delete(poolKeys.baseMint.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
helpers_1.logger.debug({ mint: poolKeys.baseMint.toString() }, `Take profit: ${takeProfit.toFixed()} | Stop loss: ${stopLoss.toFixed()} | Current: ${amountOut.toFixed()}`);
|
||||
if (amountOut.lt(stopLoss)) {
|
||||
this.stopLoss.delete(poolKeys.baseMint.toString());
|
||||
break;
|
||||
}
|
||||
if (amountOut.gt(takeProfit)) {
|
||||
this.stopLoss.delete(poolKeys.baseMint.toString());
|
||||
break;
|
||||
}
|
||||
yield (0, helpers_1.sleep)(this.config.priceCheckInterval);
|
||||
}
|
||||
catch (e) {
|
||||
helpers_1.logger.trace({ mint: poolKeys.baseMint.toString(), e }, `Failed to check token price`);
|
||||
}
|
||||
finally {
|
||||
timesChecked++;
|
||||
}
|
||||
} while (timesChecked < timesToCheck);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.Bot = Bot;
|
||||
19
cache/index.js
vendored
Normal file
19
cache/index.js
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./market.cache"), exports);
|
||||
__exportStar(require("./pool.cache"), exports);
|
||||
__exportStar(require("./snipe-list.cache"), exports);
|
||||
6
cache/index.ts
vendored
6
cache/index.ts
vendored
@ -1,3 +1,3 @@
|
||||
export * from './market.cache';
|
||||
export * from './pool.cache';
|
||||
export * from './snipe-list.cache';
|
||||
export * from "./market.cache";
|
||||
export * from "./pool.cache";
|
||||
export * from "./snipe-list.cache";
|
||||
|
||||
68
cache/market.cache.js
vendored
Normal file
68
cache/market.cache.js
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MarketCache = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const helpers_1 = require("../helpers");
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
class MarketCache {
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.keys = new Map();
|
||||
}
|
||||
init(config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
helpers_1.logger.debug({}, `Fetching all existing ${config.quoteToken.symbol} markets...`);
|
||||
const accounts = yield this.connection.getProgramAccounts(raydium_sdk_1.MAINNET_PROGRAM_ID.OPENBOOK_MARKET, {
|
||||
commitment: this.connection.commitment,
|
||||
dataSlice: {
|
||||
offset: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.offsetOf("eventQueue"),
|
||||
length: helpers_1.MINIMAL_MARKET_STATE_LAYOUT_V3.span,
|
||||
},
|
||||
filters: [
|
||||
{ dataSize: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
for (const account of accounts) {
|
||||
const market = helpers_1.MINIMAL_MARKET_STATE_LAYOUT_V3.decode(account.account.data);
|
||||
this.keys.set(account.pubkey.toString(), market);
|
||||
}
|
||||
helpers_1.logger.debug({}, `Cached ${this.keys.size} markets`);
|
||||
});
|
||||
}
|
||||
save(marketId, keys) {
|
||||
if (!this.keys.has(marketId)) {
|
||||
helpers_1.logger.trace({}, `Caching new market: ${marketId}`);
|
||||
this.keys.set(marketId, keys);
|
||||
}
|
||||
}
|
||||
get(marketId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.keys.has(marketId)) {
|
||||
return this.keys.get(marketId);
|
||||
}
|
||||
helpers_1.logger.trace({}, `Fetching new market keys for ${marketId}`);
|
||||
const market = yield this.fetch(marketId);
|
||||
this.keys.set(marketId, market);
|
||||
return market;
|
||||
});
|
||||
}
|
||||
fetch(marketId) {
|
||||
return (0, helpers_1.getMinimalMarketV3)(this.connection, new web3_js_1.PublicKey(marketId), this.connection.commitment);
|
||||
}
|
||||
}
|
||||
exports.MarketCache = MarketCache;
|
||||
94
cache/market.cache.ts
vendored
94
cache/market.cache.ts
vendored
@ -1,58 +1,58 @@
|
||||
import { Connection, PublicKey } from '@solana/web3.js';
|
||||
import { getMinimalMarketV3, logger, MINIMAL_MARKET_STATE_LAYOUT_V3, MinimalMarketLayoutV3 } from '../helpers';
|
||||
import { MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from '@raydium-io/raydium-sdk';
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { getMinimalMarketV3, logger, MINIMAL_MARKET_STATE_LAYOUT_V3, MinimalMarketLayoutV3 } from "../helpers";
|
||||
import { MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from "@raydium-io/raydium-sdk";
|
||||
|
||||
export class MarketCache {
|
||||
private readonly keys: Map<string, MinimalMarketLayoutV3> = new Map<string, MinimalMarketLayoutV3>();
|
||||
constructor(private readonly connection: Connection) {}
|
||||
private readonly keys: Map<string, MinimalMarketLayoutV3> = new Map<string, MinimalMarketLayoutV3>();
|
||||
constructor(private readonly connection: Connection) {}
|
||||
|
||||
async init(config: { quoteToken: Token }) {
|
||||
logger.debug({}, `Fetching all existing ${config.quoteToken.symbol} markets...`);
|
||||
async init(config: { quoteToken: Token }) {
|
||||
logger.debug({}, `Fetching all existing ${config.quoteToken.symbol} markets...`);
|
||||
|
||||
const accounts = await this.connection.getProgramAccounts(MAINNET_PROGRAM_ID.OPENBOOK_MARKET, {
|
||||
commitment: this.connection.commitment,
|
||||
dataSlice: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'),
|
||||
length: MINIMAL_MARKET_STATE_LAYOUT_V3.span,
|
||||
},
|
||||
filters: [
|
||||
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const accounts = await this.connection.getProgramAccounts(MAINNET_PROGRAM_ID.OPENBOOK_MARKET, {
|
||||
commitment: this.connection.commitment,
|
||||
dataSlice: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf("eventQueue"),
|
||||
length: MINIMAL_MARKET_STATE_LAYOUT_V3.span,
|
||||
},
|
||||
filters: [
|
||||
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
for (const account of accounts) {
|
||||
const market = MINIMAL_MARKET_STATE_LAYOUT_V3.decode(account.account.data);
|
||||
this.keys.set(account.pubkey.toString(), market);
|
||||
}
|
||||
for (const account of accounts) {
|
||||
const market = MINIMAL_MARKET_STATE_LAYOUT_V3.decode(account.account.data);
|
||||
this.keys.set(account.pubkey.toString(), market);
|
||||
}
|
||||
|
||||
logger.debug({}, `Cached ${this.keys.size} markets`);
|
||||
}
|
||||
logger.debug({}, `Cached ${this.keys.size} markets`);
|
||||
}
|
||||
|
||||
public save(marketId: string, keys: MinimalMarketLayoutV3) {
|
||||
if (!this.keys.has(marketId)) {
|
||||
logger.trace({}, `Caching new market: ${marketId}`);
|
||||
this.keys.set(marketId, keys);
|
||||
}
|
||||
}
|
||||
public save(marketId: string, keys: MinimalMarketLayoutV3) {
|
||||
if (!this.keys.has(marketId)) {
|
||||
logger.trace({}, `Caching new market: ${marketId}`);
|
||||
this.keys.set(marketId, keys);
|
||||
}
|
||||
}
|
||||
|
||||
public async get(marketId: string): Promise<MinimalMarketLayoutV3> {
|
||||
if (this.keys.has(marketId)) {
|
||||
return this.keys.get(marketId)!;
|
||||
}
|
||||
public async get(marketId: string): Promise<MinimalMarketLayoutV3> {
|
||||
if (this.keys.has(marketId)) {
|
||||
return this.keys.get(marketId)!;
|
||||
}
|
||||
|
||||
logger.trace({}, `Fetching new market keys for ${marketId}`);
|
||||
const market = await this.fetch(marketId);
|
||||
this.keys.set(marketId, market);
|
||||
return market;
|
||||
}
|
||||
logger.trace({}, `Fetching new market keys for ${marketId}`);
|
||||
const market = await this.fetch(marketId);
|
||||
this.keys.set(marketId, market);
|
||||
return market;
|
||||
}
|
||||
|
||||
private fetch(marketId: string): Promise<MinimalMarketLayoutV3> {
|
||||
return getMinimalMarketV3(this.connection, new PublicKey(marketId), this.connection.commitment);
|
||||
}
|
||||
private fetch(marketId: string): Promise<MinimalMarketLayoutV3> {
|
||||
return getMinimalMarketV3(this.connection, new PublicKey(marketId), this.connection.commitment);
|
||||
}
|
||||
}
|
||||
|
||||
12
cache/optimize.js
vendored
Normal file
12
cache/optimize.js
vendored
Normal file
File diff suppressed because one or more lines are too long
30
cache/pool.cache.js
vendored
Normal file
30
cache/pool.cache.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PoolCache = void 0;
|
||||
const helpers_1 = require("../helpers");
|
||||
class PoolCache {
|
||||
constructor() {
|
||||
this.keys = new Map();
|
||||
}
|
||||
save(id, state) {
|
||||
if (!this.keys.has(state.baseMint.toString())) {
|
||||
helpers_1.logger.trace(`Caching new pool for mint: ${state.baseMint.toString()}`);
|
||||
this.keys.set(state.baseMint.toString(), { id, state });
|
||||
}
|
||||
}
|
||||
get(mint) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.keys.get(mint);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.PoolCache = PoolCache;
|
||||
30
cache/pool.cache.ts
vendored
30
cache/pool.cache.ts
vendored
@ -1,20 +1,20 @@
|
||||
import { LiquidityStateV4 } from '@raydium-io/raydium-sdk';
|
||||
import { logger } from '../helpers';
|
||||
import { LiquidityStateV4 } from "@raydium-io/raydium-sdk";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class PoolCache {
|
||||
private readonly keys: Map<string, { id: string; state: LiquidityStateV4 }> = new Map<
|
||||
string,
|
||||
{ id: string; state: LiquidityStateV4 }
|
||||
>();
|
||||
private readonly keys: Map<string, { id: string; state: LiquidityStateV4 }> = new Map<
|
||||
string,
|
||||
{ id: string; state: LiquidityStateV4 }
|
||||
>();
|
||||
|
||||
public save(id: string, state: LiquidityStateV4) {
|
||||
if (!this.keys.has(state.baseMint.toString())) {
|
||||
logger.trace(`Caching new pool for mint: ${state.baseMint.toString()}`);
|
||||
this.keys.set(state.baseMint.toString(), { id, state });
|
||||
}
|
||||
}
|
||||
public save(id: string, state: LiquidityStateV4) {
|
||||
if (!this.keys.has(state.baseMint.toString())) {
|
||||
logger.trace(`Caching new pool for mint: ${state.baseMint.toString()}`);
|
||||
this.keys.set(state.baseMint.toString(), { id, state });
|
||||
}
|
||||
}
|
||||
|
||||
public async get(mint: string): Promise<{ id: string; state: LiquidityStateV4 }> {
|
||||
return this.keys.get(mint)!;
|
||||
}
|
||||
public async get(mint: string): Promise<{ id: string; state: LiquidityStateV4 }> {
|
||||
return this.keys.get(mint)!;
|
||||
}
|
||||
}
|
||||
|
||||
35
cache/snipe-list.cache.js
vendored
Normal file
35
cache/snipe-list.cache.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SnipeListCache = void 0;
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const helpers_1 = require("../helpers");
|
||||
class SnipeListCache {
|
||||
constructor() {
|
||||
this.snipeList = [];
|
||||
this.fileLocation = path_1.default.join(__dirname, "../snipe-list.txt");
|
||||
setInterval(() => this.loadSnipeList(), helpers_1.SNIPE_LIST_REFRESH_INTERVAL);
|
||||
}
|
||||
init() {
|
||||
this.loadSnipeList();
|
||||
}
|
||||
isInList(mint) {
|
||||
return this.snipeList.includes(mint);
|
||||
}
|
||||
loadSnipeList() {
|
||||
helpers_1.logger.trace(`Refreshing snipe list...`);
|
||||
const count = this.snipeList.length;
|
||||
const data = fs_1.default.readFileSync(this.fileLocation, "utf-8");
|
||||
this.snipeList = data
|
||||
.split("\n")
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a);
|
||||
if (this.snipeList.length != count) {
|
||||
helpers_1.logger.info(`Loaded snipe list: ${this.snipeList.length}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.SnipeListCache = SnipeListCache;
|
||||
52
cache/snipe-list.cache.ts
vendored
52
cache/snipe-list.cache.ts
vendored
@ -1,35 +1,35 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { logger, SNIPE_LIST_REFRESH_INTERVAL } from '../helpers';
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { logger, SNIPE_LIST_REFRESH_INTERVAL } from "../helpers";
|
||||
|
||||
export class SnipeListCache {
|
||||
private snipeList: string[] = [];
|
||||
private fileLocation = path.join(__dirname, '../snipe-list.txt');
|
||||
private snipeList: string[] = [];
|
||||
private fileLocation = path.join(__dirname, "../snipe-list.txt");
|
||||
|
||||
constructor() {
|
||||
setInterval(() => this.loadSnipeList(), SNIPE_LIST_REFRESH_INTERVAL);
|
||||
}
|
||||
constructor() {
|
||||
setInterval(() => this.loadSnipeList(), SNIPE_LIST_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
public init() {
|
||||
this.loadSnipeList();
|
||||
}
|
||||
public init() {
|
||||
this.loadSnipeList();
|
||||
}
|
||||
|
||||
public isInList(mint: string) {
|
||||
return this.snipeList.includes(mint);
|
||||
}
|
||||
public isInList(mint: string) {
|
||||
return this.snipeList.includes(mint);
|
||||
}
|
||||
|
||||
private loadSnipeList() {
|
||||
logger.trace(`Refreshing snipe list...`);
|
||||
private loadSnipeList() {
|
||||
logger.trace(`Refreshing snipe list...`);
|
||||
|
||||
const count = this.snipeList.length;
|
||||
const data = fs.readFileSync(this.fileLocation, 'utf-8');
|
||||
this.snipeList = data
|
||||
.split('\n')
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a);
|
||||
const count = this.snipeList.length;
|
||||
const data = fs.readFileSync(this.fileLocation, "utf-8");
|
||||
this.snipeList = data
|
||||
.split("\n")
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a);
|
||||
|
||||
if (this.snipeList.length != count) {
|
||||
logger.info(`Loaded snipe list: ${this.snipeList.length}`);
|
||||
}
|
||||
}
|
||||
if (this.snipeList.length != count) {
|
||||
logger.info(`Loaded snipe list: ${this.snipeList.length}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
filters/burn.filter.js
Normal file
43
filters/burn.filter.js
Normal file
@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BurnFilter = void 0;
|
||||
const helpers_1 = require("../helpers");
|
||||
class BurnFilter {
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.cachedResult = undefined;
|
||||
}
|
||||
execute(poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
try {
|
||||
const amount = yield this.connection.getTokenSupply(poolKeys.lpMint, this.connection.commitment);
|
||||
const burned = amount.value.uiAmount === 0;
|
||||
const result = { ok: burned, message: burned ? undefined : "Burned -> Creator didn't burn LP" };
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code == -32602) {
|
||||
return { ok: true };
|
||||
}
|
||||
helpers_1.logger.error({ mint: poolKeys.baseMint }, `Failed to check if LP is burned`);
|
||||
}
|
||||
return { ok: false, message: "Failed to check if LP is burned" };
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.BurnFilter = BurnFilter;
|
||||
@ -1,36 +1,36 @@
|
||||
import { Filter, FilterResult } from './pool-filters';
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { LiquidityPoolKeysV4 } from '@raydium-io/raydium-sdk';
|
||||
import { logger } from '../helpers';
|
||||
import { Filter, FilterResult } from "./pool-filters";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { LiquidityPoolKeysV4 } from "@raydium-io/raydium-sdk";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class BurnFilter implements Filter {
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
|
||||
constructor(private readonly connection: Connection) {}
|
||||
constructor(private readonly connection: Connection) {}
|
||||
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
const amount = await this.connection.getTokenSupply(poolKeys.lpMint, this.connection.commitment);
|
||||
const burned = amount.value.uiAmount === 0;
|
||||
const result = { ok: burned, message: burned ? undefined : "Burned -> Creator didn't burn LP" };
|
||||
try {
|
||||
const amount = await this.connection.getTokenSupply(poolKeys.lpMint, this.connection.commitment);
|
||||
const burned = amount.value.uiAmount === 0;
|
||||
const result = { ok: burned, message: burned ? undefined : "Burned -> Creator didn't burn LP" };
|
||||
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e: any) {
|
||||
if (e.code == -32602) {
|
||||
return { ok: true };
|
||||
}
|
||||
return result;
|
||||
} catch (e: any) {
|
||||
if (e.code == -32602) {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
logger.error({ mint: poolKeys.baseMint }, `Failed to check if LP is burned`);
|
||||
}
|
||||
logger.error({ mint: poolKeys.baseMint }, `Failed to check if LP is burned`);
|
||||
}
|
||||
|
||||
return { ok: false, message: 'Failed to check if LP is burned' };
|
||||
}
|
||||
return { ok: false, message: "Failed to check if LP is burned" };
|
||||
}
|
||||
}
|
||||
|
||||
21
filters/index.js
Normal file
21
filters/index.js
Normal file
@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./burn.filter"), exports);
|
||||
__exportStar(require("./mutable.filter"), exports);
|
||||
__exportStar(require("./pool-filters"), exports);
|
||||
__exportStar(require("./pool-size.filter"), exports);
|
||||
__exportStar(require("./renounced.filter"), exports);
|
||||
@ -1,5 +1,5 @@
|
||||
export * from './burn.filter';
|
||||
export * from './mutable.filter';
|
||||
export * from './pool-filters';
|
||||
export * from './pool-size.filter';
|
||||
export * from './renounced.filter';
|
||||
export * from "./burn.filter";
|
||||
export * from "./mutable.filter";
|
||||
export * from "./pool-filters";
|
||||
export * from "./pool-size.filter";
|
||||
export * from "./renounced.filter";
|
||||
|
||||
76
filters/mutable.filter.js
Normal file
76
filters/mutable.filter.js
Normal file
@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MutableFilter = void 0;
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const helpers_1 = require("../helpers");
|
||||
class MutableFilter {
|
||||
constructor(connection, metadataSerializer, checkMutable, checkSocials) {
|
||||
this.connection = connection;
|
||||
this.metadataSerializer = metadataSerializer;
|
||||
this.checkMutable = checkMutable;
|
||||
this.checkSocials = checkSocials;
|
||||
this.errorMessage = [];
|
||||
this.cachedResult = undefined;
|
||||
if (this.checkMutable) {
|
||||
this.errorMessage.push("mutable");
|
||||
}
|
||||
if (this.checkSocials) {
|
||||
this.errorMessage.push("socials");
|
||||
}
|
||||
}
|
||||
execute(poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
try {
|
||||
const metadataPDA = (0, raydium_sdk_1.getPdaMetadataKey)(poolKeys.baseMint);
|
||||
const metadataAccount = yield this.connection.getAccountInfo(metadataPDA.publicKey, this.connection.commitment);
|
||||
if (!(metadataAccount === null || metadataAccount === void 0 ? void 0 : metadataAccount.data)) {
|
||||
return { ok: false, message: "Mutable -> Failed to fetch account data" };
|
||||
}
|
||||
const deserialize = this.metadataSerializer.deserialize(metadataAccount.data);
|
||||
const mutable = !this.checkMutable || deserialize[0].isMutable;
|
||||
const hasSocials = !this.checkSocials || (yield this.hasSocials(deserialize[0]));
|
||||
const ok = !mutable && hasSocials;
|
||||
const message = [];
|
||||
if (mutable) {
|
||||
message.push("metadata can be changed");
|
||||
}
|
||||
if (!hasSocials) {
|
||||
message.push("has no socials");
|
||||
}
|
||||
const result = { ok: ok, message: ok ? undefined : `MutableSocials -> Token ${message.join(" and ")}` };
|
||||
if (!mutable) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
helpers_1.logger.error({ mint: poolKeys.baseMint }, `MutableSocials -> Failed to check ${this.errorMessage.join(" and ")}`);
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
message: `MutableSocials -> Failed to check ${this.errorMessage.join(" and ")}`,
|
||||
};
|
||||
});
|
||||
}
|
||||
hasSocials(metadata) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const response = yield fetch(metadata.uri);
|
||||
const data = yield response.json();
|
||||
return Object.values((_a = data === null || data === void 0 ? void 0 : data.extensions) !== null && _a !== void 0 ? _a : {}).filter((value) => value).length > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.MutableFilter = MutableFilter;
|
||||
@ -1,77 +1,77 @@
|
||||
import { Filter, FilterResult } from './pool-filters';
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { LiquidityPoolKeysV4 } from '@raydium-io/raydium-sdk';
|
||||
import { getPdaMetadataKey } from '@raydium-io/raydium-sdk';
|
||||
import { MetadataAccountData, MetadataAccountDataArgs } from '@metaplex-foundation/mpl-token-metadata';
|
||||
import { Serializer } from '@metaplex-foundation/umi/serializers';
|
||||
import { logger } from '../helpers';
|
||||
import { Filter, FilterResult } from "./pool-filters";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { LiquidityPoolKeysV4 } from "@raydium-io/raydium-sdk";
|
||||
import { getPdaMetadataKey } from "@raydium-io/raydium-sdk";
|
||||
import { MetadataAccountData, MetadataAccountDataArgs } from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { Serializer } from "@metaplex-foundation/umi/serializers";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class MutableFilter implements Filter {
|
||||
private readonly errorMessage: string[] = [];
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
private readonly errorMessage: string[] = [];
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly metadataSerializer: Serializer<MetadataAccountDataArgs, MetadataAccountData>,
|
||||
private readonly checkMutable: boolean,
|
||||
private readonly checkSocials: boolean,
|
||||
) {
|
||||
if (this.checkMutable) {
|
||||
this.errorMessage.push('mutable');
|
||||
}
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly metadataSerializer: Serializer<MetadataAccountDataArgs, MetadataAccountData>,
|
||||
private readonly checkMutable: boolean,
|
||||
private readonly checkSocials: boolean,
|
||||
) {
|
||||
if (this.checkMutable) {
|
||||
this.errorMessage.push("mutable");
|
||||
}
|
||||
|
||||
if (this.checkSocials) {
|
||||
this.errorMessage.push('socials');
|
||||
}
|
||||
}
|
||||
if (this.checkSocials) {
|
||||
this.errorMessage.push("socials");
|
||||
}
|
||||
}
|
||||
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
const metadataPDA = getPdaMetadataKey(poolKeys.baseMint);
|
||||
const metadataAccount = await this.connection.getAccountInfo(metadataPDA.publicKey, this.connection.commitment);
|
||||
try {
|
||||
const metadataPDA = getPdaMetadataKey(poolKeys.baseMint);
|
||||
const metadataAccount = await this.connection.getAccountInfo(metadataPDA.publicKey, this.connection.commitment);
|
||||
|
||||
if (!metadataAccount?.data) {
|
||||
return { ok: false, message: 'Mutable -> Failed to fetch account data' };
|
||||
}
|
||||
if (!metadataAccount?.data) {
|
||||
return { ok: false, message: "Mutable -> Failed to fetch account data" };
|
||||
}
|
||||
|
||||
const deserialize = this.metadataSerializer.deserialize(metadataAccount.data);
|
||||
const mutable = !this.checkMutable || deserialize[0].isMutable;
|
||||
const hasSocials = !this.checkSocials || (await this.hasSocials(deserialize[0]));
|
||||
const ok = !mutable && hasSocials;
|
||||
const message: string[] = [];
|
||||
const deserialize = this.metadataSerializer.deserialize(metadataAccount.data);
|
||||
const mutable = !this.checkMutable || deserialize[0].isMutable;
|
||||
const hasSocials = !this.checkSocials || (await this.hasSocials(deserialize[0]));
|
||||
const ok = !mutable && hasSocials;
|
||||
const message: string[] = [];
|
||||
|
||||
if (mutable) {
|
||||
message.push('metadata can be changed');
|
||||
}
|
||||
if (mutable) {
|
||||
message.push("metadata can be changed");
|
||||
}
|
||||
|
||||
if (!hasSocials) {
|
||||
message.push('has no socials');
|
||||
}
|
||||
if (!hasSocials) {
|
||||
message.push("has no socials");
|
||||
}
|
||||
|
||||
const result = { ok: ok, message: ok ? undefined : `MutableSocials -> Token ${message.join(' and ')}` };
|
||||
const result = { ok: ok, message: ok ? undefined : `MutableSocials -> Token ${message.join(" and ")}` };
|
||||
|
||||
if (!mutable) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
if (!mutable) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
logger.error({ mint: poolKeys.baseMint }, `MutableSocials -> Failed to check ${this.errorMessage.join(' and ')}`);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
logger.error({ mint: poolKeys.baseMint }, `MutableSocials -> Failed to check ${this.errorMessage.join(" and ")}`);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
message: `MutableSocials -> Failed to check ${this.errorMessage.join(' and ')}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
message: `MutableSocials -> Failed to check ${this.errorMessage.join(" and ")}`,
|
||||
};
|
||||
}
|
||||
|
||||
private async hasSocials(metadata: MetadataAccountData) {
|
||||
const response = await fetch(metadata.uri);
|
||||
const data = await response.json();
|
||||
return Object.values(data?.extensions ?? {}).filter((value: any) => value).length > 0;
|
||||
}
|
||||
private async hasSocials(metadata: MetadataAccountData) {
|
||||
const response = await fetch(metadata.uri);
|
||||
const data = await response.json();
|
||||
return Object.values(data?.extensions ?? {}).filter((value: any) => value).length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
54
filters/pool-filters.js
Normal file
54
filters/pool-filters.js
Normal file
@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PoolFilters = void 0;
|
||||
const mpl_token_metadata_1 = require("@metaplex-foundation/mpl-token-metadata");
|
||||
const burn_filter_1 = require("./burn.filter");
|
||||
const mutable_filter_1 = require("./mutable.filter");
|
||||
const renounced_filter_1 = require("./renounced.filter");
|
||||
const pool_size_filter_1 = require("./pool-size.filter");
|
||||
const helpers_1 = require("../helpers");
|
||||
class PoolFilters {
|
||||
constructor(connection, args) {
|
||||
this.connection = connection;
|
||||
this.args = args;
|
||||
this.filters = [];
|
||||
if (helpers_1.CHECK_IF_BURNED) {
|
||||
this.filters.push(new burn_filter_1.BurnFilter(connection));
|
||||
}
|
||||
if (helpers_1.CHECK_IF_MINT_IS_RENOUNCED || helpers_1.CHECK_IF_FREEZABLE) {
|
||||
this.filters.push(new renounced_filter_1.RenouncedFreezeFilter(connection, helpers_1.CHECK_IF_MINT_IS_RENOUNCED, helpers_1.CHECK_IF_FREEZABLE));
|
||||
}
|
||||
if (helpers_1.CHECK_IF_MUTABLE || helpers_1.CHECK_IF_SOCIALS) {
|
||||
this.filters.push(new mutable_filter_1.MutableFilter(connection, (0, mpl_token_metadata_1.getMetadataAccountDataSerializer)(), helpers_1.CHECK_IF_MUTABLE, helpers_1.CHECK_IF_SOCIALS));
|
||||
}
|
||||
if (!args.minPoolSize.isZero() || !args.maxPoolSize.isZero()) {
|
||||
this.filters.push(new pool_size_filter_1.PoolSizeFilter(connection, args.quoteToken, args.minPoolSize, args.maxPoolSize));
|
||||
}
|
||||
}
|
||||
execute(poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.filters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const result = yield Promise.all(this.filters.map((f) => f.execute(poolKeys)));
|
||||
const pass = result.every((r) => r.ok);
|
||||
if (pass) {
|
||||
return true;
|
||||
}
|
||||
for (const filterResult of result.filter((r) => !r.ok)) {
|
||||
helpers_1.logger.trace(filterResult.message);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.PoolFilters = PoolFilters;
|
||||
@ -1,67 +1,76 @@
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { LiquidityPoolKeysV4, Token, TokenAmount } from '@raydium-io/raydium-sdk';
|
||||
import { getMetadataAccountDataSerializer } from '@metaplex-foundation/mpl-token-metadata';
|
||||
import { BurnFilter } from './burn.filter';
|
||||
import { MutableFilter } from './mutable.filter';
|
||||
import { RenouncedFreezeFilter } from './renounced.filter';
|
||||
import { PoolSizeFilter } from './pool-size.filter';
|
||||
import { CHECK_IF_BURNED, CHECK_IF_FREEZABLE, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_MUTABLE, CHECK_IF_SOCIALS, logger } from '../helpers';
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { LiquidityPoolKeysV4, Token, TokenAmount } from "@raydium-io/raydium-sdk";
|
||||
import { getMetadataAccountDataSerializer } from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { BurnFilter } from "./burn.filter";
|
||||
import { MutableFilter } from "./mutable.filter";
|
||||
import { RenouncedFreezeFilter } from "./renounced.filter";
|
||||
import { PoolSizeFilter } from "./pool-size.filter";
|
||||
import {
|
||||
CHECK_IF_BURNED,
|
||||
CHECK_IF_FREEZABLE,
|
||||
CHECK_IF_MINT_IS_RENOUNCED,
|
||||
CHECK_IF_MUTABLE,
|
||||
CHECK_IF_SOCIALS,
|
||||
logger,
|
||||
} from "../helpers";
|
||||
|
||||
export interface Filter {
|
||||
execute(poolKeysV4: LiquidityPoolKeysV4): Promise<FilterResult>;
|
||||
execute(poolKeysV4: LiquidityPoolKeysV4): Promise<FilterResult>;
|
||||
}
|
||||
|
||||
export interface FilterResult {
|
||||
ok: boolean;
|
||||
message?: string;
|
||||
ok: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface PoolFilterArgs {
|
||||
minPoolSize: TokenAmount;
|
||||
maxPoolSize: TokenAmount;
|
||||
quoteToken: Token;
|
||||
minPoolSize: TokenAmount;
|
||||
maxPoolSize: TokenAmount;
|
||||
quoteToken: Token;
|
||||
}
|
||||
|
||||
export class PoolFilters {
|
||||
private readonly filters: Filter[] = [];
|
||||
private readonly filters: Filter[] = [];
|
||||
|
||||
constructor(
|
||||
readonly connection: Connection,
|
||||
readonly args: PoolFilterArgs,
|
||||
) {
|
||||
if (CHECK_IF_BURNED) {
|
||||
this.filters.push(new BurnFilter(connection));
|
||||
}
|
||||
constructor(
|
||||
readonly connection: Connection,
|
||||
readonly args: PoolFilterArgs,
|
||||
) {
|
||||
if (CHECK_IF_BURNED) {
|
||||
this.filters.push(new BurnFilter(connection));
|
||||
}
|
||||
|
||||
if (CHECK_IF_MINT_IS_RENOUNCED || CHECK_IF_FREEZABLE) {
|
||||
this.filters.push(new RenouncedFreezeFilter(connection, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_FREEZABLE));
|
||||
}
|
||||
if (CHECK_IF_MINT_IS_RENOUNCED || CHECK_IF_FREEZABLE) {
|
||||
this.filters.push(new RenouncedFreezeFilter(connection, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_FREEZABLE));
|
||||
}
|
||||
|
||||
if (CHECK_IF_MUTABLE || CHECK_IF_SOCIALS) {
|
||||
this.filters.push(new MutableFilter(connection, getMetadataAccountDataSerializer(), CHECK_IF_MUTABLE, CHECK_IF_SOCIALS));
|
||||
}
|
||||
if (CHECK_IF_MUTABLE || CHECK_IF_SOCIALS) {
|
||||
this.filters.push(
|
||||
new MutableFilter(connection, getMetadataAccountDataSerializer(), CHECK_IF_MUTABLE, CHECK_IF_SOCIALS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!args.minPoolSize.isZero() || !args.maxPoolSize.isZero()) {
|
||||
this.filters.push(new PoolSizeFilter(connection, args.quoteToken, args.minPoolSize, args.maxPoolSize));
|
||||
}
|
||||
}
|
||||
if (!args.minPoolSize.isZero() || !args.maxPoolSize.isZero()) {
|
||||
this.filters.push(new PoolSizeFilter(connection, args.quoteToken, args.minPoolSize, args.maxPoolSize));
|
||||
}
|
||||
}
|
||||
|
||||
public async execute(poolKeys: LiquidityPoolKeysV4): Promise<boolean> {
|
||||
if (this.filters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
public async execute(poolKeys: LiquidityPoolKeysV4): Promise<boolean> {
|
||||
if (this.filters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = await Promise.all(this.filters.map((f) => f.execute(poolKeys)));
|
||||
const pass = result.every((r) => r.ok);
|
||||
const result = await Promise.all(this.filters.map((f) => f.execute(poolKeys)));
|
||||
const pass = result.every((r) => r.ok);
|
||||
|
||||
if (pass) {
|
||||
return true;
|
||||
}
|
||||
if (pass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const filterResult of result.filter((r) => !r.ok)) {
|
||||
logger.trace(filterResult.message);
|
||||
}
|
||||
for (const filterResult of result.filter((r) => !r.ok)) {
|
||||
logger.trace(filterResult.message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
50
filters/pool-size.filter.js
Normal file
50
filters/pool-size.filter.js
Normal file
@ -0,0 +1,50 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PoolSizeFilter = void 0;
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const helpers_1 = require("../helpers");
|
||||
class PoolSizeFilter {
|
||||
constructor(connection, quoteToken, minPoolSize, maxPoolSize) {
|
||||
this.connection = connection;
|
||||
this.quoteToken = quoteToken;
|
||||
this.minPoolSize = minPoolSize;
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
execute(poolKeys) {
|
||||
var _a, _b;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
const response = yield this.connection.getTokenAccountBalance(poolKeys.quoteVault, this.connection.commitment);
|
||||
const poolSize = new raydium_sdk_1.TokenAmount(this.quoteToken, response.value.amount, true);
|
||||
let inRange = true;
|
||||
if (!((_a = this.maxPoolSize) === null || _a === void 0 ? void 0 : _a.isZero())) {
|
||||
inRange = poolSize.raw.lte(this.maxPoolSize.raw);
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} > ${this.maxPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
if (!((_b = this.minPoolSize) === null || _b === void 0 ? void 0 : _b.isZero())) {
|
||||
inRange = poolSize.raw.gte(this.minPoolSize.raw);
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} < ${this.minPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
return { ok: inRange };
|
||||
}
|
||||
catch (error) {
|
||||
helpers_1.logger.error({ mint: poolKeys.baseMint }, `Failed to check pool size`);
|
||||
}
|
||||
return { ok: false, message: "PoolSize -> Failed to check pool size" };
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.PoolSizeFilter = PoolSizeFilter;
|
||||
@ -1,43 +1,43 @@
|
||||
import { Filter, FilterResult } from './pool-filters';
|
||||
import { LiquidityPoolKeysV4, Token, TokenAmount } from '@raydium-io/raydium-sdk';
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { logger } from '../helpers';
|
||||
import { Filter, FilterResult } from "./pool-filters";
|
||||
import { LiquidityPoolKeysV4, Token, TokenAmount } from "@raydium-io/raydium-sdk";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class PoolSizeFilter implements Filter {
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly quoteToken: Token,
|
||||
private readonly minPoolSize: TokenAmount,
|
||||
private readonly maxPoolSize: TokenAmount,
|
||||
) {}
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly quoteToken: Token,
|
||||
private readonly minPoolSize: TokenAmount,
|
||||
private readonly maxPoolSize: TokenAmount,
|
||||
) {}
|
||||
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
try {
|
||||
const response = await this.connection.getTokenAccountBalance(poolKeys.quoteVault, this.connection.commitment);
|
||||
const poolSize = new TokenAmount(this.quoteToken, response.value.amount, true);
|
||||
let inRange = true;
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
try {
|
||||
const response = await this.connection.getTokenAccountBalance(poolKeys.quoteVault, this.connection.commitment);
|
||||
const poolSize = new TokenAmount(this.quoteToken, response.value.amount, true);
|
||||
let inRange = true;
|
||||
|
||||
if (!this.maxPoolSize?.isZero()) {
|
||||
inRange = poolSize.raw.lte(this.maxPoolSize.raw);
|
||||
if (!this.maxPoolSize?.isZero()) {
|
||||
inRange = poolSize.raw.lte(this.maxPoolSize.raw);
|
||||
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} > ${this.maxPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} > ${this.maxPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.minPoolSize?.isZero()) {
|
||||
inRange = poolSize.raw.gte(this.minPoolSize.raw);
|
||||
if (!this.minPoolSize?.isZero()) {
|
||||
inRange = poolSize.raw.gte(this.minPoolSize.raw);
|
||||
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} < ${this.minPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
if (!inRange) {
|
||||
return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} < ${this.minPoolSize.toFixed()}` };
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: inRange };
|
||||
} catch (error) {
|
||||
logger.error({ mint: poolKeys.baseMint }, `Failed to check pool size`);
|
||||
}
|
||||
return { ok: inRange };
|
||||
} catch (error) {
|
||||
logger.error({ mint: poolKeys.baseMint }, `Failed to check pool size`);
|
||||
}
|
||||
|
||||
return { ok: false, message: 'PoolSize -> Failed to check pool size' };
|
||||
}
|
||||
return { ok: false, message: "PoolSize -> Failed to check pool size" };
|
||||
}
|
||||
}
|
||||
|
||||
69
filters/renounced.filter.js
Normal file
69
filters/renounced.filter.js
Normal file
@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RenouncedFreezeFilter = void 0;
|
||||
const spl_token_1 = require("@solana/spl-token");
|
||||
const helpers_1 = require("../helpers");
|
||||
class RenouncedFreezeFilter {
|
||||
constructor(connection, checkRenounced, checkFreezable) {
|
||||
this.connection = connection;
|
||||
this.checkRenounced = checkRenounced;
|
||||
this.checkFreezable = checkFreezable;
|
||||
this.errorMessage = [];
|
||||
this.cachedResult = undefined;
|
||||
if (this.checkRenounced) {
|
||||
this.errorMessage.push("mint");
|
||||
}
|
||||
if (this.checkFreezable) {
|
||||
this.errorMessage.push("freeze");
|
||||
}
|
||||
}
|
||||
execute(poolKeys) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
try {
|
||||
const accountInfo = yield this.connection.getAccountInfo(poolKeys.baseMint, this.connection.commitment);
|
||||
if (!(accountInfo === null || accountInfo === void 0 ? void 0 : accountInfo.data)) {
|
||||
return { ok: false, message: "RenouncedFreeze -> Failed to fetch account data" };
|
||||
}
|
||||
const deserialize = spl_token_1.MintLayout.decode(accountInfo.data);
|
||||
const renounced = !this.checkRenounced || deserialize.mintAuthorityOption === 0;
|
||||
const freezable = !this.checkFreezable || deserialize.freezeAuthorityOption !== 0;
|
||||
const ok = renounced && !freezable;
|
||||
const message = [];
|
||||
if (!renounced) {
|
||||
message.push("mint");
|
||||
}
|
||||
if (freezable) {
|
||||
message.push("freeze");
|
||||
}
|
||||
const result = {
|
||||
ok: ok,
|
||||
message: ok ? undefined : `RenouncedFreeze -> Creator can ${message.join(" and ")} tokens`,
|
||||
};
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
helpers_1.logger.error({ mint: poolKeys.baseMint }, `RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(" and ")} tokens`);
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
message: `RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(" and ")} tokens`,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RenouncedFreezeFilter = RenouncedFreezeFilter;
|
||||
@ -1,72 +1,72 @@
|
||||
import { Filter, FilterResult } from './pool-filters';
|
||||
import { MintLayout } from '@solana/spl-token';
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { LiquidityPoolKeysV4 } from '@raydium-io/raydium-sdk';
|
||||
import { logger } from '../helpers';
|
||||
import { Filter, FilterResult } from "./pool-filters";
|
||||
import { MintLayout } from "@solana/spl-token";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { LiquidityPoolKeysV4 } from "@raydium-io/raydium-sdk";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class RenouncedFreezeFilter implements Filter {
|
||||
private readonly errorMessage: string[] = [];
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
private readonly errorMessage: string[] = [];
|
||||
private cachedResult: FilterResult | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly checkRenounced: boolean,
|
||||
private readonly checkFreezable: boolean,
|
||||
) {
|
||||
if (this.checkRenounced) {
|
||||
this.errorMessage.push('mint');
|
||||
}
|
||||
constructor(
|
||||
private readonly connection: Connection,
|
||||
private readonly checkRenounced: boolean,
|
||||
private readonly checkFreezable: boolean,
|
||||
) {
|
||||
if (this.checkRenounced) {
|
||||
this.errorMessage.push("mint");
|
||||
}
|
||||
|
||||
if (this.checkFreezable) {
|
||||
this.errorMessage.push('freeze');
|
||||
}
|
||||
}
|
||||
if (this.checkFreezable) {
|
||||
this.errorMessage.push("freeze");
|
||||
}
|
||||
}
|
||||
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
async execute(poolKeys: LiquidityPoolKeysV4): Promise<FilterResult> {
|
||||
if (this.cachedResult) {
|
||||
return this.cachedResult;
|
||||
}
|
||||
|
||||
try {
|
||||
const accountInfo = await this.connection.getAccountInfo(poolKeys.baseMint, this.connection.commitment);
|
||||
if (!accountInfo?.data) {
|
||||
return { ok: false, message: 'RenouncedFreeze -> Failed to fetch account data' };
|
||||
}
|
||||
try {
|
||||
const accountInfo = await this.connection.getAccountInfo(poolKeys.baseMint, this.connection.commitment);
|
||||
if (!accountInfo?.data) {
|
||||
return { ok: false, message: "RenouncedFreeze -> Failed to fetch account data" };
|
||||
}
|
||||
|
||||
const deserialize = MintLayout.decode(accountInfo.data);
|
||||
const renounced = !this.checkRenounced || deserialize.mintAuthorityOption === 0;
|
||||
const freezable = !this.checkFreezable || deserialize.freezeAuthorityOption !== 0;
|
||||
const ok = renounced && !freezable;
|
||||
const message: string[] = [];
|
||||
const deserialize = MintLayout.decode(accountInfo.data);
|
||||
const renounced = !this.checkRenounced || deserialize.mintAuthorityOption === 0;
|
||||
const freezable = !this.checkFreezable || deserialize.freezeAuthorityOption !== 0;
|
||||
const ok = renounced && !freezable;
|
||||
const message: string[] = [];
|
||||
|
||||
if (!renounced) {
|
||||
message.push('mint');
|
||||
}
|
||||
if (!renounced) {
|
||||
message.push("mint");
|
||||
}
|
||||
|
||||
if (freezable) {
|
||||
message.push('freeze');
|
||||
}
|
||||
if (freezable) {
|
||||
message.push("freeze");
|
||||
}
|
||||
|
||||
const result = {
|
||||
ok: ok,
|
||||
message: ok ? undefined : `RenouncedFreeze -> Creator can ${message.join(' and ')} tokens`,
|
||||
};
|
||||
const result = {
|
||||
ok: ok,
|
||||
message: ok ? undefined : `RenouncedFreeze -> Creator can ${message.join(" and ")} tokens`,
|
||||
};
|
||||
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
if (result.ok) {
|
||||
this.cachedResult = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
{ mint: poolKeys.baseMint },
|
||||
`RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(' and ')} tokens`,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
{ mint: poolKeys.baseMint },
|
||||
`RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(" and ")} tokens`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
message: `RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(' and ')} tokens`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
message: `RenouncedFreeze -> Failed to check if creator can ${this.errorMessage.join(" and ")} tokens`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
63
helpers/constants.js
Normal file
63
helpers/constants.js
Normal file
@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SNIPE_LIST_REFRESH_INTERVAL = exports.USE_SNIPE_LIST = exports.MAX_POOL_SIZE = exports.MIN_POOL_SIZE = exports.CHECK_IF_BURNED = exports.CHECK_IF_FREEZABLE = exports.CHECK_IF_MINT_IS_RENOUNCED = exports.CHECK_IF_SOCIALS = exports.CHECK_IF_MUTABLE = exports.CONSECUTIVE_FILTER_MATCHES = exports.FILTER_CHECK_DURATION = exports.FILTER_CHECK_INTERVAL = exports.SKIP_SELLING_IF_LOST_MORE_THAN = exports.SELL_SLIPPAGE = exports.PRICE_CHECK_DURATION = exports.PRICE_CHECK_INTERVAL = exports.TRAILING_STOP_LOSS = exports.STOP_LOSS = exports.TAKE_PROFIT = exports.MAX_SELL_RETRIES = exports.AUTO_SELL_DELAY = exports.AUTO_SELL = exports.BUY_SLIPPAGE = exports.MAX_BUY_RETRIES = exports.QUOTE_AMOUNT = exports.QUOTE_MINT = exports.AUTO_BUY_DELAY = exports.CUSTOM_FEE = exports.TRANSACTION_EXECUTOR = exports.CACHE_NEW_MARKETS = exports.PRE_LOAD_EXISTING_MARKETS = exports.COMPUTE_UNIT_PRICE = exports.COMPUTE_UNIT_LIMIT = exports.MAX_TOKENS_AT_THE_TIME = exports.LOG_LEVEL = exports.RPC_WEBSOCKET_ENDPOINT = exports.RPC_ENDPOINT = exports.COMMITMENT_LEVEL = exports.NETWORK = exports.PRIVATE_KEY = void 0;
|
||||
const dotenv_1 = __importDefault(require("dotenv"));
|
||||
const logger_1 = require("./logger");
|
||||
dotenv_1.default.config();
|
||||
const retrieveEnvVariable = (variableName, logger) => {
|
||||
const variable = process.env[variableName] || "";
|
||||
if (!variable) {
|
||||
logger.error(`${variableName} is not set`);
|
||||
process.exit(1);
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
// Wallet
|
||||
exports.PRIVATE_KEY = retrieveEnvVariable("PRIVATE_KEY", logger_1.logger);
|
||||
// Connection
|
||||
exports.NETWORK = "mainnet-beta";
|
||||
exports.COMMITMENT_LEVEL = retrieveEnvVariable("COMMITMENT_LEVEL", logger_1.logger);
|
||||
exports.RPC_ENDPOINT = retrieveEnvVariable("RPC_ENDPOINT", logger_1.logger);
|
||||
exports.RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable("RPC_WEBSOCKET_ENDPOINT", logger_1.logger);
|
||||
// Bot
|
||||
exports.LOG_LEVEL = retrieveEnvVariable("LOG_LEVEL", logger_1.logger);
|
||||
exports.MAX_TOKENS_AT_THE_TIME = Number(retrieveEnvVariable("MAX_TOKENS_AT_THE_TIME", logger_1.logger));
|
||||
exports.COMPUTE_UNIT_LIMIT = Number(retrieveEnvVariable("COMPUTE_UNIT_LIMIT", logger_1.logger));
|
||||
exports.COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable("COMPUTE_UNIT_PRICE", logger_1.logger));
|
||||
exports.PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable("PRE_LOAD_EXISTING_MARKETS", logger_1.logger) === "true";
|
||||
exports.CACHE_NEW_MARKETS = retrieveEnvVariable("CACHE_NEW_MARKETS", logger_1.logger) === "true";
|
||||
exports.TRANSACTION_EXECUTOR = retrieveEnvVariable("TRANSACTION_EXECUTOR", logger_1.logger);
|
||||
exports.CUSTOM_FEE = retrieveEnvVariable("CUSTOM_FEE", logger_1.logger);
|
||||
// Buy
|
||||
exports.AUTO_BUY_DELAY = Number(retrieveEnvVariable("AUTO_BUY_DELAY", logger_1.logger));
|
||||
exports.QUOTE_MINT = retrieveEnvVariable("QUOTE_MINT", logger_1.logger);
|
||||
exports.QUOTE_AMOUNT = retrieveEnvVariable("QUOTE_AMOUNT", logger_1.logger);
|
||||
exports.MAX_BUY_RETRIES = Number(retrieveEnvVariable("MAX_BUY_RETRIES", logger_1.logger));
|
||||
exports.BUY_SLIPPAGE = Number(retrieveEnvVariable("BUY_SLIPPAGE", logger_1.logger));
|
||||
// Sell
|
||||
exports.AUTO_SELL = retrieveEnvVariable("AUTO_SELL", logger_1.logger) === "true";
|
||||
exports.AUTO_SELL_DELAY = Number(retrieveEnvVariable("AUTO_SELL_DELAY", logger_1.logger));
|
||||
exports.MAX_SELL_RETRIES = Number(retrieveEnvVariable("MAX_SELL_RETRIES", logger_1.logger));
|
||||
exports.TAKE_PROFIT = Number(retrieveEnvVariable("TAKE_PROFIT", logger_1.logger));
|
||||
exports.STOP_LOSS = Number(retrieveEnvVariable("STOP_LOSS", logger_1.logger));
|
||||
exports.TRAILING_STOP_LOSS = retrieveEnvVariable("TRAILING_STOP_LOSS", logger_1.logger) === "true";
|
||||
exports.PRICE_CHECK_INTERVAL = Number(retrieveEnvVariable("PRICE_CHECK_INTERVAL", logger_1.logger));
|
||||
exports.PRICE_CHECK_DURATION = Number(retrieveEnvVariable("PRICE_CHECK_DURATION", logger_1.logger));
|
||||
exports.SELL_SLIPPAGE = Number(retrieveEnvVariable("SELL_SLIPPAGE", logger_1.logger));
|
||||
exports.SKIP_SELLING_IF_LOST_MORE_THAN = Number(retrieveEnvVariable("SKIP_SELLING_IF_LOST_MORE_THAN", logger_1.logger));
|
||||
// Filters
|
||||
exports.FILTER_CHECK_INTERVAL = Number(retrieveEnvVariable("FILTER_CHECK_INTERVAL", logger_1.logger));
|
||||
exports.FILTER_CHECK_DURATION = Number(retrieveEnvVariable("FILTER_CHECK_DURATION", logger_1.logger));
|
||||
exports.CONSECUTIVE_FILTER_MATCHES = Number(retrieveEnvVariable("CONSECUTIVE_FILTER_MATCHES", logger_1.logger));
|
||||
exports.CHECK_IF_MUTABLE = retrieveEnvVariable("CHECK_IF_MUTABLE", logger_1.logger) === "true";
|
||||
exports.CHECK_IF_SOCIALS = retrieveEnvVariable("CHECK_IF_SOCIALS", logger_1.logger) === "true";
|
||||
exports.CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable("CHECK_IF_MINT_IS_RENOUNCED", logger_1.logger) === "true";
|
||||
exports.CHECK_IF_FREEZABLE = retrieveEnvVariable("CHECK_IF_FREEZABLE", logger_1.logger) === "true";
|
||||
exports.CHECK_IF_BURNED = retrieveEnvVariable("CHECK_IF_BURNED", logger_1.logger) === "true";
|
||||
exports.MIN_POOL_SIZE = retrieveEnvVariable("MIN_POOL_SIZE", logger_1.logger);
|
||||
exports.MAX_POOL_SIZE = retrieveEnvVariable("MAX_POOL_SIZE", logger_1.logger);
|
||||
exports.USE_SNIPE_LIST = retrieveEnvVariable("USE_SNIPE_LIST", logger_1.logger) === "true";
|
||||
exports.SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable("SNIPE_LIST_REFRESH_INTERVAL", logger_1.logger));
|
||||
@ -1,67 +1,67 @@
|
||||
import { Logger } from 'pino';
|
||||
import dotenv from 'dotenv';
|
||||
import { Commitment } from '@solana/web3.js';
|
||||
import { logger } from './logger';
|
||||
import { Logger } from "pino";
|
||||
import dotenv from "dotenv";
|
||||
import { Commitment } from "@solana/web3.js";
|
||||
import { logger } from "./logger";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const retrieveEnvVariable = (variableName: string, logger: Logger) => {
|
||||
const variable = process.env[variableName] || '';
|
||||
if (!variable) {
|
||||
logger.error(`${variableName} is not set`);
|
||||
process.exit(1);
|
||||
}
|
||||
return variable;
|
||||
const variable = process.env[variableName] || "";
|
||||
if (!variable) {
|
||||
logger.error(`${variableName} is not set`);
|
||||
process.exit(1);
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
|
||||
// Wallet
|
||||
export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger);
|
||||
export const PRIVATE_KEY = retrieveEnvVariable("PRIVATE_KEY", logger);
|
||||
|
||||
// Connection
|
||||
export const NETWORK = 'mainnet-beta';
|
||||
export const COMMITMENT_LEVEL: Commitment = retrieveEnvVariable('COMMITMENT_LEVEL', logger) as Commitment;
|
||||
export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger);
|
||||
export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger);
|
||||
export const NETWORK = "mainnet-beta";
|
||||
export const COMMITMENT_LEVEL: Commitment = retrieveEnvVariable("COMMITMENT_LEVEL", logger) as Commitment;
|
||||
export const RPC_ENDPOINT = retrieveEnvVariable("RPC_ENDPOINT", logger);
|
||||
export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable("RPC_WEBSOCKET_ENDPOINT", logger);
|
||||
|
||||
// Bot
|
||||
export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger);
|
||||
export const MAX_TOKENS_AT_THE_TIME = Number(retrieveEnvVariable('MAX_TOKENS_AT_THE_TIME', logger));
|
||||
export const COMPUTE_UNIT_LIMIT = Number(retrieveEnvVariable('COMPUTE_UNIT_LIMIT', logger));
|
||||
export const COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable('COMPUTE_UNIT_PRICE', logger));
|
||||
export const PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable('PRE_LOAD_EXISTING_MARKETS', logger) === 'true';
|
||||
export const CACHE_NEW_MARKETS = retrieveEnvVariable('CACHE_NEW_MARKETS', logger) === 'true';
|
||||
export const TRANSACTION_EXECUTOR = retrieveEnvVariable('TRANSACTION_EXECUTOR', logger);
|
||||
export const CUSTOM_FEE = retrieveEnvVariable('CUSTOM_FEE', logger);
|
||||
export const LOG_LEVEL = retrieveEnvVariable("LOG_LEVEL", logger);
|
||||
export const MAX_TOKENS_AT_THE_TIME = Number(retrieveEnvVariable("MAX_TOKENS_AT_THE_TIME", logger));
|
||||
export const COMPUTE_UNIT_LIMIT = Number(retrieveEnvVariable("COMPUTE_UNIT_LIMIT", logger));
|
||||
export const COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable("COMPUTE_UNIT_PRICE", logger));
|
||||
export const PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable("PRE_LOAD_EXISTING_MARKETS", logger) === "true";
|
||||
export const CACHE_NEW_MARKETS = retrieveEnvVariable("CACHE_NEW_MARKETS", logger) === "true";
|
||||
export const TRANSACTION_EXECUTOR = retrieveEnvVariable("TRANSACTION_EXECUTOR", logger);
|
||||
export const CUSTOM_FEE = retrieveEnvVariable("CUSTOM_FEE", logger);
|
||||
|
||||
// Buy
|
||||
export const AUTO_BUY_DELAY = Number(retrieveEnvVariable('AUTO_BUY_DELAY', logger));
|
||||
export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger);
|
||||
export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger);
|
||||
export const MAX_BUY_RETRIES = Number(retrieveEnvVariable('MAX_BUY_RETRIES', logger));
|
||||
export const BUY_SLIPPAGE = Number(retrieveEnvVariable('BUY_SLIPPAGE', logger));
|
||||
export const AUTO_BUY_DELAY = Number(retrieveEnvVariable("AUTO_BUY_DELAY", logger));
|
||||
export const QUOTE_MINT = retrieveEnvVariable("QUOTE_MINT", logger);
|
||||
export const QUOTE_AMOUNT = retrieveEnvVariable("QUOTE_AMOUNT", logger);
|
||||
export const MAX_BUY_RETRIES = Number(retrieveEnvVariable("MAX_BUY_RETRIES", logger));
|
||||
export const BUY_SLIPPAGE = Number(retrieveEnvVariable("BUY_SLIPPAGE", logger));
|
||||
|
||||
// Sell
|
||||
export const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true';
|
||||
export const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger));
|
||||
export const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger));
|
||||
export const TAKE_PROFIT = Number(retrieveEnvVariable('TAKE_PROFIT', logger));
|
||||
export const STOP_LOSS = Number(retrieveEnvVariable('STOP_LOSS', logger));
|
||||
export const TRAILING_STOP_LOSS = retrieveEnvVariable('TRAILING_STOP_LOSS', logger) === 'true';
|
||||
export const PRICE_CHECK_INTERVAL = Number(retrieveEnvVariable('PRICE_CHECK_INTERVAL', logger));
|
||||
export const PRICE_CHECK_DURATION = Number(retrieveEnvVariable('PRICE_CHECK_DURATION', logger));
|
||||
export const SELL_SLIPPAGE = Number(retrieveEnvVariable('SELL_SLIPPAGE', logger));
|
||||
export const SKIP_SELLING_IF_LOST_MORE_THAN = Number(retrieveEnvVariable('SKIP_SELLING_IF_LOST_MORE_THAN', logger));
|
||||
export const AUTO_SELL = retrieveEnvVariable("AUTO_SELL", logger) === "true";
|
||||
export const AUTO_SELL_DELAY = Number(retrieveEnvVariable("AUTO_SELL_DELAY", logger));
|
||||
export const MAX_SELL_RETRIES = Number(retrieveEnvVariable("MAX_SELL_RETRIES", logger));
|
||||
export const TAKE_PROFIT = Number(retrieveEnvVariable("TAKE_PROFIT", logger));
|
||||
export const STOP_LOSS = Number(retrieveEnvVariable("STOP_LOSS", logger));
|
||||
export const TRAILING_STOP_LOSS = retrieveEnvVariable("TRAILING_STOP_LOSS", logger) === "true";
|
||||
export const PRICE_CHECK_INTERVAL = Number(retrieveEnvVariable("PRICE_CHECK_INTERVAL", logger));
|
||||
export const PRICE_CHECK_DURATION = Number(retrieveEnvVariable("PRICE_CHECK_DURATION", logger));
|
||||
export const SELL_SLIPPAGE = Number(retrieveEnvVariable("SELL_SLIPPAGE", logger));
|
||||
export const SKIP_SELLING_IF_LOST_MORE_THAN = Number(retrieveEnvVariable("SKIP_SELLING_IF_LOST_MORE_THAN", logger));
|
||||
|
||||
// Filters
|
||||
export const FILTER_CHECK_INTERVAL = Number(retrieveEnvVariable('FILTER_CHECK_INTERVAL', logger));
|
||||
export const FILTER_CHECK_DURATION = Number(retrieveEnvVariable('FILTER_CHECK_DURATION', logger));
|
||||
export const CONSECUTIVE_FILTER_MATCHES = Number(retrieveEnvVariable('CONSECUTIVE_FILTER_MATCHES', logger));
|
||||
export const CHECK_IF_MUTABLE = retrieveEnvVariable('CHECK_IF_MUTABLE', logger) === 'true';
|
||||
export const CHECK_IF_SOCIALS = retrieveEnvVariable('CHECK_IF_SOCIALS', logger) === 'true';
|
||||
export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true';
|
||||
export const CHECK_IF_FREEZABLE = retrieveEnvVariable('CHECK_IF_FREEZABLE', logger) === 'true';
|
||||
export const CHECK_IF_BURNED = retrieveEnvVariable('CHECK_IF_BURNED', logger) === 'true';
|
||||
export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger);
|
||||
export const MAX_POOL_SIZE = retrieveEnvVariable('MAX_POOL_SIZE', logger);
|
||||
export const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true';
|
||||
export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger));
|
||||
export const FILTER_CHECK_INTERVAL = Number(retrieveEnvVariable("FILTER_CHECK_INTERVAL", logger));
|
||||
export const FILTER_CHECK_DURATION = Number(retrieveEnvVariable("FILTER_CHECK_DURATION", logger));
|
||||
export const CONSECUTIVE_FILTER_MATCHES = Number(retrieveEnvVariable("CONSECUTIVE_FILTER_MATCHES", logger));
|
||||
export const CHECK_IF_MUTABLE = retrieveEnvVariable("CHECK_IF_MUTABLE", logger) === "true";
|
||||
export const CHECK_IF_SOCIALS = retrieveEnvVariable("CHECK_IF_SOCIALS", logger) === "true";
|
||||
export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable("CHECK_IF_MINT_IS_RENOUNCED", logger) === "true";
|
||||
export const CHECK_IF_FREEZABLE = retrieveEnvVariable("CHECK_IF_FREEZABLE", logger) === "true";
|
||||
export const CHECK_IF_BURNED = retrieveEnvVariable("CHECK_IF_BURNED", logger) === "true";
|
||||
export const MIN_POOL_SIZE = retrieveEnvVariable("MIN_POOL_SIZE", logger);
|
||||
export const MAX_POOL_SIZE = retrieveEnvVariable("MAX_POOL_SIZE", logger);
|
||||
export const USE_SNIPE_LIST = retrieveEnvVariable("USE_SNIPE_LIST", logger) === "true";
|
||||
export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable("SNIPE_LIST_REFRESH_INTERVAL", logger));
|
||||
|
||||
8
helpers/helper.cache.js
Normal file
8
helpers/helper.cache.js
Normal file
File diff suppressed because one or more lines are too long
23
helpers/index.js
Normal file
23
helpers/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./market"), exports);
|
||||
__exportStar(require("./liquidity"), exports);
|
||||
__exportStar(require("./logger"), exports);
|
||||
__exportStar(require("./constants"), exports);
|
||||
__exportStar(require("./token"), exports);
|
||||
__exportStar(require("./wallet"), exports);
|
||||
__exportStar(require("./promises"), exports);
|
||||
@ -1,7 +1,7 @@
|
||||
export * from './market';
|
||||
export * from './liquidity';
|
||||
export * from './logger';
|
||||
export * from './constants';
|
||||
export * from './token';
|
||||
export * from './wallet';
|
||||
export * from './promises'
|
||||
export * from "./market";
|
||||
export * from "./liquidity";
|
||||
export * from "./logger";
|
||||
export * from "./constants";
|
||||
export * from "./token";
|
||||
export * from "./wallet";
|
||||
export * from "./promises";
|
||||
|
||||
41
helpers/liquidity.js
Normal file
41
helpers/liquidity.js
Normal file
@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createPoolKeys = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
function createPoolKeys(id, accountData, minimalMarketLayoutV3) {
|
||||
return {
|
||||
id,
|
||||
baseMint: accountData.baseMint,
|
||||
quoteMint: accountData.quoteMint,
|
||||
lpMint: accountData.lpMint,
|
||||
baseDecimals: accountData.baseDecimal.toNumber(),
|
||||
quoteDecimals: accountData.quoteDecimal.toNumber(),
|
||||
lpDecimals: 5,
|
||||
version: 4,
|
||||
programId: raydium_sdk_1.MAINNET_PROGRAM_ID.AmmV4,
|
||||
authority: raydium_sdk_1.Liquidity.getAssociatedAuthority({
|
||||
programId: raydium_sdk_1.MAINNET_PROGRAM_ID.AmmV4,
|
||||
}).publicKey,
|
||||
openOrders: accountData.openOrders,
|
||||
targetOrders: accountData.targetOrders,
|
||||
baseVault: accountData.baseVault,
|
||||
quoteVault: accountData.quoteVault,
|
||||
marketVersion: 3,
|
||||
marketProgramId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
marketAuthority: raydium_sdk_1.Market.getAssociatedAuthority({
|
||||
programId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
}).publicKey,
|
||||
marketBaseVault: accountData.baseVault,
|
||||
marketQuoteVault: accountData.quoteVault,
|
||||
marketBids: minimalMarketLayoutV3.bids,
|
||||
marketAsks: minimalMarketLayoutV3.asks,
|
||||
marketEventQueue: minimalMarketLayoutV3.eventQueue,
|
||||
withdrawQueue: accountData.withdrawQueue,
|
||||
lpVault: accountData.lpVault,
|
||||
lookupTableAccount: web3_js_1.PublicKey.default,
|
||||
};
|
||||
}
|
||||
exports.createPoolKeys = createPoolKeys;
|
||||
@ -1,43 +1,43 @@
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { Liquidity, LiquidityPoolKeys, LiquidityStateV4, MAINNET_PROGRAM_ID, Market } from '@raydium-io/raydium-sdk';
|
||||
import { MinimalMarketLayoutV3 } from './market';
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { Liquidity, LiquidityPoolKeys, LiquidityStateV4, MAINNET_PROGRAM_ID, Market } from "@raydium-io/raydium-sdk";
|
||||
import { MinimalMarketLayoutV3 } from "./market";
|
||||
|
||||
export function createPoolKeys(
|
||||
id: PublicKey,
|
||||
accountData: LiquidityStateV4,
|
||||
minimalMarketLayoutV3: MinimalMarketLayoutV3,
|
||||
id: PublicKey,
|
||||
accountData: LiquidityStateV4,
|
||||
minimalMarketLayoutV3: MinimalMarketLayoutV3,
|
||||
): LiquidityPoolKeys {
|
||||
return {
|
||||
id,
|
||||
baseMint: accountData.baseMint,
|
||||
quoteMint: accountData.quoteMint,
|
||||
lpMint: accountData.lpMint,
|
||||
baseDecimals: accountData.baseDecimal.toNumber(),
|
||||
quoteDecimals: accountData.quoteDecimal.toNumber(),
|
||||
lpDecimals: 5,
|
||||
version: 4,
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
authority: Liquidity.getAssociatedAuthority({
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
}).publicKey,
|
||||
openOrders: accountData.openOrders,
|
||||
targetOrders: accountData.targetOrders,
|
||||
baseVault: accountData.baseVault,
|
||||
quoteVault: accountData.quoteVault,
|
||||
marketVersion: 3,
|
||||
marketProgramId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
marketAuthority: Market.getAssociatedAuthority({
|
||||
programId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
}).publicKey,
|
||||
marketBaseVault: accountData.baseVault,
|
||||
marketQuoteVault: accountData.quoteVault,
|
||||
marketBids: minimalMarketLayoutV3.bids,
|
||||
marketAsks: minimalMarketLayoutV3.asks,
|
||||
marketEventQueue: minimalMarketLayoutV3.eventQueue,
|
||||
withdrawQueue: accountData.withdrawQueue,
|
||||
lpVault: accountData.lpVault,
|
||||
lookupTableAccount: PublicKey.default,
|
||||
};
|
||||
return {
|
||||
id,
|
||||
baseMint: accountData.baseMint,
|
||||
quoteMint: accountData.quoteMint,
|
||||
lpMint: accountData.lpMint,
|
||||
baseDecimals: accountData.baseDecimal.toNumber(),
|
||||
quoteDecimals: accountData.quoteDecimal.toNumber(),
|
||||
lpDecimals: 5,
|
||||
version: 4,
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
authority: Liquidity.getAssociatedAuthority({
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
}).publicKey,
|
||||
openOrders: accountData.openOrders,
|
||||
targetOrders: accountData.targetOrders,
|
||||
baseVault: accountData.baseVault,
|
||||
quoteVault: accountData.quoteVault,
|
||||
marketVersion: 3,
|
||||
marketProgramId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
marketAuthority: Market.getAssociatedAuthority({
|
||||
programId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
}).publicKey,
|
||||
marketBaseVault: accountData.baseVault,
|
||||
marketQuoteVault: accountData.quoteVault,
|
||||
marketBids: minimalMarketLayoutV3.bids,
|
||||
marketAsks: minimalMarketLayoutV3.asks,
|
||||
marketEventQueue: minimalMarketLayoutV3.eventQueue,
|
||||
withdrawQueue: accountData.withdrawQueue,
|
||||
lpVault: accountData.lpVault,
|
||||
lookupTableAccount: PublicKey.default,
|
||||
};
|
||||
}
|
||||
|
||||
18
helpers/logger.js
Normal file
18
helpers/logger.js
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.logger = void 0;
|
||||
const pino_1 = __importDefault(require("pino"));
|
||||
const transport = pino_1.default.transport({
|
||||
target: "pino-pretty",
|
||||
});
|
||||
exports.logger = (0, pino_1.default)({
|
||||
level: "info",
|
||||
redact: ["poolKeys"],
|
||||
serializers: {
|
||||
error: pino_1.default.stdSerializers.err,
|
||||
},
|
||||
base: undefined,
|
||||
}, transport);
|
||||
@ -1,17 +1,17 @@
|
||||
import pino from 'pino';
|
||||
import pino from "pino";
|
||||
|
||||
const transport = pino.transport({
|
||||
target: 'pino-pretty',
|
||||
target: "pino-pretty",
|
||||
});
|
||||
|
||||
export const logger = pino(
|
||||
{
|
||||
level: 'info',
|
||||
redact: ['poolKeys'],
|
||||
serializers: {
|
||||
error: pino.stdSerializers.err,
|
||||
},
|
||||
base: undefined,
|
||||
},
|
||||
transport,
|
||||
{
|
||||
level: "info",
|
||||
redact: ["poolKeys"],
|
||||
serializers: {
|
||||
error: pino.stdSerializers.err,
|
||||
},
|
||||
base: undefined,
|
||||
},
|
||||
transport,
|
||||
);
|
||||
|
||||
27
helpers/market.js
Normal file
27
helpers/market.js
Normal file
@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getMinimalMarketV3 = exports.MINIMAL_MARKET_STATE_LAYOUT_V3 = void 0;
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
exports.MINIMAL_MARKET_STATE_LAYOUT_V3 = (0, raydium_sdk_1.struct)([(0, raydium_sdk_1.publicKey)("eventQueue"), (0, raydium_sdk_1.publicKey)("bids"), (0, raydium_sdk_1.publicKey)("asks")]);
|
||||
function getMinimalMarketV3(connection, marketId, commitment) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const marketInfo = yield connection.getAccountInfo(marketId, {
|
||||
commitment,
|
||||
dataSlice: {
|
||||
offset: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.offsetOf("eventQueue"),
|
||||
length: 32 * 3,
|
||||
},
|
||||
});
|
||||
return exports.MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo.data);
|
||||
});
|
||||
}
|
||||
exports.getMinimalMarketV3 = getMinimalMarketV3;
|
||||
@ -1,22 +1,22 @@
|
||||
import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
||||
import { GetStructureSchema, MARKET_STATE_LAYOUT_V3, publicKey, struct } from '@raydium-io/raydium-sdk';
|
||||
import { Commitment, Connection, PublicKey } from "@solana/web3.js";
|
||||
import { GetStructureSchema, MARKET_STATE_LAYOUT_V3, publicKey, struct } from "@raydium-io/raydium-sdk";
|
||||
|
||||
export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([publicKey('eventQueue'), publicKey('bids'), publicKey('asks')]);
|
||||
export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([publicKey("eventQueue"), publicKey("bids"), publicKey("asks")]);
|
||||
export type MinimalMarketStateLayoutV3 = typeof MINIMAL_MARKET_STATE_LAYOUT_V3;
|
||||
export type MinimalMarketLayoutV3 = GetStructureSchema<MinimalMarketStateLayoutV3>;
|
||||
|
||||
export async function getMinimalMarketV3(
|
||||
connection: Connection,
|
||||
marketId: PublicKey,
|
||||
commitment?: Commitment,
|
||||
connection: Connection,
|
||||
marketId: PublicKey,
|
||||
commitment?: Commitment,
|
||||
): Promise<MinimalMarketLayoutV3> {
|
||||
const marketInfo = await connection.getAccountInfo(marketId, {
|
||||
commitment,
|
||||
dataSlice: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'),
|
||||
length: 32 * 3,
|
||||
},
|
||||
});
|
||||
const marketInfo = await connection.getAccountInfo(marketId, {
|
||||
commitment,
|
||||
dataSlice: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf("eventQueue"),
|
||||
length: 32 * 3,
|
||||
},
|
||||
});
|
||||
|
||||
return MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo!.data);
|
||||
return MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo!.data);
|
||||
}
|
||||
|
||||
5
helpers/promises.js
Normal file
5
helpers/promises.js
Normal file
@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.sleep = void 0;
|
||||
const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
exports.sleep = sleep;
|
||||
20
helpers/token.js
Normal file
20
helpers/token.js
Normal file
@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getToken = void 0;
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const spl_token_1 = require("@solana/spl-token");
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
function getToken(token) {
|
||||
switch (token) {
|
||||
case "WSOL": {
|
||||
return raydium_sdk_1.Token.WSOL;
|
||||
}
|
||||
case "USDC": {
|
||||
return new raydium_sdk_1.Token(spl_token_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), 6, "USDC", "USDC");
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported quote mint "${token}". Supported values are USDC and WSOL`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.getToken = getToken;
|
||||
@ -1,23 +1,23 @@
|
||||
import { Token } from '@raydium-io/raydium-sdk';
|
||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { Token } from "@raydium-io/raydium-sdk";
|
||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export function getToken(token: string) {
|
||||
switch (token) {
|
||||
case 'WSOL': {
|
||||
return Token.WSOL;
|
||||
}
|
||||
case 'USDC': {
|
||||
return new Token(
|
||||
TOKEN_PROGRAM_ID,
|
||||
new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
||||
6,
|
||||
'USDC',
|
||||
'USDC',
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported quote mint "${token}". Supported values are USDC and WSOL`);
|
||||
}
|
||||
}
|
||||
switch (token) {
|
||||
case "WSOL": {
|
||||
return Token.WSOL;
|
||||
}
|
||||
case "USDC": {
|
||||
return new Token(
|
||||
TOKEN_PROGRAM_ID,
|
||||
new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
|
||||
6,
|
||||
"USDC",
|
||||
"USDC",
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported quote mint "${token}". Supported values are USDC and WSOL`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
helpers/wallet.js
Normal file
25
helpers/wallet.js
Normal file
@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getWallet = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const bs58_1 = __importDefault(require("bs58"));
|
||||
const bip39_1 = require("bip39");
|
||||
const ed25519_hd_key_1 = require("ed25519-hd-key");
|
||||
function getWallet(wallet) {
|
||||
// most likely someone pasted the private key in binary format
|
||||
if (wallet.startsWith("[")) {
|
||||
return web3_js_1.Keypair.fromSecretKey(JSON.parse(wallet));
|
||||
}
|
||||
// most likely someone pasted mnemonic
|
||||
if (wallet.split(" ").length > 1) {
|
||||
const seed = (0, bip39_1.mnemonicToSeedSync)(wallet, "");
|
||||
const path = `m/44'/501'/0'/0'`; // we assume it's first path
|
||||
return web3_js_1.Keypair.fromSeed((0, ed25519_hd_key_1.derivePath)(path, seed.toString("hex")).key);
|
||||
}
|
||||
// most likely someone pasted base58 encoded private key
|
||||
return web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(wallet));
|
||||
}
|
||||
exports.getWallet = getWallet;
|
||||
@ -1,21 +1,21 @@
|
||||
import { Keypair } from '@solana/web3.js';
|
||||
import bs58 from 'bs58';
|
||||
import { mnemonicToSeedSync } from 'bip39';
|
||||
import { derivePath } from 'ed25519-hd-key';
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import bs58 from "bs58";
|
||||
import { mnemonicToSeedSync } from "bip39";
|
||||
import { derivePath } from "ed25519-hd-key";
|
||||
|
||||
export function getWallet(wallet: string): Keypair {
|
||||
// most likely someone pasted the private key in binary format
|
||||
if (wallet.startsWith('[')) {
|
||||
return Keypair.fromSecretKey(JSON.parse(wallet));
|
||||
}
|
||||
// most likely someone pasted the private key in binary format
|
||||
if (wallet.startsWith("[")) {
|
||||
return Keypair.fromSecretKey(JSON.parse(wallet));
|
||||
}
|
||||
|
||||
// most likely someone pasted mnemonic
|
||||
if (wallet.split(' ').length > 1) {
|
||||
const seed = mnemonicToSeedSync(wallet, '');
|
||||
const path = `m/44'/501'/0'/0'`; // we assume it's first path
|
||||
return Keypair.fromSeed(derivePath(path, seed.toString('hex')).key);
|
||||
}
|
||||
// most likely someone pasted mnemonic
|
||||
if (wallet.split(" ").length > 1) {
|
||||
const seed = mnemonicToSeedSync(wallet, "");
|
||||
const path = `m/44'/501'/0'/0'`; // we assume it's first path
|
||||
return Keypair.fromSeed(derivePath(path, seed.toString("hex")).key);
|
||||
}
|
||||
|
||||
// most likely someone pasted base58 encoded private key
|
||||
return Keypair.fromSecretKey(bs58.decode(wallet));
|
||||
// most likely someone pasted base58 encoded private key
|
||||
return Keypair.fromSecretKey(bs58.decode(wallet));
|
||||
}
|
||||
|
||||
189
index.js
Normal file
189
index.js
Normal file
@ -0,0 +1,189 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const cache_1 = require("./cache");
|
||||
const listeners_1 = require("./listeners");
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const spl_token_1 = require("@solana/spl-token");
|
||||
const bot_1 = require("./bot");
|
||||
const transactions_1 = require("./transactions");
|
||||
const helpers_1 = require("./helpers");
|
||||
const package_json_1 = require("./package.json");
|
||||
const warp_transaction_executor_1 = require("./transactions/warp-transaction-executor");
|
||||
const jito_rpc_transaction_executor_1 = require("./transactions/jito-rpc-transaction-executor");
|
||||
const connection = new web3_js_1.Connection(helpers_1.RPC_ENDPOINT, {
|
||||
wsEndpoint: helpers_1.RPC_WEBSOCKET_ENDPOINT,
|
||||
commitment: helpers_1.COMMITMENT_LEVEL,
|
||||
});
|
||||
function printDetails(wallet, quoteToken, bot) {
|
||||
helpers_1.logger.info(`
|
||||
.. :-===++++-
|
||||
.-==+++++++- =+++++++++-
|
||||
..:::--===+=.=: .+++++++++++:=+++++++++:
|
||||
.==+++++++++++++++=:+++: .+++++++++++.=++++++++-.
|
||||
.-+++++++++++++++=:=++++- .+++++++++=:.=+++++-::-.
|
||||
-:+++++++++++++=:+++++++- .++++++++-:- =+++++=-:
|
||||
-:++++++=++++=:++++=++++= .++++++++++- =+++++:
|
||||
-:++++-:=++=:++++=:-+++++:+++++====--:::::::.
|
||||
::=+-:::==:=+++=::-:--::::::::::---------::.
|
||||
::-: .::::::::. --------:::..
|
||||
:- .:.-:::.
|
||||
|
||||
WARP DRIVE ACTIVATED 🚀🐟
|
||||
Made with ❤️ by humans.
|
||||
Version: ${package_json_1.version}
|
||||
`);
|
||||
const botConfig = bot.config;
|
||||
helpers_1.logger.info('------- CONFIGURATION START -------');
|
||||
helpers_1.logger.info(`Wallet: ${wallet.publicKey.toString()}`);
|
||||
helpers_1.logger.info('- Bot -');
|
||||
helpers_1.logger.info(`Using transaction executor: ${helpers_1.TRANSACTION_EXECUTOR}`);
|
||||
if (bot.isWarp || bot.isJito) {
|
||||
helpers_1.logger.info(`${helpers_1.TRANSACTION_EXECUTOR} fee: ${helpers_1.CUSTOM_FEE}`);
|
||||
}
|
||||
else {
|
||||
helpers_1.logger.info(`Compute Unit limit: ${botConfig.unitLimit}`);
|
||||
helpers_1.logger.info(`Compute Unit price (micro lamports): ${botConfig.unitPrice}`);
|
||||
}
|
||||
helpers_1.logger.info(`Max tokens at the time: ${botConfig.maxTokensAtTheTime}`);
|
||||
helpers_1.logger.info(`Pre load existing markets: ${helpers_1.PRE_LOAD_EXISTING_MARKETS}`);
|
||||
helpers_1.logger.info(`Cache new markets: ${helpers_1.CACHE_NEW_MARKETS}`);
|
||||
helpers_1.logger.info(`Log level: ${helpers_1.LOG_LEVEL}`);
|
||||
helpers_1.logger.info('- Buy -');
|
||||
helpers_1.logger.info(`Buy amount: ${botConfig.quoteAmount.toFixed()} ${botConfig.quoteToken.name}`);
|
||||
helpers_1.logger.info(`Auto buy delay: ${botConfig.autoBuyDelay} ms`);
|
||||
helpers_1.logger.info(`Max buy retries: ${botConfig.maxBuyRetries}`);
|
||||
helpers_1.logger.info(`Buy amount (${quoteToken.symbol}): ${botConfig.quoteAmount.toFixed()}`);
|
||||
helpers_1.logger.info(`Buy slippage: ${botConfig.buySlippage}%`);
|
||||
helpers_1.logger.info('- Sell -');
|
||||
helpers_1.logger.info(`Auto sell: ${helpers_1.AUTO_SELL}`);
|
||||
helpers_1.logger.info(`Auto sell delay: ${botConfig.autoSellDelay} ms`);
|
||||
helpers_1.logger.info(`Max sell retries: ${botConfig.maxSellRetries}`);
|
||||
helpers_1.logger.info(`Sell slippage: ${botConfig.sellSlippage}%`);
|
||||
helpers_1.logger.info(`Price check interval: ${botConfig.priceCheckInterval} ms`);
|
||||
helpers_1.logger.info(`Price check duration: ${botConfig.priceCheckDuration} ms`);
|
||||
helpers_1.logger.info(`Take profit: ${botConfig.takeProfit}%`);
|
||||
helpers_1.logger.info(`Stop loss: ${botConfig.stopLoss}%`);
|
||||
helpers_1.logger.info(`Trailing stop loss: ${botConfig.trailingStopLoss}`);
|
||||
helpers_1.logger.info(`Skip selling if lost more than: ${botConfig.skipSellingIfLostMoreThan}%`);
|
||||
helpers_1.logger.info('- Snipe list -');
|
||||
helpers_1.logger.info(`Snipe list: ${botConfig.useSnipeList}`);
|
||||
helpers_1.logger.info(`Snipe list refresh interval: ${helpers_1.SNIPE_LIST_REFRESH_INTERVAL} ms`);
|
||||
if (botConfig.useSnipeList) {
|
||||
helpers_1.logger.info('- Filters -');
|
||||
helpers_1.logger.info(`Filters are disabled when snipe list is on`);
|
||||
}
|
||||
else {
|
||||
helpers_1.logger.info('- Filters -');
|
||||
helpers_1.logger.info(`Filter check interval: ${botConfig.filterCheckInterval} ms`);
|
||||
helpers_1.logger.info(`Filter check duration: ${botConfig.filterCheckDuration} ms`);
|
||||
helpers_1.logger.info(`Consecutive filter matches: ${botConfig.consecutiveMatchCount}`);
|
||||
helpers_1.logger.info(`Check renounced: ${helpers_1.CHECK_IF_MINT_IS_RENOUNCED}`);
|
||||
helpers_1.logger.info(`Check freezable: ${helpers_1.CHECK_IF_FREEZABLE}`);
|
||||
helpers_1.logger.info(`Check burned: ${helpers_1.CHECK_IF_BURNED}`);
|
||||
helpers_1.logger.info(`Check mutable: ${helpers_1.CHECK_IF_MUTABLE}`);
|
||||
helpers_1.logger.info(`Check socials: ${helpers_1.CHECK_IF_SOCIALS}`);
|
||||
helpers_1.logger.info(`Min pool size: ${botConfig.minPoolSize.toFixed()}`);
|
||||
helpers_1.logger.info(`Max pool size: ${botConfig.maxPoolSize.toFixed()}`);
|
||||
}
|
||||
helpers_1.logger.info('------- CONFIGURATION END -------');
|
||||
helpers_1.logger.info('Bot is running! Press CTRL + C to stop it.');
|
||||
}
|
||||
const runListener = () => __awaiter(void 0, void 0, void 0, function* () {
|
||||
helpers_1.logger.level = helpers_1.LOG_LEVEL;
|
||||
helpers_1.logger.info('Bot is starting...');
|
||||
const marketCache = new cache_1.MarketCache(connection);
|
||||
const poolCache = new cache_1.PoolCache();
|
||||
let txExecutor;
|
||||
switch (helpers_1.TRANSACTION_EXECUTOR) {
|
||||
case 'warp': {
|
||||
txExecutor = new warp_transaction_executor_1.WarpTransactionExecutor(helpers_1.CUSTOM_FEE);
|
||||
break;
|
||||
}
|
||||
case 'jito': {
|
||||
txExecutor = new jito_rpc_transaction_executor_1.JitoTransactionExecutor(helpers_1.CUSTOM_FEE, connection);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
txExecutor = new transactions_1.DefaultTransactionExecutor(connection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const wallet = (0, helpers_1.getWallet)(helpers_1.PRIVATE_KEY.trim());
|
||||
const quoteToken = (0, helpers_1.getToken)(helpers_1.QUOTE_MINT);
|
||||
const botConfig = {
|
||||
wallet,
|
||||
quoteAta: (0, spl_token_1.getAssociatedTokenAddressSync)(quoteToken.mint, wallet.publicKey),
|
||||
minPoolSize: new raydium_sdk_1.TokenAmount(quoteToken, helpers_1.MIN_POOL_SIZE, false),
|
||||
maxPoolSize: new raydium_sdk_1.TokenAmount(quoteToken, helpers_1.MAX_POOL_SIZE, false),
|
||||
quoteToken,
|
||||
quoteAmount: new raydium_sdk_1.TokenAmount(quoteToken, helpers_1.QUOTE_AMOUNT, false),
|
||||
maxTokensAtTheTime: helpers_1.MAX_TOKENS_AT_THE_TIME,
|
||||
useSnipeList: helpers_1.USE_SNIPE_LIST,
|
||||
autoSell: helpers_1.AUTO_SELL,
|
||||
autoSellDelay: helpers_1.AUTO_SELL_DELAY,
|
||||
maxSellRetries: helpers_1.MAX_SELL_RETRIES,
|
||||
autoBuyDelay: helpers_1.AUTO_BUY_DELAY,
|
||||
maxBuyRetries: helpers_1.MAX_BUY_RETRIES,
|
||||
unitLimit: helpers_1.COMPUTE_UNIT_LIMIT,
|
||||
unitPrice: helpers_1.COMPUTE_UNIT_PRICE,
|
||||
takeProfit: helpers_1.TAKE_PROFIT,
|
||||
stopLoss: helpers_1.STOP_LOSS,
|
||||
trailingStopLoss: helpers_1.TRAILING_STOP_LOSS,
|
||||
skipSellingIfLostMoreThan: helpers_1.SKIP_SELLING_IF_LOST_MORE_THAN,
|
||||
buySlippage: helpers_1.BUY_SLIPPAGE,
|
||||
sellSlippage: helpers_1.SELL_SLIPPAGE,
|
||||
priceCheckInterval: helpers_1.PRICE_CHECK_INTERVAL,
|
||||
priceCheckDuration: helpers_1.PRICE_CHECK_DURATION,
|
||||
filterCheckInterval: helpers_1.FILTER_CHECK_INTERVAL,
|
||||
filterCheckDuration: helpers_1.FILTER_CHECK_DURATION,
|
||||
consecutiveMatchCount: helpers_1.CONSECUTIVE_FILTER_MATCHES,
|
||||
};
|
||||
const bot = new bot_1.Bot(connection, marketCache, poolCache, txExecutor, botConfig);
|
||||
const valid = yield bot.validate();
|
||||
if (!valid) {
|
||||
helpers_1.logger.info('Bot is exiting...');
|
||||
process.exit(1);
|
||||
}
|
||||
if (helpers_1.PRE_LOAD_EXISTING_MARKETS) {
|
||||
yield marketCache.init({ quoteToken });
|
||||
}
|
||||
const runTimestamp = Math.floor(new Date().getTime() / 1000);
|
||||
const listeners = new listeners_1.Listeners(connection);
|
||||
yield listeners.start({
|
||||
walletPublicKey: wallet.publicKey,
|
||||
quoteToken,
|
||||
autoSell: helpers_1.AUTO_SELL,
|
||||
cacheNewMarkets: helpers_1.CACHE_NEW_MARKETS,
|
||||
});
|
||||
listeners.on('market', (updatedAccountInfo) => {
|
||||
const marketState = raydium_sdk_1.MARKET_STATE_LAYOUT_V3.decode(updatedAccountInfo.accountInfo.data);
|
||||
marketCache.save(updatedAccountInfo.accountId.toString(), marketState);
|
||||
});
|
||||
listeners.on('pool', (updatedAccountInfo) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const poolState = raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.decode(updatedAccountInfo.accountInfo.data);
|
||||
const poolOpenTime = parseInt(poolState.poolOpenTime.toString());
|
||||
const exists = yield poolCache.get(poolState.baseMint.toString());
|
||||
if (!exists && poolOpenTime > runTimestamp) {
|
||||
poolCache.save(updatedAccountInfo.accountId.toString(), poolState);
|
||||
yield bot.buy(updatedAccountInfo.accountId, poolState);
|
||||
}
|
||||
}));
|
||||
listeners.on('wallet', (updatedAccountInfo) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const accountData = spl_token_1.AccountLayout.decode(updatedAccountInfo.accountInfo.data);
|
||||
if (accountData.mint.equals(quoteToken.mint)) {
|
||||
return;
|
||||
}
|
||||
yield bot.sell(updatedAccountInfo.accountId, accountData);
|
||||
}));
|
||||
printDetails(wallet, quoteToken, bot);
|
||||
});
|
||||
runListener();
|
||||
17
listeners/index.js
Normal file
17
listeners/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./listeners"), exports);
|
||||
@ -1 +1 @@
|
||||
export * from './listeners';
|
||||
export * from "./listeners";
|
||||
|
||||
109
listeners/listeners.js
Normal file
109
listeners/listeners.js
Normal file
@ -0,0 +1,109 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Listeners = void 0;
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
const bs58_1 = __importDefault(require("bs58"));
|
||||
const spl_token_1 = require("@solana/spl-token");
|
||||
const events_1 = require("events");
|
||||
class Listeners extends events_1.EventEmitter {
|
||||
constructor(connection) {
|
||||
super();
|
||||
this.connection = connection;
|
||||
this.subscriptions = [];
|
||||
}
|
||||
start(config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (config.cacheNewMarkets) {
|
||||
const openBookSubscription = yield this.subscribeToOpenBookMarkets(config);
|
||||
this.subscriptions.push(openBookSubscription);
|
||||
}
|
||||
const raydiumSubscription = yield this.subscribeToRaydiumPools(config);
|
||||
this.subscriptions.push(raydiumSubscription);
|
||||
if (config.autoSell) {
|
||||
const walletSubscription = yield this.subscribeToWalletChanges(config);
|
||||
this.subscriptions.push(walletSubscription);
|
||||
}
|
||||
});
|
||||
}
|
||||
subscribeToOpenBookMarkets(config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.connection.onProgramAccountChange(raydium_sdk_1.MAINNET_PROGRAM_ID.OPENBOOK_MARKET, (updatedAccountInfo) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.emit("market", updatedAccountInfo);
|
||||
}), this.connection.commitment, [
|
||||
{ dataSize: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: raydium_sdk_1.MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
}
|
||||
subscribeToRaydiumPools(config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.connection.onProgramAccountChange(raydium_sdk_1.MAINNET_PROGRAM_ID.AmmV4, (updatedAccountInfo) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.emit("pool", updatedAccountInfo);
|
||||
}), this.connection.commitment, [
|
||||
{ dataSize: raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.offsetOf("marketProgramId"),
|
||||
bytes: raydium_sdk_1.MAINNET_PROGRAM_ID.OPENBOOK_MARKET.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: raydium_sdk_1.LIQUIDITY_STATE_LAYOUT_V4.offsetOf("status"),
|
||||
bytes: bs58_1.default.encode([6, 0, 0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
}
|
||||
subscribeToWalletChanges(config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.connection.onProgramAccountChange(spl_token_1.TOKEN_PROGRAM_ID, (updatedAccountInfo) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.emit("wallet", updatedAccountInfo);
|
||||
}), this.connection.commitment, [
|
||||
{
|
||||
dataSize: 165,
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: 32,
|
||||
bytes: config.walletPublicKey.toBase58(),
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
}
|
||||
stop() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
for (let i = this.subscriptions.length; i >= 0; --i) {
|
||||
const subscription = this.subscriptions[i];
|
||||
yield this.connection.removeAccountChangeListener(subscription);
|
||||
this.subscriptions.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.Listeners = Listeners;
|
||||
@ -1,112 +1,112 @@
|
||||
import { LIQUIDITY_STATE_LAYOUT_V4, MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from '@raydium-io/raydium-sdk';
|
||||
import bs58 from 'bs58';
|
||||
import { Connection, PublicKey } from '@solana/web3.js';
|
||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||
import { EventEmitter } from 'events';
|
||||
import { LIQUIDITY_STATE_LAYOUT_V4, MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from "@raydium-io/raydium-sdk";
|
||||
import bs58 from "bs58";
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
export class Listeners extends EventEmitter {
|
||||
private subscriptions: number[] = [];
|
||||
private subscriptions: number[] = [];
|
||||
|
||||
constructor(private readonly connection: Connection) {
|
||||
super();
|
||||
}
|
||||
constructor(private readonly connection: Connection) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async start(config: {
|
||||
walletPublicKey: PublicKey;
|
||||
quoteToken: Token;
|
||||
autoSell: boolean;
|
||||
cacheNewMarkets: boolean;
|
||||
}) {
|
||||
if (config.cacheNewMarkets) {
|
||||
const openBookSubscription = await this.subscribeToOpenBookMarkets(config);
|
||||
this.subscriptions.push(openBookSubscription);
|
||||
}
|
||||
public async start(config: {
|
||||
walletPublicKey: PublicKey;
|
||||
quoteToken: Token;
|
||||
autoSell: boolean;
|
||||
cacheNewMarkets: boolean;
|
||||
}) {
|
||||
if (config.cacheNewMarkets) {
|
||||
const openBookSubscription = await this.subscribeToOpenBookMarkets(config);
|
||||
this.subscriptions.push(openBookSubscription);
|
||||
}
|
||||
|
||||
const raydiumSubscription = await this.subscribeToRaydiumPools(config);
|
||||
this.subscriptions.push(raydiumSubscription);
|
||||
const raydiumSubscription = await this.subscribeToRaydiumPools(config);
|
||||
this.subscriptions.push(raydiumSubscription);
|
||||
|
||||
if (config.autoSell) {
|
||||
const walletSubscription = await this.subscribeToWalletChanges(config);
|
||||
this.subscriptions.push(walletSubscription);
|
||||
}
|
||||
}
|
||||
if (config.autoSell) {
|
||||
const walletSubscription = await this.subscribeToWalletChanges(config);
|
||||
this.subscriptions.push(walletSubscription);
|
||||
}
|
||||
}
|
||||
|
||||
private async subscribeToOpenBookMarkets(config: { quoteToken: Token }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
MAINNET_PROGRAM_ID.OPENBOOK_MARKET,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit('market', updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
private async subscribeToOpenBookMarkets(config: { quoteToken: Token }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
MAINNET_PROGRAM_ID.OPENBOOK_MARKET,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit("market", updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private async subscribeToRaydiumPools(config: { quoteToken: Token }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
MAINNET_PROGRAM_ID.AmmV4,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit('pool', updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{ dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('quoteMint'),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('marketProgramId'),
|
||||
bytes: MAINNET_PROGRAM_ID.OPENBOOK_MARKET.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('status'),
|
||||
bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
private async subscribeToRaydiumPools(config: { quoteToken: Token }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
MAINNET_PROGRAM_ID.AmmV4,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit("pool", updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{ dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("quoteMint"),
|
||||
bytes: config.quoteToken.mint.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("marketProgramId"),
|
||||
bytes: MAINNET_PROGRAM_ID.OPENBOOK_MARKET.toBase58(),
|
||||
},
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("status"),
|
||||
bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private async subscribeToWalletChanges(config: { walletPublicKey: PublicKey }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
TOKEN_PROGRAM_ID,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit('wallet', updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{
|
||||
dataSize: 165,
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: 32,
|
||||
bytes: config.walletPublicKey.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
private async subscribeToWalletChanges(config: { walletPublicKey: PublicKey }) {
|
||||
return this.connection.onProgramAccountChange(
|
||||
TOKEN_PROGRAM_ID,
|
||||
async (updatedAccountInfo) => {
|
||||
this.emit("wallet", updatedAccountInfo);
|
||||
},
|
||||
this.connection.commitment,
|
||||
[
|
||||
{
|
||||
dataSize: 165,
|
||||
},
|
||||
{
|
||||
memcmp: {
|
||||
offset: 32,
|
||||
bytes: config.walletPublicKey.toBase58(),
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
for (let i = this.subscriptions.length; i >= 0; --i) {
|
||||
const subscription = this.subscriptions[i];
|
||||
await this.connection.removeAccountChangeListener(subscription);
|
||||
this.subscriptions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
public async stop() {
|
||||
for (let i = this.subscriptions.length; i >= 0; --i) {
|
||||
const subscription = this.subscriptions[i];
|
||||
await this.connection.removeAccountChangeListener(subscription);
|
||||
this.subscriptions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
409
package-lock.json
generated
409
package-lock.json
generated
@ -24,7 +24,8 @@
|
||||
"npm": "^10.5.2",
|
||||
"pino": "^8.18.0",
|
||||
"pino-pretty": "^10.3.1",
|
||||
"pino-std-serializers": "^6.2.2"
|
||||
"pino-std-serializers": "^6.2.2",
|
||||
"request": "^2.88.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.5",
|
||||
@ -456,11 +457,45 @@
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"license": "MIT",
|
||||
@ -479,6 +514,21 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/aws4": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.8",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
|
||||
@ -511,6 +561,21 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf/node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/big.js": {
|
||||
"version": "6.2.1",
|
||||
"license": "MIT",
|
||||
@ -622,6 +687,12 @@
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"license": "MIT",
|
||||
@ -648,6 +719,12 @@
|
||||
"version": "2.20.3",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-hash": {
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
@ -676,6 +753,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "4.6.3",
|
||||
"license": "MIT",
|
||||
@ -726,6 +815,16 @@
|
||||
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ed25519-hd-key": {
|
||||
"version": "1.3.0",
|
||||
"license": "MIT",
|
||||
@ -770,6 +869,21 @@
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eyes": {
|
||||
"version": "0.1.8",
|
||||
"engines": {
|
||||
@ -780,6 +894,18 @@
|
||||
"version": "3.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-redact": {
|
||||
"version": "3.2.0",
|
||||
"license": "MIT",
|
||||
@ -827,6 +953,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"license": "MIT",
|
||||
@ -839,6 +974,38 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"deprecated": "this library is no longer supported",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/hash-base": {
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
@ -867,6 +1034,21 @@
|
||||
"version": "5.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8",
|
||||
"npm": ">=1.3.7"
|
||||
}
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"license": "MIT",
|
||||
@ -902,6 +1084,12 @@
|
||||
"version": "2.0.4",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isomorphic-ws": {
|
||||
"version": "4.0.1",
|
||||
"license": "MIT",
|
||||
@ -909,6 +1097,12 @@
|
||||
"ws": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jayson": {
|
||||
"version": "4.1.0",
|
||||
"license": "MIT",
|
||||
@ -940,6 +1134,24 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"license": "ISC"
|
||||
@ -965,6 +1177,21 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.4.0",
|
||||
"verror": "1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"license": "MIT"
|
||||
@ -3605,6 +3832,15 @@
|
||||
"inBundle": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/on-exit-leak-free": {
|
||||
"version": "2.1.0",
|
||||
"license": "MIT"
|
||||
@ -3616,6 +3852,12 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "8.18.0",
|
||||
"license": "MIT",
|
||||
@ -3700,6 +3942,12 @@
|
||||
"version": "1.1.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"license": "MIT",
|
||||
@ -3708,6 +3956,24 @@
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/quick-format-unescaped": {
|
||||
"version": "4.0.4",
|
||||
"license": "MIT"
|
||||
@ -3737,6 +4003,62 @@
|
||||
"version": "0.14.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.2",
|
||||
"har-validator": "~5.1.3",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/request/node_modules/form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/request/node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/ripemd160": {
|
||||
"version": "2.0.2",
|
||||
"license": "MIT",
|
||||
@ -3807,6 +4129,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"license": "BSD-3-Clause"
|
||||
@ -3836,6 +4164,37 @@
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"bin": {
|
||||
"sshpk-conv": "bin/sshpk-conv",
|
||||
"sshpk-sign": "bin/sshpk-sign",
|
||||
"sshpk-verify": "bin/sshpk-verify"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sshpk/node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"license": "MIT",
|
||||
@ -3875,6 +4234,19 @@
|
||||
"version": "2.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"license": "MIT"
|
||||
@ -3925,6 +4297,18 @@
|
||||
"version": "2.6.2",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "1.0.3",
|
||||
"license": "Unlicense"
|
||||
@ -3941,6 +4325,15 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"hasInstallScript": true,
|
||||
@ -3969,6 +4362,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"license": "BSD-2-Clause"
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
"homepage": "https://warp.id",
|
||||
"version": "2.0.2",
|
||||
"scripts": {
|
||||
"start": "ts-node index.ts",
|
||||
"tsc": "tsc --noEmit"
|
||||
"start": "npm run cache | node index.js",
|
||||
"tsc": "tsc --noEmit",
|
||||
"cache": "node helpers/helper.cache.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@metaplex-foundation/mpl-token-metadata": "^3.2.1",
|
||||
@ -24,7 +25,8 @@
|
||||
"npm": "^10.5.2",
|
||||
"pino": "^8.18.0",
|
||||
"pino-pretty": "^10.3.1",
|
||||
"pino-std-serializers": "^6.2.2"
|
||||
"pino-std-serializers": "^6.2.2",
|
||||
"request": "^2.88.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.5",
|
||||
|
||||
44
transactions/default-transaction-executor.js
Normal file
44
transactions/default-transaction-executor.js
Normal file
@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultTransactionExecutor = void 0;
|
||||
const helpers_1 = require("../helpers");
|
||||
class DefaultTransactionExecutor {
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
executeAndConfirm(transaction, payer, latestBlockhash) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
helpers_1.logger.debug("Executing transaction...");
|
||||
const signature = yield this.execute(transaction);
|
||||
helpers_1.logger.debug({ signature }, "Confirming transaction...");
|
||||
return this.confirm(signature, latestBlockhash);
|
||||
});
|
||||
}
|
||||
execute(transaction) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return this.connection.sendRawTransaction(transaction.serialize(), {
|
||||
preflightCommitment: this.connection.commitment,
|
||||
});
|
||||
});
|
||||
}
|
||||
confirm(signature, latestBlockhash) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const confirmation = yield this.connection.confirmTransaction({
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
}, this.connection.commitment);
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultTransactionExecutor = DefaultTransactionExecutor;
|
||||
@ -1,44 +1,44 @@
|
||||
import {
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Connection,
|
||||
Keypair,
|
||||
Transaction,
|
||||
VersionedTransaction,
|
||||
} from '@solana/web3.js';
|
||||
import { TransactionExecutor } from './transaction-executor.interface';
|
||||
import { logger } from '../helpers';
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Connection,
|
||||
Keypair,
|
||||
Transaction,
|
||||
VersionedTransaction,
|
||||
} from "@solana/web3.js";
|
||||
import { TransactionExecutor } from "./transaction-executor.interface";
|
||||
import { logger } from "../helpers";
|
||||
|
||||
export class DefaultTransactionExecutor implements TransactionExecutor {
|
||||
constructor(private readonly connection: Connection) {}
|
||||
constructor(private readonly connection: Connection) {}
|
||||
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string, error?: string }> {
|
||||
logger.debug('Executing transaction...');
|
||||
const signature = await this.execute(transaction);
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }> {
|
||||
logger.debug("Executing transaction...");
|
||||
const signature = await this.execute(transaction);
|
||||
|
||||
logger.debug({ signature }, 'Confirming transaction...');
|
||||
return this.confirm(signature, latestBlockhash);
|
||||
}
|
||||
logger.debug({ signature }, "Confirming transaction...");
|
||||
return this.confirm(signature, latestBlockhash);
|
||||
}
|
||||
|
||||
private async execute(transaction: Transaction | VersionedTransaction) {
|
||||
return this.connection.sendRawTransaction(transaction.serialize(), {
|
||||
preflightCommitment: this.connection.commitment,
|
||||
});
|
||||
}
|
||||
private async execute(transaction: Transaction | VersionedTransaction) {
|
||||
return this.connection.sendRawTransaction(transaction.serialize(), {
|
||||
preflightCommitment: this.connection.commitment,
|
||||
});
|
||||
}
|
||||
|
||||
private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
|
||||
const confirmation = await this.connection.confirmTransaction(
|
||||
{
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
},
|
||||
this.connection.commitment,
|
||||
);
|
||||
private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
|
||||
const confirmation = await this.connection.confirmTransaction(
|
||||
{
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
},
|
||||
this.connection.commitment,
|
||||
);
|
||||
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
}
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
}
|
||||
}
|
||||
|
||||
18
transactions/index.js
Normal file
18
transactions/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./default-transaction-executor"), exports);
|
||||
__exportStar(require("./transaction-executor.interface"), exports);
|
||||
@ -1,2 +1,2 @@
|
||||
export * from './default-transaction-executor';
|
||||
export * from './transaction-executor.interface';
|
||||
export * from "./default-transaction-executor";
|
||||
export * from "./transaction-executor.interface";
|
||||
|
||||
139
transactions/jito-rpc-transaction-executor.js
Normal file
139
transactions/jito-rpc-transaction-executor.js
Normal file
@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.JitoTransactionExecutor = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const helpers_1 = require("../helpers");
|
||||
const axios_1 = __importStar(require("axios"));
|
||||
const bs58_1 = __importDefault(require("bs58"));
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
class JitoTransactionExecutor {
|
||||
constructor(jitoFee, connection) {
|
||||
this.jitoFee = jitoFee;
|
||||
this.connection = connection;
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/bundles/gettipaccounts
|
||||
this.jitpTipAccounts = [
|
||||
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
|
||||
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
|
||||
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
|
||||
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
|
||||
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
|
||||
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
|
||||
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
|
||||
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
|
||||
];
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey();
|
||||
}
|
||||
getRandomValidatorKey() {
|
||||
const randomValidator = this.jitpTipAccounts[Math.floor(Math.random() * this.jitpTipAccounts.length)];
|
||||
return new web3_js_1.PublicKey(randomValidator);
|
||||
}
|
||||
executeAndConfirm(transaction, payer, latestBlockhash) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
helpers_1.logger.debug("Starting Jito transaction execution...");
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey(); // Update wallet key each execution
|
||||
helpers_1.logger.trace(`Selected Jito fee wallet: ${this.JitoFeeWallet.toBase58()}`);
|
||||
try {
|
||||
const fee = new raydium_sdk_1.CurrencyAmount(raydium_sdk_1.Currency.SOL, this.jitoFee, false).raw.toNumber();
|
||||
helpers_1.logger.trace(`Calculated fee: ${fee} lamports`);
|
||||
const jitTipTxFeeMessage = new web3_js_1.TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
web3_js_1.SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.JitoFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
const jitoFeeTx = new web3_js_1.VersionedTransaction(jitTipTxFeeMessage);
|
||||
jitoFeeTx.sign([payer]);
|
||||
const jitoTxsignature = bs58_1.default.encode(jitoFeeTx.signatures[0]);
|
||||
// Serialize the transactions once here
|
||||
const serializedjitoFeeTx = bs58_1.default.encode(jitoFeeTx.serialize());
|
||||
const serializedTransaction = bs58_1.default.encode(transaction.serialize());
|
||||
const serializedTransactions = [serializedjitoFeeTx, serializedTransaction];
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url
|
||||
const endpoints = [
|
||||
"https://mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
];
|
||||
const requests = endpoints.map((url) => axios_1.default.post(url, {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "sendBundle",
|
||||
params: [serializedTransactions],
|
||||
}));
|
||||
helpers_1.logger.trace("Sending transactions to endpoints...");
|
||||
const results = yield Promise.all(requests.map((p) => p.catch((e) => e)));
|
||||
const successfulResults = results.filter((result) => !(result instanceof Error));
|
||||
if (successfulResults.length > 0) {
|
||||
helpers_1.logger.trace(`At least one successful response`);
|
||||
helpers_1.logger.debug(`Confirming jito transaction...`);
|
||||
return yield this.confirm(jitoTxsignature, latestBlockhash);
|
||||
}
|
||||
else {
|
||||
helpers_1.logger.debug(`No successful responses received for jito`);
|
||||
}
|
||||
return { confirmed: false };
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof axios_1.AxiosError) {
|
||||
helpers_1.logger.trace({ error: (_a = error.response) === null || _a === void 0 ? void 0 : _a.data }, "Failed to execute jito transaction");
|
||||
}
|
||||
helpers_1.logger.error("Error during transaction execution", error);
|
||||
return { confirmed: false };
|
||||
}
|
||||
});
|
||||
}
|
||||
confirm(signature, latestBlockhash) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const confirmation = yield this.connection.confirmTransaction({
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
}, this.connection.commitment);
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.JitoTransactionExecutor = JitoTransactionExecutor;
|
||||
@ -1,131 +1,131 @@
|
||||
import {
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Connection,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from '@solana/web3.js';
|
||||
import { TransactionExecutor } from './transaction-executor.interface';
|
||||
import { logger } from '../helpers';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import bs58 from 'bs58';
|
||||
import { Currency, CurrencyAmount } from '@raydium-io/raydium-sdk';
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Connection,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from "@solana/web3.js";
|
||||
import { TransactionExecutor } from "./transaction-executor.interface";
|
||||
import { logger } from "../helpers";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import bs58 from "bs58";
|
||||
import { Currency, CurrencyAmount } from "@raydium-io/raydium-sdk";
|
||||
|
||||
export class JitoTransactionExecutor implements TransactionExecutor {
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/bundles/gettipaccounts
|
||||
private jitpTipAccounts = [
|
||||
'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY',
|
||||
'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL',
|
||||
'96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5',
|
||||
'3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT',
|
||||
'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe',
|
||||
'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49',
|
||||
'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt',
|
||||
'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh',
|
||||
];
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/bundles/gettipaccounts
|
||||
private jitpTipAccounts = [
|
||||
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
|
||||
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
|
||||
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
|
||||
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
|
||||
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
|
||||
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
|
||||
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
|
||||
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
|
||||
];
|
||||
|
||||
private JitoFeeWallet: PublicKey;
|
||||
private JitoFeeWallet: PublicKey;
|
||||
|
||||
constructor(
|
||||
private readonly jitoFee: string,
|
||||
private readonly connection: Connection,
|
||||
) {
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey();
|
||||
}
|
||||
constructor(
|
||||
private readonly jitoFee: string,
|
||||
private readonly connection: Connection,
|
||||
) {
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey();
|
||||
}
|
||||
|
||||
private getRandomValidatorKey(): PublicKey {
|
||||
const randomValidator = this.jitpTipAccounts[Math.floor(Math.random() * this.jitpTipAccounts.length)];
|
||||
return new PublicKey(randomValidator);
|
||||
}
|
||||
private getRandomValidatorKey(): PublicKey {
|
||||
const randomValidator = this.jitpTipAccounts[Math.floor(Math.random() * this.jitpTipAccounts.length)];
|
||||
return new PublicKey(randomValidator);
|
||||
}
|
||||
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }> {
|
||||
logger.debug('Starting Jito transaction execution...');
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey(); // Update wallet key each execution
|
||||
logger.trace(`Selected Jito fee wallet: ${this.JitoFeeWallet.toBase58()}`);
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }> {
|
||||
logger.debug("Starting Jito transaction execution...");
|
||||
this.JitoFeeWallet = this.getRandomValidatorKey(); // Update wallet key each execution
|
||||
logger.trace(`Selected Jito fee wallet: ${this.JitoFeeWallet.toBase58()}`);
|
||||
|
||||
try {
|
||||
const fee = new CurrencyAmount(Currency.SOL, this.jitoFee, false).raw.toNumber();
|
||||
logger.trace(`Calculated fee: ${fee} lamports`);
|
||||
try {
|
||||
const fee = new CurrencyAmount(Currency.SOL, this.jitoFee, false).raw.toNumber();
|
||||
logger.trace(`Calculated fee: ${fee} lamports`);
|
||||
|
||||
const jitTipTxFeeMessage = new TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.JitoFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
const jitTipTxFeeMessage = new TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.JitoFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
|
||||
const jitoFeeTx = new VersionedTransaction(jitTipTxFeeMessage);
|
||||
jitoFeeTx.sign([payer]);
|
||||
const jitoFeeTx = new VersionedTransaction(jitTipTxFeeMessage);
|
||||
jitoFeeTx.sign([payer]);
|
||||
|
||||
const jitoTxsignature = bs58.encode(jitoFeeTx.signatures[0]);
|
||||
const jitoTxsignature = bs58.encode(jitoFeeTx.signatures[0]);
|
||||
|
||||
// Serialize the transactions once here
|
||||
const serializedjitoFeeTx = bs58.encode(jitoFeeTx.serialize());
|
||||
const serializedTransaction = bs58.encode(transaction.serialize());
|
||||
const serializedTransactions = [serializedjitoFeeTx, serializedTransaction];
|
||||
// Serialize the transactions once here
|
||||
const serializedjitoFeeTx = bs58.encode(jitoFeeTx.serialize());
|
||||
const serializedTransaction = bs58.encode(transaction.serialize());
|
||||
const serializedTransactions = [serializedjitoFeeTx, serializedTransaction];
|
||||
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url
|
||||
const endpoints = [
|
||||
'https://mainnet.block-engine.jito.wtf/api/v1/bundles',
|
||||
'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles',
|
||||
'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles',
|
||||
'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles',
|
||||
'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles',
|
||||
];
|
||||
// https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url
|
||||
const endpoints = [
|
||||
"https://mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
"https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles",
|
||||
];
|
||||
|
||||
const requests = endpoints.map((url) =>
|
||||
axios.post(url, {
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'sendBundle',
|
||||
params: [serializedTransactions],
|
||||
}),
|
||||
);
|
||||
const requests = endpoints.map((url) =>
|
||||
axios.post(url, {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "sendBundle",
|
||||
params: [serializedTransactions],
|
||||
}),
|
||||
);
|
||||
|
||||
logger.trace('Sending transactions to endpoints...');
|
||||
const results = await Promise.all(requests.map((p) => p.catch((e) => e)));
|
||||
logger.trace("Sending transactions to endpoints...");
|
||||
const results = await Promise.all(requests.map((p) => p.catch((e) => e)));
|
||||
|
||||
const successfulResults = results.filter((result) => !(result instanceof Error));
|
||||
const successfulResults = results.filter((result) => !(result instanceof Error));
|
||||
|
||||
if (successfulResults.length > 0) {
|
||||
logger.trace(`At least one successful response`);
|
||||
logger.debug(`Confirming jito transaction...`);
|
||||
return await this.confirm(jitoTxsignature, latestBlockhash);
|
||||
} else {
|
||||
logger.debug(`No successful responses received for jito`);
|
||||
}
|
||||
if (successfulResults.length > 0) {
|
||||
logger.trace(`At least one successful response`);
|
||||
logger.debug(`Confirming jito transaction...`);
|
||||
return await this.confirm(jitoTxsignature, latestBlockhash);
|
||||
} else {
|
||||
logger.debug(`No successful responses received for jito`);
|
||||
}
|
||||
|
||||
return { confirmed: false };
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
logger.trace({ error: error.response?.data }, 'Failed to execute jito transaction');
|
||||
}
|
||||
logger.error('Error during transaction execution', error);
|
||||
return { confirmed: false };
|
||||
}
|
||||
}
|
||||
return { confirmed: false };
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
logger.trace({ error: error.response?.data }, "Failed to execute jito transaction");
|
||||
}
|
||||
logger.error("Error during transaction execution", error);
|
||||
return { confirmed: false };
|
||||
}
|
||||
}
|
||||
|
||||
private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
|
||||
const confirmation = await this.connection.confirmTransaction(
|
||||
{
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
},
|
||||
this.connection.commitment,
|
||||
);
|
||||
private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
|
||||
const confirmation = await this.connection.confirmTransaction(
|
||||
{
|
||||
signature,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
},
|
||||
this.connection.commitment,
|
||||
);
|
||||
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
}
|
||||
return { confirmed: !confirmation.value.err, signature };
|
||||
}
|
||||
}
|
||||
|
||||
2
transactions/transaction-executor.interface.js
Normal file
2
transactions/transaction-executor.interface.js
Normal file
@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
@ -1,9 +1,9 @@
|
||||
import { BlockhashWithExpiryBlockHeight, Keypair, VersionedTransaction } from '@solana/web3.js';
|
||||
import { BlockhashWithExpiryBlockHeight, Keypair, VersionedTransaction } from "@solana/web3.js";
|
||||
|
||||
export interface TransactionExecutor {
|
||||
executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockHash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string, error?: string }>;
|
||||
executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockHash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }>;
|
||||
}
|
||||
|
||||
85
transactions/warp-transaction-executor.js
Normal file
85
transactions/warp-transaction-executor.js
Normal file
@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WarpTransactionExecutor = void 0;
|
||||
const web3_js_1 = require("@solana/web3.js");
|
||||
const helpers_1 = require("../helpers");
|
||||
const axios_1 = __importStar(require("axios"));
|
||||
const bs58_1 = __importDefault(require("bs58"));
|
||||
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
|
||||
class WarpTransactionExecutor {
|
||||
constructor(warpFee) {
|
||||
this.warpFee = warpFee;
|
||||
this.warpFeeWallet = new web3_js_1.PublicKey("WARPzUMPnycu9eeCZ95rcAUxorqpBqHndfV3ZP5FSyS");
|
||||
}
|
||||
executeAndConfirm(transaction, payer, latestBlockhash) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
helpers_1.logger.debug("Executing transaction...");
|
||||
try {
|
||||
const fee = new raydium_sdk_1.CurrencyAmount(raydium_sdk_1.Currency.SOL, this.warpFee, false).raw.toNumber();
|
||||
const warpFeeMessage = new web3_js_1.TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
web3_js_1.SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.warpFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
const warpFeeTx = new web3_js_1.VersionedTransaction(warpFeeMessage);
|
||||
warpFeeTx.sign([payer]);
|
||||
const response = yield axios_1.default.post("https://tx.warp.id/transaction/execute", {
|
||||
transactions: [bs58_1.default.encode(warpFeeTx.serialize()), bs58_1.default.encode(transaction.serialize())],
|
||||
latestBlockhash,
|
||||
}, {
|
||||
timeout: 100000,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof axios_1.AxiosError) {
|
||||
helpers_1.logger.trace({ error: (_a = error.response) === null || _a === void 0 ? void 0 : _a.data }, "Failed to execute warp transaction");
|
||||
}
|
||||
}
|
||||
return { confirmed: false };
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.WarpTransactionExecutor = WarpTransactionExecutor;
|
||||
@ -1,64 +1,64 @@
|
||||
import {
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from '@solana/web3.js';
|
||||
import { TransactionExecutor } from './transaction-executor.interface';
|
||||
import { logger } from '../helpers';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import bs58 from 'bs58';
|
||||
import { Currency, CurrencyAmount } from '@raydium-io/raydium-sdk';
|
||||
BlockhashWithExpiryBlockHeight,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from "@solana/web3.js";
|
||||
import { TransactionExecutor } from "./transaction-executor.interface";
|
||||
import { logger } from "../helpers";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import bs58 from "bs58";
|
||||
import { Currency, CurrencyAmount } from "@raydium-io/raydium-sdk";
|
||||
|
||||
export class WarpTransactionExecutor implements TransactionExecutor {
|
||||
private readonly warpFeeWallet = new PublicKey('WARPzUMPnycu9eeCZ95rcAUxorqpBqHndfV3ZP5FSyS');
|
||||
private readonly warpFeeWallet = new PublicKey("WARPzUMPnycu9eeCZ95rcAUxorqpBqHndfV3ZP5FSyS");
|
||||
|
||||
constructor(private readonly warpFee: string) {}
|
||||
constructor(private readonly warpFee: string) {}
|
||||
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }> {
|
||||
logger.debug('Executing transaction...');
|
||||
public async executeAndConfirm(
|
||||
transaction: VersionedTransaction,
|
||||
payer: Keypair,
|
||||
latestBlockhash: BlockhashWithExpiryBlockHeight,
|
||||
): Promise<{ confirmed: boolean; signature?: string; error?: string }> {
|
||||
logger.debug("Executing transaction...");
|
||||
|
||||
try {
|
||||
const fee = new CurrencyAmount(Currency.SOL, this.warpFee, false).raw.toNumber();
|
||||
const warpFeeMessage = new TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.warpFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
try {
|
||||
const fee = new CurrencyAmount(Currency.SOL, this.warpFee, false).raw.toNumber();
|
||||
const warpFeeMessage = new TransactionMessage({
|
||||
payerKey: payer.publicKey,
|
||||
recentBlockhash: latestBlockhash.blockhash,
|
||||
instructions: [
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: this.warpFeeWallet,
|
||||
lamports: fee,
|
||||
}),
|
||||
],
|
||||
}).compileToV0Message();
|
||||
|
||||
const warpFeeTx = new VersionedTransaction(warpFeeMessage);
|
||||
warpFeeTx.sign([payer]);
|
||||
const warpFeeTx = new VersionedTransaction(warpFeeMessage);
|
||||
warpFeeTx.sign([payer]);
|
||||
|
||||
const response = await axios.post<{ confirmed: boolean; signature: string; error?: string }>(
|
||||
'https://tx.warp.id/transaction/execute',
|
||||
{
|
||||
transactions: [bs58.encode(warpFeeTx.serialize()), bs58.encode(transaction.serialize())],
|
||||
latestBlockhash,
|
||||
},
|
||||
{
|
||||
timeout: 100000,
|
||||
},
|
||||
);
|
||||
const response = await axios.post<{ confirmed: boolean; signature: string; error?: string }>(
|
||||
"https://tx.warp.id/transaction/execute",
|
||||
{
|
||||
transactions: [bs58.encode(warpFeeTx.serialize()), bs58.encode(transaction.serialize())],
|
||||
latestBlockhash,
|
||||
},
|
||||
{
|
||||
timeout: 100000,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
logger.trace({ error: error.response?.data }, 'Failed to execute warp transaction');
|
||||
}
|
||||
}
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
logger.trace({ error: error.response?.data }, "Failed to execute warp transaction");
|
||||
}
|
||||
}
|
||||
|
||||
return { confirmed: false };
|
||||
}
|
||||
return { confirmed: false };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user