diff --git a/.github/workflows/common_checks.yaml b/.github/workflows/common_checks.yaml new file mode 100644 index 0000000..93afbbd --- /dev/null +++ b/.github/workflows/common_checks.yaml @@ -0,0 +1,22 @@ +name: main_workflow + +on: + push: + branches: + - development + - main + pull_request: + +jobs: + scan: + name: gitleaks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - run: | + wget https://github.com/zricethezav/gitleaks/releases/download/v8.10.1/gitleaks_8.10.1_linux_x64.tar.gz && \ + tar -xzf gitleaks_8.10.1_linux_x64.tar.gz && \ + sudo install gitleaks /usr/bin && \ + gitleaks detect --report-format json --report-path leak_report -v diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..66bdf79 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,26 @@ +# Title for the gitleaks configuration file. +title = "Custom Gitleaks configuration" + +# You have basically two options for your custom configuration: +# +# 1. define your own configuration, default rules do not apply +# +# use e.g., the default configuration as starting point: +# https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml +# +# 2. extend a configuration, the rules are overwritten or extended +# +# When you extend a configuration the extended rules take precedence over the +# default rules. I.e., if there are duplicate rules in both the extended +# configuration and the default configuration the extended rules or +# attributes of them will override the default rules. +# Another thing to know with extending configurations is you can chain +# together multiple configuration files to a depth of 2. Allowlist arrays are +# appended and can contain duplicates. + +# useDefault and path can NOT be used at the same time. Choose one. +[extend] +# useDefault will extend the default gitleaks config built in to the binary +# the latest version is located at: +# https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml +useDefault = true diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 0000000..acfb405 --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,3 @@ +378cbc8c71476cd4bbe81c09f780efa7ae4f9b6e:src/actions/tokenDecisionAction.ts:generic-api-key:174 +85ed0f3a738dcc9f1d00e2f8ea107dcdab876a8d:src/actions/balanceOfAction.ts:generic-api-key:59 +dc06a6f907376b7b912b76f74e05de6e63f11385:src/actions/balanceOfAction.ts:generic-api-key:59 diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..c8a0a96 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,5 @@ +# Release History - plugin-memeooorr + +## v0.1.7 (2025-02-20) + +* Initial Release diff --git a/README.md b/README.md index 19ac79c..386084d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # plugin-memeooorr -Eliza plugin for memeoor Agent developed on autonolas framework. +Eliza plugin for memeooor Agent developed on autonolas framework. + +> :warning: **Warning**
+> The code within this repository is provided without any warranties. It is important to note that the code has not been audited for potential security vulnerabilities. +> Using this code could potentially lead to loss of funds, compromised data, or asset risk. +> Exercise caution and use this code at your own risk. Please refer to the [LICENSE](./LICENSE) file for details about the terms and conditions. +> The plugin for now is specifically designed to be used with [agents-fun-eliza](https://github.com/valory-xyz/agents-fun-eliza). + # Eliza Twitter Plugin ## Overview @@ -24,7 +31,7 @@ This plugin provides: - Incorporates Twitter engagement metrics for holistic decision-making. ### Services -The **TwitterService** integrates with the ElizaOS `client-twitter` module to: +The **TwitterService** integrates with the ElizaOS `agent-twitter-client` module to: - Post tweets. - Reply to tweets with conversational threads. @@ -72,10 +79,10 @@ const result = await runtime.invokeAction('decideTokenAction', { #### Reply to a Tweet -Use the `TwitterService` to reply to a specific tweet: +Use the `TwitterScraper` to reply to a specific tweet: ```typescript -await twitterService.buildConversationThread(tweet, maxReplies); +await twitterService.replyToTweet(id, message); ``` @@ -87,13 +94,6 @@ await twitterService.buildConversationThread(tweet, maxReplies); - **constants/**: Stores shared constants and configurations. - **types/**: Defines TypeScript interfaces and types. -### Running Tests -Use the following command to run the test suite: - -```bash -pnpm test -``` - ## Contributing Contributions are welcome! Please follow these steps: @@ -116,4 +116,4 @@ Contributions are welcome! Please follow these steps: ### Contact -For support or inquiries, reach out to (mailto:keshav.mishra0298@gmail.com). +For support or inquiries, reach out to (mailto:info@valory.xyz). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..af865ac --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ +# Security Policy + +This document outlines security procedures and general policies for the `plugin-memeooorr` project. + +## Supported Versions + +The following table shows which versions of `plugin-memeooorr` are currently being supported with security updates. + +| Version | Supported | +|---------|--------------------| +| `n/a` | :white_check_mark: | +| `n/a` | :x: | + +## Reporting a Vulnerability + +The `plugin-memeooorr` team and community take all security bugs in `plugin-memeooorr` seriously. Thank you for improving the security of `plugin-memeooorr`. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. + +Report security bugs by emailing `info@valory.xyz`. + +The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating the next steps in handling your report. After the initial reply to your report, the security team will endeavour to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. + +Report security bugs in third-party modules to the person or team maintaining the module. + +## Disclosure Policy + +When the security team receives a security bug report, they will assign it to a primary handler. This person will coordinate the fix and release process, involving the following steps: + +- Confirm the problem and determine the affected versions. +- Audit code to find any potential similar problems. +- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible to PyPI. + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a pull request. diff --git a/package.json b/package.json index 6d1e493..ee58469 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "@elizaos/core": "0.1.7", "safe-client": "workspace:*", "agent-twitter-client": "^0.0.18", - "@safe-global/api-kit": "2.5.9", "@safe-global/protocol-kit": "5.2.2", "@safe-global/types-kit": "1.0.2", "@safe-global/sdk-starter-kit": "1.1.5", diff --git a/src/actions/decideTwitterInteraction.ts b/src/actions/decideTwitterInteraction.ts index a8b5c51..15d27d9 100644 --- a/src/actions/decideTwitterInteraction.ts +++ b/src/actions/decideTwitterInteraction.ts @@ -48,7 +48,9 @@ export const decideTwitterInteractionAction = ( currentState = await runtime.updateRecentMessageState(state); } - elizaLogger.log("Fetching Twitter metadata"); + elizaLogger.assert( + "Fetching Twitter metadata to decide on interaction", + ); const metadata: TwitterInteractionResponse | false = await tweetProvider.get(runtime, message); diff --git a/src/actions/tokenDecisionAction.ts b/src/actions/tokenDecisionAction.ts index 00ff7fa..78f898e 100644 --- a/src/actions/tokenDecisionAction.ts +++ b/src/actions/tokenDecisionAction.ts @@ -42,11 +42,6 @@ export const decideTokenAction = ( _params, callback, ) => { - // if (message.content.action !== "TOKEN_ACTION") { - // elizaLogger.error("No runtime provided"); - // return false; - // } - try { if (!state) { state = await runtime.composeState(message); diff --git a/src/config.ts b/src/config.ts index 65136b2..e995c54 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,59 +1,8 @@ -export const PROVIDER_CONFIG = { - BIRDEYE_API: "https://public-api.birdeye.so", - TOKEN_SECURITY_ENDPOINT: "/defi/token_security?address=", - TOKEN_METADATA_ENDPOINT: "/defi/v3/token/meta-data/single?address=", - MARKET_SEARCH_ENDPOINT: "/defi/v3/token/trade-data/single?address=", - TOKEN_PRICE_CHANGE_ENDPOINT: - "/defi/v3/search?chain=solana&target=token&sort_by=price_change_24h_percent&sort_type=desc&verify_token=true&markets=Raydium&limit=20", - TOKEN_VOLUME_24_CHANGE_ENDPOINT: - "/defi/v3/search?chain=solana&target=token&sort_by=volume_24h_change_percent&sort_type=desc&verify_token=true&markets=Raydium&limit=20", - TOKEN_BUY_24_CHANGE_ENDPOINT: - "/defi/v3/search?chain=solana&target=token&sort_by=buy_24h_change_percent&sort_type=desc&verify_token=true&markets=Raydium&offset=0&limit=20", - - TOKEN_SECURITY_ENDPOINT_BASE: "/defi/token_security?address=", - TOKEN_METADATA_ENDPOINT_BASE: "/defi/v3/token/meta-data/single?address=", - MARKET_SEARCH_ENDPOINT_BASE: "/defi/v3/token/trade-data/single?address=", - TOKEN_PRICE_CHANGE_ENDPOINT_BASE: - "/defi/v3/search?chain=base&target=token&sort_by=price_change_24h_percent&sort_type=desc&offset=0&limit=20", - TOKEN_VOLUME_24_ENDPOINT_BASE: - "/defi/v3/search?chain=base&target=token&sort_by=volume_24h_usd&sort_type=desc&offset=2&limit=20", - TOKEN_BUY_24_ENDPOINT_BASE: - "/defi/v3/search?chain=base&target=token&sort_by=buy_24h&sort_type=desc&offset=2&limit=20", - - MAX_RETRIES: 3, - RETRY_DELAY: 2000, -}; - // Add configuration for enabled chains export const CHAIN_CONFIG = { - SOLANA_ENABLED: false, // Can be controlled via settings BASE_ENABLED: true, // Can be controlled via settings }; -// Add Base chain configuration near other export constants -export const BASE_CONFIG = { - RPC_URL: process.env.EVM_PROVIDER_URL || "https://mainnet.base.org", - ROUTER_ADDRESS: "0x327Df1E6de05895d2ab08513aaDD9313Fe505d86", // Base Uniswap V2 Router - WETH_ADDRESS: "0x4200000000000000000000000000000000000006", // Base WETH - CHAIN_ID: 8453, - // Add Aerodrome-specific addresses - AERODROME: { - WETH: "0x4200000000000000000000000000000000000006", - USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - }, -}; - -export const CELO_CONFIG = { - RPC_URL: process.env.CELO_RPC_URL || "https://forno.celo.org", - CHAIN_ID: 42220, - ROUTER_ADDRESS: "0x1f98431c8ad98523631ae4a59f267346ea31f984", - WETH_ADDRESS: "0x471EcE3750Da237f93B8E339c536989b8978a438", - AERODROME: { - WETH: "0x471EcE3750Da237f93B8E339c536989b8978a438", - USDC: "0xceba9300f2b948710d2653dd7b07f33a8b32118c", - }, -}; - export const ACTIONS = { START: "START", TWITTER_INTERACTION: "TWITTER_INTERACTION", @@ -64,9 +13,6 @@ export const ACTIONS = { export default { ACTIONS, - BASE_CONFIG, - CELO_CONFIG, - PROVIDER_CONFIG, CHAIN_CONFIG, }; diff --git a/src/constants.ts b/src/constants.ts index 3d6b92b..daf44f0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,29 +1,8 @@ -export const SAFETY_LIMITS = { - MINIMUM_TRADE: 0.01, // Minimum 0.01 SOL per trade - MAX_POSITION_SIZE: 0.1, // Maximum 10% of token liquidity - MAX_SLIPPAGE: 0.05, // Maximum 5% slippage allowed - MIN_LIQUIDITY: 1000, // Minimum $1000 liquidity required - MIN_VOLUME: 2000, // Minimum $2000 24h volume required - MIN_TRUST_SCORE: 0.4, // Minimum trust score to trade - STOP_LOSS: 0.2, // 20% stop loss trigger - CHECK_INTERVAL: 5 * 60 * 1000, // Check every 5 minutes - TAKE_PROFIT: 0.12, // Take profit at 12% gain - TRAILING_STOP: 0.2, // 20% trailing stop from highest - PARTIAL_TAKE: 0.06, // Take 50% profit at 6% gain - REENTRY_DELAY: 60 * 60 * 1000, // Wait 1 hour before re-entering - MAX_ACTIVE_POSITIONS: 5, // Maximum concurrent positions - MIN_WALLET_BALANCE: 0.05, // Keep minimum 0.05 SOL in wallet -}; - -export const ANALYSIS_HISTORY_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds - export const MAX_TWEETS_PER_HOUR = { trade: 10, market_search: 5, }; -export const MARKET_SEARCH_INTERVAL = 60 * 60 * 1000; // 1 hour in milliseconds - export const TOKENS_QUERY = ` query Tokens { memeTokens { diff --git a/src/index.ts b/src/index.ts index 0c70f51..8749cfe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,49 +10,9 @@ export * as actions from "./actions/index"; export * as providers from "./providers/index"; export * as types from "./types/index"; -// // Consider exposing these settings as environment variables to allow users to provide custom configuration values. -// const config = { -// caching: { -// enabled: true, -// ttl: 3600000, // 1 hour -// maxSize: 1000, -// }, -// security: { -// rateLimit: { -// enabled: true, -// maxRequests: 100, -// windowMs: 60000, -// }, -// }, -// maxConcurrent: 5, // Maximum concurrent requests -// maxRetries: 3, // Maximum retry attempts -// batchSize: 20, // Batch size for collection requests -// }; - -// function createMemeoorPlugin(): Plugin { -// // Initialize reusable CacheManager if caching is enabled - -// return { -// name: "memeooorr", -// description: "Provides NFT collection information and market intelligence", -// providers: [ -// tweetProvider, -// tokenProvider, -// tokenProvider, -// safeWalletProvider, -// ], -// actions: [ -// decideTwitterInteractionAction(tweetProvider, twitterProvider), -// decideTokenAction(tokenProvider, safeWalletProvider), -// ], -// evaluators: [], -// }; -// } -// export const memeoorPlugin: Plugin = { name: "memeooorr", description: "Provides NFT collection information and market intelligence", - // providers: [tweetProvider, twitterProvider, tokenProvider, safeWalletProvider], providers: [], actions: [ decideTwitterInteractionAction(tweetProvider, twitterProvider), diff --git a/src/providers/safeaccount.ts b/src/providers/safeaccount.ts index 96cf325..f05299f 100644 --- a/src/providers/safeaccount.ts +++ b/src/providers/safeaccount.ts @@ -80,14 +80,16 @@ const MAX_HEART_VALUE = 20000000000000n; let protocolKitInstance: Safe | null = null; +/** + * + * @param protocolKit + */ export const getProtocolKit = async (protocolKit: Safe) => { if (!protocolKitInstance) { protocolKitInstance = protocolKit; } }; -// export const protocolKit = Safe; - /** * A Safe-based transaction provider. * @@ -399,13 +401,6 @@ export const safeAccountProvider: Provider = { let summoned_token_nonce = undefined; - // if (decision.action === "summon") { - // summoned_token_nonce = await getTokenNonce( - // receipt.transactionHash, - // rpcUrl, - // ); - // } - const actionSuccessMemory: Memory = { id: stringToUuid(Date.now().toString()), content: { diff --git a/src/providers/tokenProvider.ts b/src/providers/tokenProvider.ts index 4d04df0..a8113e3 100644 --- a/src/providers/tokenProvider.ts +++ b/src/providers/tokenProvider.ts @@ -37,14 +37,14 @@ const tokenProvider: Provider = { const scraper = new TwitterScraper(ts); - elizaLogger.log("Fetching latest tweets for user"); + elizaLogger.assert("Fetching latest tweets for user"); const tweet = await scraper.getUserLatestTweet(username); if (!tweet) { elizaLogger.error("Failed to fetch latest tweet"); return false; } - elizaLogger.log("Fetch current memecoins"); + elizaLogger.assert("Fetch current memecoins"); const memeCoins: MemeCoin[] = await scraper.getTokens( subUrl, rpcUrl, @@ -56,7 +56,7 @@ const tokenProvider: Provider = { return false; } - elizaLogger.log("Fetch replies to tweets"); + elizaLogger.assert("Fetch replies to tweets"); const replies = await scraper.getTweetReplies(username, tweet); // fetch available balance @@ -72,7 +72,7 @@ const tokenProvider: Provider = { replies: replies, balance: balance.toString(), }; - elizaLogger.debug("Tweet retrieved successfully"); + elizaLogger.success("Token interaction payload retrieved successfully"); return result; } catch (error) { elizaLogger.error("Failed to fetch tweet:", error); diff --git a/src/providers/twitterProvider.ts b/src/providers/twitterProvider.ts index 0ead7b2..2e2a137 100644 --- a/src/providers/twitterProvider.ts +++ b/src/providers/twitterProvider.ts @@ -35,28 +35,30 @@ const tweetProvider: Provider = { const scraper = new TwitterScraper(ts); - elizaLogger.log("Fetching latest tweets for user"); + elizaLogger.assert("Fetching latest tweets for user"); const tweet = await scraper.getUserLatestTweet(username); if (!tweet) { elizaLogger.error("Failed to fetch latest tweet"); return false; } - elizaLogger.log("Latest Fetched Tweet From User:", tweet); + elizaLogger.success("Latest Fetched Tweet From User:", tweet); - elizaLogger.log("Fetch previous Tweets from user"); + elizaLogger.assert("Fetch previous Tweets from user"); const previousTweets = await scraper.fetchPreviousTweets(username, tweet); if (!previousTweets) { elizaLogger.error("Failed to fetch previous tweets"); return false; } - elizaLogger.log("Previous Tweets fetched successfully!", previousTweets); + elizaLogger.success( + "Previous Tweets fetched successfully!", + previousTweets, + ); - elizaLogger.log("Fetch other tweets from user"); + elizaLogger.assert("Fetch other tweets from user, will take time"); let otherTweets: string = ""; let handles: string[] = []; try { const subUrl = runtime.getSetting("SUBGRAPH_URL") as string; - elizaLogger.log("Subgraph URL:", subUrl); try { handles = (await scraper.getUsersFromSubgraph( subUrl, @@ -67,10 +69,6 @@ const tweetProvider: Provider = { return false; } - for (const handle of handles) { - elizaLogger.log("Handle:", handle); - } - // if handles not empty, get other tweets otherTweets = await scraper.getOtherUserTweets(runtime, handles); if (!otherTweets) { @@ -94,7 +92,7 @@ const tweetProvider: Provider = { prevTweets: previousTweets, otherTweets: otherTweets, }; - elizaLogger.debug("Tweet retrieved successfully"); + elizaLogger.success("Twitter Interaction payload retrieved successfully"); return result; } catch (error) { elizaLogger.error("Failed to fetch tweet:", error); @@ -122,22 +120,22 @@ const twitterProvider: Provider = { const scraper = new TwitterScraper(ts); if (message.content.action === "tweet") { - elizaLogger.log("posting a new tweet"); + elizaLogger.assert("posting a new tweet"); await scraper.sendUserTweet(message.content.text); elizaLogger.success("Tweet posted successfully"); return true; } else if (message.content.action === "like") { - elizaLogger.log("Liking a tweet"); + elizaLogger.assert("Liking a tweet"); await scraper.likeTweet(message.content.source as string); elizaLogger.success("Tweet liked successfully"); return true; } else if (message.content.action === "retweet") { - elizaLogger.log("Retweeting a tweet"); + elizaLogger.assert("Retweeting a tweet"); await scraper.retweet(message.content.source as string); elizaLogger.success("Tweet retweeted successfully"); return true; } else if (message.content.action === "reply") { - elizaLogger.log("Replying to a tweet"); + elizaLogger.assert("Replying to a tweet"); await scraper.replyToTweet( message.content.source as string, message.content.text, @@ -145,7 +143,7 @@ const twitterProvider: Provider = { elizaLogger.success("Tweet replied successfully"); return true; } else if (message.content.action === "quote") { - elizaLogger.log("Quoting a tweet"); + elizaLogger.assert("Quoting a tweet"); await scraper.quoteTweet( message.content.source as string, message.content.text, diff --git a/src/utils/twitterScrapper.ts b/src/utils/twitterScrapper.ts index d4447ce..e2129b0 100644 --- a/src/utils/twitterScrapper.ts +++ b/src/utils/twitterScrapper.ts @@ -477,20 +477,20 @@ export async function getScrapper( elizaLogger.info("Attempting Twitter login using cookies"); await ts.setCookies(cookieStrings); if (await ts.isLoggedIn()) { - elizaLogger.info("Twitter login successful using cookies"); + elizaLogger.success("Twitter login successful using cookies"); return ts; } } else { throw new Error("No cookies found"); } - elizaLogger.info("Twitter login successful using cookies"); + elizaLogger.warn("Attempting Twitter login without cookies"); } catch (error) { elizaLogger.warn("Failed to set cookies, Performing Normal Login:", error); await ts.login(username, password, email); const cookies = await ts.getCookies(); fs.writeFileSync(__cookiesFilePath, JSON.stringify(cookies, null, 2)); if (await ts.isLoggedIn()) { - elizaLogger.warn("Twitter login successful but without cookies"); + elizaLogger.assert("Twitter login successful but without cookies"); return ts; } }