@elizaos/plugin-dexscreener
Version:
DexScreener integration plugin for ElizaOS
947 lines (937 loc) • 31.1 kB
JavaScript
// src/service.ts
import { Service } from "@elizaos/core";
import axios from "axios";
var DexScreenerService = class _DexScreenerService extends Service {
static serviceType = "dexscreener";
api;
dexConfig;
lastRequestTime = 0;
capabilityDescription = "Provides DEX analytics and token information from DexScreener";
/**
* Start the DexScreenerService with the given runtime.
* @param {IAgentRuntime} runtime - The runtime for the DexScreenerService.
* @returns {Promise<Service>} A promise that resolves with the DexScreenerService instance.
*/
static async start(runtime) {
const service = new _DexScreenerService(runtime);
service.dexConfig = {
apiUrl: runtime.getSetting("DEXSCREENER_API_URL") || "https://api.dexscreener.com",
rateLimitDelay: parseInt(
runtime.getSetting("DEXSCREENER_RATE_LIMIT_DELAY") || "100"
)
};
service.api = axios.create({
baseURL: service.dexConfig.apiUrl,
timeout: 1e4,
headers: {
Accept: "application/json",
"User-Agent": "ElizaOS-DexScreener-Plugin/1.0"
}
});
return service;
}
async stop() {
console.log("DexScreener service stopped");
}
/**
* Ensure rate limiting between requests
*/
async rateLimit() {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest < this.dexConfig.rateLimitDelay) {
await new Promise(
(resolve) => setTimeout(
resolve,
this.dexConfig.rateLimitDelay - timeSinceLastRequest
)
);
}
this.lastRequestTime = Date.now();
}
/**
* Search for tokens/pairs
*/
async search(params) {
try {
await this.rateLimit();
const response = await this.api.get(`/latest/dex/search`, {
params: { q: params.query }
});
return {
success: true,
data: response.data.pairs || []
};
} catch (error) {
console.error("DexScreener search error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to search tokens"
};
}
}
/**
* Get token pairs by token address
*/
async getTokenPairs(params) {
try {
await this.rateLimit();
const response = await this.api.get(
`/latest/dex/tokens/${params.tokenAddress}`
);
return {
success: true,
data: response.data.pairs || []
};
} catch (error) {
console.error("DexScreener getTokenPairs error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get token pairs"
};
}
}
/**
* Get pair by address
*/
async getPair(params) {
try {
await this.rateLimit();
const response = await this.api.get(
`/latest/dex/pairs/${params.pairAddress}`
);
if (!response.data.pair) {
return {
success: false,
error: "Pair not found"
};
}
return {
success: true,
data: response.data.pair
};
} catch (error) {
console.error("DexScreener getPair error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get pair"
};
}
}
/**
* Get trending pairs
*/
async getTrending(params = {}) {
try {
await this.rateLimit();
const response = await this.api.get(`/token-boosts/top/v1`);
const boostedTokens = Array.isArray(response.data) ? response.data : [response.data];
const pairPromises = boostedTokens.slice(0, params.limit || 10).map(async (token) => {
try {
const pairResponse = await this.api.get(
`/tokens/v1/${token.chainId}/${token.tokenAddress}`
);
return Array.isArray(pairResponse.data) ? pairResponse.data[0] : null;
} catch (error) {
console.error(
`Failed to get pair data for ${token.tokenAddress}:`,
error
);
return null;
}
});
const pairs = (await Promise.all(pairPromises)).filter(
(pair) => pair !== null
);
return {
success: true,
data: pairs
};
} catch (error) {
console.error("DexScreener getTrending error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get trending pairs"
};
}
}
/**
* Get pairs by chain
*/
async getPairsByChain(params) {
try {
await this.rateLimit();
const response = await this.api.get(`/latest/dex/search`, {
params: {
q: params.chain
// Search by chain name
}
});
let pairs = response.data.pairs || [];
pairs = pairs.filter(
(pair) => pair.chainId.toLowerCase() === params.chain.toLowerCase()
);
if (params.sortBy) {
pairs.sort((a, b) => {
switch (params.sortBy) {
case "volume":
return (b.volume?.h24 || 0) - (a.volume?.h24 || 0);
case "liquidity":
return (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0);
case "priceChange":
return (b.priceChange?.h24 || 0) - (a.priceChange?.h24 || 0);
case "txns":
return (b.txns?.h24.buys + b.txns?.h24.sells || 0) - (a.txns?.h24.buys + a.txns?.h24.sells || 0);
default:
return 0;
}
});
}
const limitedPairs = params.limit ? pairs.slice(0, params.limit) : pairs.slice(0, 20);
return {
success: true,
data: limitedPairs
};
} catch (error) {
console.error("DexScreener getPairsByChain error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get pairs by chain"
};
}
}
/**
* Get new pairs
*/
async getNewPairs(params = {}) {
try {
await this.rateLimit();
const response = await this.api.get(`/token-profiles/latest/v1`);
const profiles = Array.isArray(response.data) ? response.data : [response.data];
const filteredProfiles = params.chain ? profiles.filter(
(p) => p.chainId?.toLowerCase() === params.chain?.toLowerCase()
) : profiles;
const pairPromises = filteredProfiles.slice(0, params.limit || 10).map(async (profile) => {
try {
const pairResponse = await this.api.get(
`/tokens/v1/${profile.chainId}/${profile.tokenAddress}`
);
const pairs2 = Array.isArray(pairResponse.data) ? pairResponse.data : [];
if (pairs2.length > 0) {
return {
...pairs2[0],
labels: pairs2[0].labels?.includes("new") ? pairs2[0].labels : [...pairs2[0].labels || [], "new"]
};
}
return null;
} catch (error) {
console.error(
`Failed to get pair data for ${profile.tokenAddress}:`,
error
);
return null;
}
});
const pairs = (await Promise.all(pairPromises)).filter(
(pair) => pair !== null
);
return {
success: true,
data: pairs
};
} catch (error) {
console.error("DexScreener getNewPairs error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get new pairs"
};
}
}
/**
* Get token profile
*/
async getTokenProfile(tokenAddress) {
try {
await this.rateLimit();
const response = await this.api.get(`/token-profiles/latest/v1`);
const profiles = Array.isArray(response.data) ? response.data : [response.data];
const profile = profiles.find(
(p) => p.tokenAddress?.toLowerCase() === tokenAddress.toLowerCase()
);
if (!profile) {
return {
success: false,
error: "Token profile not found"
};
}
return {
success: true,
data: profile
};
} catch (error) {
console.error("DexScreener getTokenProfile error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get token profile"
};
}
}
/**
* Format price with appropriate decimals
*/
formatPrice(price) {
const numPrice = typeof price === "string" ? parseFloat(price) : price;
if (numPrice >= 1) {
return numPrice.toFixed(2);
} else if (numPrice >= 0.01) {
return numPrice.toFixed(4);
} else {
return numPrice.toFixed(8);
}
}
/**
* Format percentage change
*/
formatPriceChange(change) {
const sign = change >= 0 ? "+" : "";
return `${sign}${change.toFixed(2)}%`;
}
/**
* Format volume/liquidity numbers
*/
formatUsdValue(value) {
if (value >= 1e6) {
return `$${(value / 1e6).toFixed(2)}M`;
} else if (value >= 1e3) {
return `$${(value / 1e3).toFixed(2)}K`;
} else {
return `$${value.toFixed(2)}`;
}
}
/**
* Get multiple tokens by addresses (up to 30)
*/
async getMultipleTokens(chainId, tokenAddresses) {
try {
if (tokenAddresses.length > 30) {
return {
success: false,
error: "Maximum 30 token addresses allowed"
};
}
await this.rateLimit();
const addresses = tokenAddresses.join(",");
const response = await this.api.get(`/tokens/v1/${chainId}/${addresses}`);
return {
success: true,
data: response.data || []
};
} catch (error) {
console.error("DexScreener getMultipleTokens error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get multiple tokens"
};
}
}
/**
* Get latest token profiles
*/
async getLatestTokenProfiles() {
try {
await this.rateLimit();
const response = await this.api.get(`/token-profiles/latest/v1`);
return {
success: true,
data: Array.isArray(response.data) ? response.data : [response.data]
};
} catch (error) {
console.error("DexScreener getLatestTokenProfiles error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get latest token profiles"
};
}
}
/**
* Get latest boosted tokens
*/
async getLatestBoostedTokens() {
try {
await this.rateLimit();
const response = await this.api.get(`/token-boosts/latest/v1`);
return {
success: true,
data: Array.isArray(response.data) ? response.data : [response.data]
};
} catch (error) {
console.error("DexScreener getLatestBoostedTokens error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get latest boosted tokens"
};
}
}
/**
* Get top boosted tokens
*/
async getTopBoostedTokens() {
try {
await this.rateLimit();
const response = await this.api.get(`/token-boosts/top/v1`);
return {
success: true,
data: Array.isArray(response.data) ? response.data : [response.data]
};
} catch (error) {
console.error("DexScreener getTopBoostedTokens error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get top boosted tokens"
};
}
}
/**
* Check order status for a token
*/
async checkOrderStatus(chainId, tokenAddress) {
try {
await this.rateLimit();
const response = await this.api.get(
`/orders/v1/${chainId}/${tokenAddress}`
);
return {
success: true,
data: response.data || []
};
} catch (error) {
console.error("DexScreener checkOrderStatus error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to check order status"
};
}
}
/**
* Get token pairs by chain and address
*/
async getTokenPairsByChain(chainId, tokenAddress) {
try {
await this.rateLimit();
const response = await this.api.get(
`/token-pairs/v1/${chainId}/${tokenAddress}`
);
return {
success: true,
data: response.data || []
};
} catch (error) {
console.error("DexScreener getTokenPairsByChain error:", error);
return {
success: false,
error: error.response?.data?.message || error.message || "Failed to get token pairs by chain"
};
}
}
};
// src/actions.ts
var searchTokensAction = {
name: "DEXSCREENER_SEARCH",
description: "Search for tokens or trading pairs on DexScreener by name, symbol, or contract address",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("search") || content.toLowerCase().includes("find") || content.toLowerCase().includes("look for");
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const queryMatch = content.match(
/(?:search|find|look for)\s+(?:for\s+)?(.+?)(?:\s+on\s+dexscreener)?$/i
);
if (!queryMatch) {
callback({
text: 'Please provide a search query. Example: "Search for PEPE"',
action: "DEXSCREENER_SEARCH"
});
return;
}
const result = await service.search({ query: queryMatch[1].trim() });
if (!result.success || !result.data) {
callback({
text: `Failed to search: ${result.error}`,
action: "DEXSCREENER_SEARCH"
});
return;
}
const pairs = result.data.slice(0, 5);
if (pairs.length === 0) {
callback({
text: `No results found for "${queryMatch[1].trim()}"`,
action: "DEXSCREENER_SEARCH"
});
return;
}
const pairList = pairs.map((pair, i) => {
const priceChange = service.formatPriceChange(pair.priceChange.h24);
return `**${i + 1}. ${pair.baseToken.symbol}/${pair.quoteToken.symbol}** on ${pair.dexId} (${pair.chainId})
\u{1F4B0} Price: ${service.formatPrice(pair.priceUsd || pair.priceNative)}
\u{1F4C8} 24h: ${priceChange} | Vol: ${service.formatUsdValue(pair.volume.h24)}
\u{1F4A7} Liq: ${pair.liquidity?.usd ? service.formatUsdValue(pair.liquidity.usd) : "N/A"}
\u{1F517} ${pair.url}`;
}).join("\n\n");
callback({
text: `**\u{1F50D} Search Results for "${queryMatch[1].trim()}"**
${pairList}`,
action: "DEXSCREENER_SEARCH",
data: pairs
});
return;
},
similes: ["find token", "look for", "search dexscreener"],
examples: [
[
{
name: "Alice",
content: { text: "Search for PEPE tokens" }
},
{
name: "Bob",
content: { text: "Find USDC pairs on dexscreener" }
}
]
]
};
var getTokenInfoAction = {
name: "DEXSCREENER_TOKEN_INFO",
description: "Get detailed information about a specific token including price, volume, liquidity, and trading pairs from DexScreener",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("token") && (content.toLowerCase().includes("info") || content.toLowerCase().includes("details") || content.toLowerCase().includes("price"));
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const addressMatch = content.match(/0x[a-fA-F0-9]{40}/);
if (!addressMatch) {
callback({
text: 'Please provide a token address. Example: "Get token info for 0x..."',
action: "DEXSCREENER_TOKEN_INFO"
});
return;
}
const result = await service.getTokenPairs({
tokenAddress: addressMatch[0]
});
if (!result.success || !result.data) {
callback({
text: `Failed to get token info: ${result.error}`,
action: "DEXSCREENER_TOKEN_INFO"
});
return;
}
const pairs = result.data;
if (pairs.length === 0) {
callback({
text: `No pairs found for token ${addressMatch[0]}`,
action: "DEXSCREENER_TOKEN_INFO"
});
return;
}
const mainPair = pairs.reduce(
(prev, curr) => (curr.liquidity?.usd || 0) > (prev.liquidity?.usd || 0) ? curr : prev
);
const pairList = pairs.slice(0, 3).map(
(pair) => `\u2022 **${pair.baseToken.symbol}/${pair.quoteToken.symbol}** on ${pair.dexId} (${pair.chainId})
Price: ${service.formatPrice(pair.priceUsd || pair.priceNative)} | Liq: ${pair.liquidity?.usd ? service.formatUsdValue(pair.liquidity.usd) : "N/A"}`
).join("\n");
callback({
text: `**\u{1F4CA} Token Information**
**Token:** ${mainPair.baseToken.name} (${mainPair.baseToken.symbol})
**Address:** \`${mainPair.baseToken.address}\`
**Price:** ${service.formatPrice(mainPair.priceUsd || mainPair.priceNative)}
**24h Change:** ${service.formatPriceChange(mainPair.priceChange.h24)}
**24h Volume:** ${service.formatUsdValue(mainPair.volume.h24)}
**Market Cap:** ${mainPair.marketCap ? service.formatUsdValue(mainPair.marketCap) : "N/A"}
**FDV:** ${mainPair.fdv ? service.formatUsdValue(mainPair.fdv) : "N/A"}
**Top Trading Pairs:**
${pairList}`,
action: "DEXSCREENER_TOKEN_INFO",
data: pairs
});
return;
},
similes: ["token details", "token price", "get token", "check token"],
examples: [
[
{
name: "Alice",
content: {
text: "Get token info for 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
},
{
name: "Bob",
content: { text: "What is the price of token 0x..." }
}
]
]
};
var getTrendingAction = {
name: "DEXSCREENER_TRENDING",
description: "Get trending tokens from DexScreener based on volume, price changes, and trading activity",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("trending") || content.toLowerCase().includes("hot") || content.toLowerCase().includes("popular") || content.toLowerCase().includes("gainers");
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const timeframeMatch = content.match(/\b(1h|6h|24h)\b/);
const limitMatch = content.match(/top\s+(\d+)/i);
const result = await service.getTrending({
timeframe: timeframeMatch?.[1] || "24h",
limit: limitMatch ? parseInt(limitMatch[1]) : 10
});
if (!result.success || !result.data) {
callback({
text: `Failed to get trending tokens: ${result.error}`,
action: "DEXSCREENER_TRENDING"
});
return;
}
const pairs = result.data;
const trendingList = pairs.map((pair, i) => {
const priceChange = service.formatPriceChange(pair.priceChange.h24);
return `**${i + 1}. ${pair.baseToken.symbol}/${pair.quoteToken.symbol}**
\u{1F4B0} ${service.formatPrice(pair.priceUsd || pair.priceNative)} (${priceChange})
\u{1F4CA} Vol: ${service.formatUsdValue(pair.volume.h24)} | MCap: ${pair.marketCap ? service.formatUsdValue(pair.marketCap) : "N/A"}
\u{1F525} Buys: ${pair.txns.h24.buys} | Sells: ${pair.txns.h24.sells}`;
}).join("\n\n");
callback({
text: `**\u{1F525} Trending Tokens (${timeframeMatch?.[1] || "24h"})**
${trendingList}`,
action: "DEXSCREENER_TRENDING",
data: pairs
});
return;
},
similes: ["hot tokens", "popular coins", "top gainers", "what's trending"],
examples: [
[
{
name: "Alice",
content: { text: "Show me trending tokens on DexScreener" }
},
{
name: "Bob",
content: { text: "What are the top 5 hot tokens in the last 6h?" }
}
]
]
};
var getNewPairsAction = {
name: "DEXSCREENER_NEW_PAIRS",
description: "Get newly created trading pairs from DexScreener, showing recently launched tokens and their initial liquidity",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("new") && (content.toLowerCase().includes("pairs") || content.toLowerCase().includes("tokens") || content.toLowerCase().includes("listings"));
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const chainMatch = content.match(/on\s+(\w+)/i);
const limitMatch = content.match(/(\d+)\s+(?:new|latest)/i);
const result = await service.getNewPairs({
chain: chainMatch?.[1],
limit: limitMatch ? parseInt(limitMatch[1]) : 10
});
if (!result.success || !result.data) {
callback({
text: `Failed to get new pairs: ${result.error}`,
action: "DEXSCREENER_NEW_PAIRS"
});
return;
}
const pairs = result.data;
const newPairsList = pairs.map((pair, i) => {
const age = pair.pairCreatedAt ? `${Math.floor((Date.now() - pair.pairCreatedAt) / 6e4)} mins ago` : "Unknown";
return `**${i + 1}. ${pair.baseToken.symbol}/${pair.quoteToken.symbol}** ${pair.labels?.includes("new") ? "\u{1F195}" : ""}
\u23F0 Created: ${age} on ${pair.dexId} (${pair.chainId})
\u{1F4B0} Price: ${service.formatPrice(pair.priceUsd || pair.priceNative)}
\u{1F4A7} Liquidity: ${pair.liquidity?.usd ? service.formatUsdValue(pair.liquidity.usd) : "N/A"}`;
}).join("\n\n");
callback({
text: `**\u{1F195} New Trading Pairs${chainMatch ? ` on ${chainMatch[1]}` : ""}**
${newPairsList}`,
action: "DEXSCREENER_NEW_PAIRS",
data: pairs
});
return;
},
similes: ["new listings", "latest pairs", "new tokens", "fresh pairs"],
examples: [
[
{
name: "Alice",
content: { text: "Show me new pairs on DexScreener" }
},
{
name: "Bob",
content: { text: "What are the 5 new tokens on ethereum?" }
}
]
]
};
var getPairsByChainAction = {
name: "DEXSCREENER_CHAIN_PAIRS",
description: "Get top trading pairs from a specific blockchain sorted by volume, liquidity, price change, or transaction count",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const chains = [
"ethereum",
"bsc",
"polygon",
"arbitrum",
"optimism",
"base",
"solana",
"avalanche"
];
return chains.some((chain) => content.toLowerCase().includes(chain));
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const chains = [
"ethereum",
"bsc",
"polygon",
"arbitrum",
"optimism",
"base",
"solana",
"avalanche"
];
const chain = chains.find((c) => content.toLowerCase().includes(c));
if (!chain) {
callback({
text: "Please specify a blockchain. Supported: ethereum, bsc, polygon, arbitrum, optimism, base, solana, avalanche",
action: "DEXSCREENER_CHAIN_PAIRS"
});
return;
}
let sortBy = "volume";
if (content.toLowerCase().includes("liquid")) sortBy = "liquidity";
else if (content.toLowerCase().includes("gain") || content.toLowerCase().includes("change"))
sortBy = "priceChange";
else if (content.toLowerCase().includes("active") || content.toLowerCase().includes("trades"))
sortBy = "txns";
const result = await service.getPairsByChain({
chain,
sortBy,
limit: 10
});
if (!result.success || !result.data) {
callback({
text: `Failed to get ${chain} pairs: ${result.error}`,
action: "DEXSCREENER_CHAIN_PAIRS"
});
return;
}
const pairs = result.data;
const pairsList = pairs.slice(0, 5).map((pair, i) => {
const metric = sortBy === "volume" ? `Vol: ${service.formatUsdValue(pair.volume.h24)}` : sortBy === "liquidity" ? `Liq: ${pair.liquidity?.usd ? service.formatUsdValue(pair.liquidity.usd) : "N/A"}` : sortBy === "priceChange" ? `24h: ${service.formatPriceChange(pair.priceChange.h24)}` : `Trades: ${pair.txns.h24.buys + pair.txns.h24.sells}`;
return `**${i + 1}. ${pair.baseToken.symbol}/${pair.quoteToken.symbol}** on ${pair.dexId}
\u{1F4B0} ${service.formatPrice(pair.priceUsd || pair.priceNative)} | ${metric}`;
}).join("\n\n");
callback({
text: `**\u26D3\uFE0F Top ${chain.charAt(0).toUpperCase() + chain.slice(1)} Pairs by ${sortBy}**
${pairsList}`,
action: "DEXSCREENER_CHAIN_PAIRS",
data: pairs
});
return;
},
similes: ["tokens on", "pairs on", "top on"],
examples: [
[
{
name: "Alice",
content: { text: "Show me top tokens on ethereum" }
},
{
name: "Bob",
content: { text: "What are the most liquid pairs on polygon?" }
}
]
]
};
var getBoostedTokensAction = {
name: "DEXSCREENER_BOOSTED_TOKENS",
description: "Get boosted (promoted/sponsored) tokens from DexScreener, showing tokens with paid promotional boosts",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("boosted") || content.toLowerCase().includes("promoted") || content.toLowerCase().includes("sponsored");
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
const isTop = content.toLowerCase().includes("top");
const result = isTop ? await service.getTopBoostedTokens() : await service.getLatestBoostedTokens();
if (!result.success || !result.data) {
callback({
text: `Failed to get boosted tokens: ${result.error}`,
action: "DEXSCREENER_BOOSTED_TOKENS"
});
return;
}
const tokens = result.data.slice(0, 10);
if (tokens.length === 0) {
callback({
text: "No boosted tokens found",
action: "DEXSCREENER_BOOSTED_TOKENS"
});
return;
}
const tokenList = tokens.map((token, i) => {
return `**${i + 1}. ${token.tokenAddress}** on ${token.chainId}
\u{1F4B0} Boost Amount: ${token.amount} (Total: ${token.totalAmount})
\u{1F4DD} ${token.description || "No description"}
\u{1F517} ${token.url}`;
}).join("\n\n");
callback({
text: `**\u26A1 ${isTop ? "Top" : "Latest"} Boosted Tokens**
${tokenList}`,
action: "DEXSCREENER_BOOSTED_TOKENS",
data: tokens
});
return;
},
similes: ["promoted tokens", "sponsored tokens", "boosted coins"],
examples: [
[
{
name: "Alice",
content: { text: "Show me boosted tokens on DexScreener" }
},
{
name: "Bob",
content: { text: "What are the top promoted tokens?" }
}
]
]
};
var getTokenProfilesAction = {
name: "DEXSCREENER_TOKEN_PROFILES",
description: "Get latest token profiles from DexScreener including social links, descriptions, and project information",
validate: async (runtime, message) => {
const content = typeof message.content === "string" ? message.content : message.content?.text || "";
return content.toLowerCase().includes("profile") && content.toLowerCase().includes("token");
},
handler: async (runtime, message, _, __, callback) => {
if (!callback) {
console.error("No callback");
return;
}
const service = runtime.getService("dexscreener");
const result = await service.getLatestTokenProfiles();
if (!result.success || !result.data) {
callback({
text: `Failed to get token profiles: ${result.error}`,
action: "DEXSCREENER_TOKEN_PROFILES"
});
return;
}
const profiles = result.data.slice(0, 5);
if (profiles.length === 0) {
callback({
text: "No token profiles found",
action: "DEXSCREENER_TOKEN_PROFILES"
});
return;
}
const profileList = profiles.map((profile, i) => {
const links = profile.links?.map((l) => `[${l.label}](${l.url})`).join(" | ") || "No links";
return `**${i + 1}. ${profile.tokenAddress}** on ${profile.chainId}
\u{1F4DD} ${profile.description || "No description"}
\u{1F517} Links: ${links}
\u{1F310} ${profile.url}`;
}).join("\n\n");
callback({
text: `**\u{1F4CB} Latest Token Profiles**
${profileList}`,
action: "DEXSCREENER_TOKEN_PROFILES",
data: profiles
});
return;
},
similes: ["token profiles", "token details page"],
examples: [
[
{
name: "Alice",
content: { text: "Show me latest token profiles" }
}
]
]
};
var dexscreenerActions = [
searchTokensAction,
getTokenInfoAction,
getTrendingAction,
getNewPairsAction,
getPairsByChainAction,
getBoostedTokensAction,
getTokenProfilesAction
];
// src/index.ts
var dexscreenerPlugin = {
name: "dexscreener-analytics-plugin",
description: "Plugin for DexScreener DEX analytics and token information",
actions: dexscreenerActions,
evaluators: [],
providers: [],
services: [DexScreenerService],
init: async (_, runtime) => {
console.log("DexScreener plugin initialized");
}
};
var index_default = dexscreenerPlugin;
export {
DexScreenerService,
index_default as default,
dexscreenerActions,
dexscreenerPlugin,
getBoostedTokensAction,
getNewPairsAction,
getPairsByChainAction,
getTokenInfoAction,
getTokenProfilesAction,
getTrendingAction,
searchTokensAction
};
//# sourceMappingURL=index.js.map