diff --git a/.env.copy b/.env.copy index 2a4bdfd..3e24846 100644 --- a/.env.copy +++ b/.env.copy @@ -42,8 +42,9 @@ SNIPE_LIST_REFRESH_INTERVAL=30000 FILTER_CHECK_DURATION=60000 FILTER_CHECK_INTERVAL=2000 CONSECUTIVE_FILTER_MATCHES=3 +CHECK_IF_MUTABLE=false CHECK_IF_MINT_IS_RENOUNCED=true -CHECK_IF_FREEZABLE=true +CHECK_IF_FREEZABLE=false CHECK_IF_BURNED=true MIN_POOL_SIZE=5 MAX_POOL_SIZE=50 diff --git a/README.md b/README.md index 9f9957d..d9bb52e 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Note: When using snipe list filters below will be disabled. - Set to zero to disable filters. - `CONSECUTIVE_FILTER_MATCHES` - How many times in a row pool needs to match the filters. - This is useful because when pool is burned (and rugged), other filters may not report the same behavior. eg. pool size may still have old value +- `CHECK_IF_MUTABLE` - Set to `true` to buy tokens only if their metadata are not mutable. - `CHECK_IF_MINT_IS_RENOUNCED` - Set to `true` to buy tokens only if their mint is renounced. - `CHECK_IF_FREEZABLE` - Set to `true` to buy tokens only if they are not freezable. - `CHECK_IF_BURNED` - Set to `true` to buy tokens only if their liquidity pool is burned. diff --git a/filters/index.ts b/filters/index.ts index d2ba963..697388b 100644 --- a/filters/index.ts +++ b/filters/index.ts @@ -1,4 +1,5 @@ export * from './burn.filter'; +export * from './mutable.filter'; export * from './pool-filters'; export * from './pool-size.filter'; export * from './renounced.filter'; diff --git a/filters/mutable.filter.ts b/filters/mutable.filter.ts new file mode 100644 index 0000000..a12c74e --- /dev/null +++ b/filters/mutable.filter.ts @@ -0,0 +1,29 @@ +import { Filter, FilterResult } from './pool-filters'; +import { Connection, PublicKey } from '@solana/web3.js'; +import { LiquidityPoolKeysV4 } from '@raydium-io/raydium-sdk'; +import { getPdaMetadataKey } from '@raydium-io/raydium-sdk'; +import { getMetadataAccountDataSerializer, MetadataAccountData, MetadataAccountDataArgs } from '@metaplex-foundation/mpl-token-metadata'; +import { Serializer } from '@metaplex-foundation/umi/serializers'; +import { logger } from '../helpers'; + +export class MutableFilter implements Filter { + constructor(private readonly connection: Connection, private readonly metadataSerializer: Serializer) {} + + async execute(poolKeys: LiquidityPoolKeysV4): Promise { + try { + const metadataPDA = getPdaMetadataKey(poolKeys.baseMint); + const metadataAccount = await this.connection.getAccountInfo(metadataPDA.publicKey); + if (!metadataAccount?.data) { + return { ok: false, message: 'Mutable -> Failed to fetch account data' }; + } + const deserialize = this.metadataSerializer.deserialize(metadataAccount.data); + const mutable = deserialize[0].isMutable; + + return { ok: !mutable, message: !mutable ? undefined : "Mutable -> Creator can change metadata" }; + } catch (e: any) { + logger.error({ mint: poolKeys.baseMint }, `Mutable -> Failed to check if metadata are mutable`); + } + + return { ok: false, message: 'Mutable -> Failed to check if metadata are mutable' }; + } +} diff --git a/filters/pool-filters.ts b/filters/pool-filters.ts index fc7d457..350a4d9 100644 --- a/filters/pool-filters.ts +++ b/filters/pool-filters.ts @@ -1,9 +1,11 @@ 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, logger } from '../helpers'; +import { CHECK_IF_BURNED, CHECK_IF_FREEZABLE, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_MUTABLE, logger } from '../helpers'; export interface Filter { execute(poolKeysV4: LiquidityPoolKeysV4): Promise; @@ -35,6 +37,10 @@ export class PoolFilters { this.filters.push(new RenouncedFreezeFilter(connection, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_FREEZABLE)); } + if (CHECK_IF_MUTABLE) { + this.filters.push(new MutableFilter(connection, getMetadataAccountDataSerializer())); + } + if (!args.minPoolSize.isZero() || !args.maxPoolSize.isZero()) { this.filters.push(new PoolSizeFilter(connection, args.quoteToken, args.minPoolSize, args.maxPoolSize)); } diff --git a/helpers/constants.ts b/helpers/constants.ts index 2e8da8f..1ad8e76 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -54,6 +54,7 @@ export const SELL_SLIPPAGE = Number(retrieveEnvVariable('SELL_SLIPPAGE', 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_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'; diff --git a/index.ts b/index.ts index 2c7ae33..72028d7 100644 --- a/index.ts +++ b/index.ts @@ -14,6 +14,7 @@ import { RPC_WEBSOCKET_ENDPOINT, PRE_LOAD_EXISTING_MARKETS, LOG_LEVEL, + CHECK_IF_MUTABLE, CHECK_IF_MINT_IS_RENOUNCED, CHECK_IF_FREEZABLE, CHECK_IF_BURNED, diff --git a/package-lock.json b/package-lock.json index b11b17c..d37e058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "warp-solana-bot", "version": "2.0.1", "dependencies": { + "@metaplex-foundation/mpl-token-metadata": "^3.2.1", "@raydium-io/raydium-sdk": "^1.3.1-beta.47", "@solana/spl-token": "^0.4.0", "@solana/web3.js": "^1.89.1", @@ -75,6 +76,88 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@metaplex-foundation/mpl-token-metadata": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-3.2.1.tgz", + "integrity": "sha512-26W1NhQwDWmLOg/pBRYut7x/vEs/5kFS2sWVEY5/X0f2jJOLhnd4NaZQcq+5u+XZsXvm1jq2AtrRGPNK43oqWQ==", + "dependencies": { + "@metaplex-foundation/mpl-toolbox": "^0.9.4" + }, + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" + } + }, + "node_modules/@metaplex-foundation/mpl-toolbox": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-toolbox/-/mpl-toolbox-0.9.4.tgz", + "integrity": "sha512-fd6JxfoLbj/MM8FG2x91KYVy1U6AjBQw4qjt7+Da3trzQaWnSaYHDcYRG/53xqfvZ9qofY1T2t53GXPlD87lnQ==", + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" + } + }, + "node_modules/@metaplex-foundation/umi": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi/-/umi-0.9.1.tgz", + "integrity": "sha512-IhHoOvp4vfO/++YL+78+iVuLM53+FDwUOZDYgH6lx0jYXyQ27BeaieeR5i+q3A9dz4KxQo5Nzc5aCA1109QGCQ==", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers": "^0.9.0" + } + }, + "node_modules/@metaplex-foundation/umi-options": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-options/-/umi-options-0.8.9.tgz", + "integrity": "sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==", + "peer": true + }, + "node_modules/@metaplex-foundation/umi-public-keys": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-public-keys/-/umi-public-keys-0.8.9.tgz", + "integrity": "sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers/-/umi-serializers-0.9.0.tgz", + "integrity": "sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers-core": "^0.8.9", + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9", + "@metaplex-foundation/umi-serializers-numbers": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-core": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-core/-/umi-serializers-core-0.8.9.tgz", + "integrity": "sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==", + "peer": true + }, + "node_modules/@metaplex-foundation/umi-serializers-encodings": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-encodings/-/umi-serializers-encodings-0.8.9.tgz", + "integrity": "sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-numbers": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-numbers/-/umi-serializers-numbers-0.8.9.tgz", + "integrity": "sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==", + "peer": true, + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, "node_modules/@noble/curves": { "version": "1.3.0", "license": "MIT", diff --git a/package.json b/package.json index 526c489..d619d4e 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "tsc": "tsc --noEmit" }, "dependencies": { + "@metaplex-foundation/mpl-token-metadata": "^3.2.1", "@raydium-io/raydium-sdk": "^1.3.1-beta.47", "@solana/spl-token": "^0.4.0", "@solana/web3.js": "^1.89.1",