Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugin Solana web3.js V2 & automated LPing on Orca #2136

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from

Conversation

calintje
Copy link

@calintje calintje commented Jan 10, 2025

Background

What does this PR do?

  1. Adds plugin-solana-v2, leveraging the latest features of @solana/web3.js v2
  • This plugin can be used alongside existing plugins that use @solana/web3.js v1.
  • Other protocols can use this plugin to integrate their functionality, if their tooling leverages @solana/web3.js v2
  • The pluting-tee uses Keypair from @solana/web3.js v1, while v2 uses CryptoKeyPair from the web api. To account for this, I added some utils that add TEE functionality for solana-plugin-v2 as well (tested locally).
  1. Adds autonomous LP repositioning capabilities on Orca
  • Automatically repositions liquidity positions if the center price of the position deviates from the current pool price by more than a user-specified threshold (repositionThresholdBps).
  • Maintains the original width of the position during repositioning.

What kind of change is this?

Features (non-breaking change which adds functionality)

Why are we doing this? Any context or related work?

Future proof Eliza for new projects that build on top of the new Solana SDK.

Documentation changes needed?

My changes do not require a change to the project documentation.
I added a README to the plugin.

Testing

I'm currently still testing the stability of the repositioning functionality. The behavior of the agent is at times unpredictable. I would like one of the core devs to give me guidance on the following:

  • explain in detail the inner workings of provider-evaluator loop,
  • explain what information each of the components needs,
  • how all three components interact with state and memory
  • how to control the timing of the loop to control the amount of API/RPC calls,

Where should a reviewer start?

TBA

Detailed testing steps

TBA

Discord username

@calintje

Summary by CodeRabbit

Release Notes for Solana Plugin V2

  • New Features

    • Introduced Solana Plugin V2 with advanced position management for decentralized finance (DeFi) strategies
    • Added support for automated liquidity position repositioning on Orca DEX
    • Implemented Trusted Execution Environment (TEE) key derivation and remote attestation
  • Improvements

    • Upgraded to @solana/web3.js v2 with modern JavaScript practices
    • Enhanced transaction handling with improved compute unit and priority fee management
    • Added flexible plugin configuration for position management
  • Dependencies

    • Updated Solana-related package references across multiple packages
    • Added new @elizaos/plugin-solana-v2 package

@calintje calintje changed the title feat: Add plugin for Solana web3.js V2 & automated LPing on Orca feat: plugin Solana web3.js V2 & automated LPing on Orca Jan 10, 2025
@shakkernerd shakkernerd changed the base branch from main to develop January 14, 2025 15:43
Copy link
Contributor

coderabbitai bot commented Jan 19, 2025

📝 Walkthrough

Walkthrough

The pull request introduces a new Solana Plugin V2 (@elizaos/plugin-solana-v2) for the Eliza AI agent framework. This plugin enhances Solana blockchain interactions by providing advanced position management, utilizing @solana/web3.js v2, and offering tools for decentralized finance strategies. The changes span multiple files, adding new dependencies, configurations, and utility functions to support sophisticated blockchain operations.

Changes

File Change Summary
agent/package.json Added @elizaos/plugin-solana-v2 dependency
agent/src/index.ts Imported and configured solanaPluginV2
package.json Added overrides for @solana/web3.js versions
packages/core/package.json Updated @solana/web3.js dependency reference
packages/plugin-nft-generation/package.json Updated @solana/web3.js dependency reference
packages/plugin-solana-v2/* Created new plugin with multiple source files

Possibly related PRs

Suggested Labels

plugin_new, solana, blockchain

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (9)
packages/plugin-solana-v2/src/providers/orca/positionProvider.ts (1)

50-50: Use consistent variable naming.

Variable FetchedPositionsStatistics should be in camelCase to match naming conventions.

Apply this diff for consistency:

-            const FetchedPositionsStatistics: FetchedPositionStatistics[] = await Promise.all(positions.map(async (position) => {
+            const fetchedPositionsStatistics: FetchedPositionStatistics[] = await Promise.all(positions.map(async (position) => {
packages/plugin-solana-v2/src/utils/TEE/deriveKeyProvider.ts (2)

24-26: Replace console.log with elizaLogger for consistent logging.

Use the provided logging utility for uniform log management.

Example changes:

-                    console.log("TEE: Connecting to local simulator at localhost:8090");
+                    elizaLogger.log("TEE: Connecting to local simulator at localhost:8090");

Apply similar changes to other console.log statements.

Also applies to: 30-32, 36-38, 59-62, 77-79, 92-93


107-134: Ensure consistent error handling and return values in get method.

Returning empty strings on error may lead to unclear failures. Consider throwing exceptions or returning null.

packages/plugin-solana-v2/src/index.ts (1)

14-21: Remove commented-out orcaPlugin code.

Cleaning up unused code improves readability.

Apply this diff to remove the commented code:

-// export const orcaPlugin: Plugin = {
-//     name: "orca",
-//     description: "Orca Plugin for Eliza",
-//     actions: [managePositions, repositionPositions],
-//     evaluators: [repositionEvaluator],
-//     providers: [positionProvider],
-// }
packages/plugin-solana-v2/src/evaluators/orca/repositionEvaluator.ts (1)

38-38: Fix typo in variable name.

The variable name contains a typo.

-        const instervalMs = config.intervalSeconds * 1000;
+        const intervalMs = config.intervalSeconds * 1000;
packages/plugin-solana-v2/tsup.config.ts (1)

3-26: Consider additional build optimizations.

The configuration could benefit from performance improvements.

 export default defineConfig({
     entry: ['src/index.ts'],
     format: ['esm'],
     dts: true,
     splitting: false,
     sourcemap: true,
     clean: true,
+    minify: true,
+    treeshake: true,
     external: [
package.json (1)

51-53: Well-structured version management strategy.

The overrides section effectively manages multiple web3.js versions:

  • v1.95.5 for NFT generation
  • v1.95.8 for core and plugin-solana
  • v2.0.0 for the new plugin-solana-v2

Consider documenting this version management strategy in the project's README to help future contributors understand the multi-version setup.

packages/plugin-solana-v2/README.md (2)

26-26: Fix typo: "isntance" → "instance"

-- Accepts the RPC isntance, transaction instructions, and a wallet.
++ Accepts the RPC instance, transaction instructions, and a wallet.

70-76: Enhance code example with imports

The configuration example should include import statements for better clarity.

+import { managePositions, repositionPosition } from './actions';
+import { repositionEvaluator } from './evaluators';
+import { positionProvider } from './providers';
+
 export const solanaPluginV2: Plugin = {
   name: "solanaV2",
   description: "Solana Plugin V2 for Eliza",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0404e29 and 03383f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • agent/package.json (1 hunks)
  • agent/src/index.ts (3 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/plugin-nft-generation/package.json (1 hunks)
  • packages/plugin-solana-v2/README.md (1 hunks)
  • packages/plugin-solana-v2/package.json (1 hunks)
  • packages/plugin-solana-v2/src/actions/orca/managePositions.ts (1 hunks)
  • packages/plugin-solana-v2/src/evaluators/orca/repositionEvaluator.ts (1 hunks)
  • packages/plugin-solana-v2/src/index.ts (1 hunks)
  • packages/plugin-solana-v2/src/providers/orca/positionProvider.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/TEE/deriveKeyProvider.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/TEE/remoteAttestationProvider.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/TEE/types.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/loadWallet.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/sendMessage.ts (1 hunks)
  • packages/plugin-solana-v2/src/utils/sendTransaction.ts (1 hunks)
  • packages/plugin-solana-v2/tsconfig.json (1 hunks)
  • packages/plugin-solana-v2/tsup.config.ts (1 hunks)
  • packages/plugin-solana/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/plugin-solana-v2/tsconfig.json
🧰 Additional context used
🪛 LanguageTool
packages/plugin-solana-v2/README.md

[uncategorized] ~47-~47: Loose punctuation mark.
Context: ...TEE functionality. - sendTransaction: Smart transaction handling with CU opti...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 Biome (1.9.4)
packages/plugin-solana-v2/src/actions/orca/managePositions.ts

[error] 219-219: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


[error] 246-246: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

🔇 Additional comments (11)
packages/plugin-solana-v2/src/utils/loadWallet.ts (1)

1-83: LGTM

The loadWallet function is well-implemented, correctly handling different TEE modes and runtime settings to return the appropriate keypair or public key.

agent/src/index.ts (4)

58-58: Importing solanaPluginV2.

The import statement correctly brings in the new Solana V2 plugin.


537-537: Conditional inclusion of solanaPluginV2.

The logic properly includes the plugin when Solana credentials are present.


609-609: Adding echoChamberPlugin based on secrets.

The plugin is conditionally added when the required secrets are available.


614-614: Using .flat() to handle nested plugin arrays.

Flattening the plugins array ensures correct plugin integration.

packages/plugin-solana-v2/src/utils/TEE/types.ts (2)

1-6: Enum TEEMode is well-defined.

It accurately represents the different TEE modes with clear comments.


8-11: Interface RemoteAttestationQuote is properly defined.

The structure includes necessary properties for attestation quotes.

packages/plugin-solana/package.json (1)

27-27: LGTM! Explicit npm alias helps prevent version conflicts.

The change to use npm:@solana/[email protected] ensures consistent version resolution.

packages/plugin-nft-generation/package.json (1)

30-30: LGTM! Consistent version management approach.

Using npm:@solana/[email protected] aligns with the project-wide version resolution strategy.

packages/core/package.json (1)

42-42: LGTM! Maintains version consistency.

The explicit npm alias for web3.js matches the project-wide approach.

agent/package.json (1)

51-51: Verify integration with existing Solana plugin.

The addition of @elizaos/plugin-solana-v2 alongside the existing v1 plugin requires careful consideration of plugin initialization order and potential conflicts.

✅ Verification successful

No plugin conflict risk detected

The codebase implements controlled loading of Solana plugins:

  • V2 plugin loads for standard Solana operations
  • V1 plugin only activates in TEE mode with specific configuration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential plugin conflicts
rg -A 5 "plugin-solana|plugin-solana-v2" | grep -i "initialize\|setup\|configure"

Length of output: 368


Script:

#!/bin/bash
# Search for plugin imports and usage
echo "=== Checking imports and plugin registration ==="
rg -l "plugin-solana-v2|@elizaos/plugin-solana-v2"

echo -e "\n=== Checking plugin initialization patterns ==="
ast-grep --pattern 'import $_ from "@elizaos/plugin-solana$$$"'
ast-grep --pattern 'import $_ from "@elizaos/plugin-solana-v2$$$"'

echo -e "\n=== Looking for plugin registration code ==="
rg -A 5 "registerPlugin|addPlugin|usePlugin" 

Length of output: 5800


Script:

#!/bin/bash
echo "=== Checking agent initialization code ==="
cat agent/src/index.ts

echo -e "\n=== Looking for plugin configuration patterns ==="
ast-grep --pattern 'new AgentRuntime($$$)'

Length of output: 37411

Comment on lines +37 to +44
const prioritizationFee = await rpc.getRecentPrioritizationFees()
.send()
.then(fees =>
fees
.map(fee => Number(fee.prioritizationFee))
.sort((a, b) => a - b)
[Math.ceil(0.95 * fees.length) - 1]
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle empty prioritization fees array

If getRecentPrioritizationFees() returns an empty array, the calculation of prioritizationFee will fail due to accessing an undefined index. Ensure that you handle the case when no fees are returned.

Apply this diff to handle empty fees:

 const fees = await rpc.getRecentPrioritizationFees()
   .send();
+ if (!fees || fees.length === 0) {
+   throw new Error('No prioritization fees available');
+ }
 const prioritizationFee = fees
   .map(fee => Number(fee.prioritizationFee))
   .sort((a, b) => a - b)
   [Math.ceil(0.95 * fees.length) - 1];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prioritizationFee = await rpc.getRecentPrioritizationFees()
.send()
.then(fees =>
fees
.map(fee => Number(fee.prioritizationFee))
.sort((a, b) => a - b)
[Math.ceil(0.95 * fees.length) - 1]
);
const fees = await rpc.getRecentPrioritizationFees()
.send();
if (!fees || fees.length === 0) {
throw new Error('No prioritization fees available');
}
const prioritizationFee = fees
.map(fee => Number(fee.prioritizationFee))
.sort((a, b) => a - b)
[Math.ceil(0.95 * fees.length) - 1];

Comment on lines +54 to +75
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
const signature = await rpc.sendTransaction(base64EncodedWireTransaction, {
maxRetries: 0n,
skipPreflight: true,
encoding: 'base64'
}).send();
const statuses = await rpc.getSignatureStatuses([signature]).send();
if (statuses.value[0]) {
if (!statuses.value[0].err) {
elizaLogger.log(`Transaction confirmed: ${signature}`);
return signature
} else {
throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
}
}
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise(resolve => setTimeout(resolve, remainingTime));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for transaction sending and confirmation

If rpc.sendTransaction() or rpc.getSignatureStatuses() throw an error, the exception isn't caught, which may cause the function to fail unexpectedly. Wrap these calls in a try-catch block to handle exceptions gracefully.

Apply this diff to add error handling:

 while (Date.now() - startTime < timeoutMs) {
   const transactionStartTime = Date.now();
+  try {
     const signature = await rpc.sendTransaction(base64EncodedWireTransaction, {
       maxRetries: 0n,
       skipPreflight: true,
       encoding: 'base64'
     }).send();
     const statuses = await rpc.getSignatureStatuses([signature]).send();
     if (statuses.value[0]) {
       if (!statuses.value[0].err) {
         elizaLogger.log(`Transaction confirmed: ${signature}`);
         return signature;
       } else {
         throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
       }
     }
+  } catch (error) {
+    elizaLogger.error(`Error sending transaction: ${error}`);
+    // Optionally handle retry logic or exit
+  }
   const elapsedTime = Date.now() - transactionStartTime;
   const remainingTime = Math.max(0, 1000 - elapsedTime);
   if (remainingTime > 0) {
     await new Promise(resolve => setTimeout(resolve, remainingTime));
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
const signature = await rpc.sendTransaction(base64EncodedWireTransaction, {
maxRetries: 0n,
skipPreflight: true,
encoding: 'base64'
}).send();
const statuses = await rpc.getSignatureStatuses([signature]).send();
if (statuses.value[0]) {
if (!statuses.value[0].err) {
elizaLogger.log(`Transaction confirmed: ${signature}`);
return signature
} else {
throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
}
}
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise(resolve => setTimeout(resolve, remainingTime));
}
}
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
try {
const signature = await rpc.sendTransaction(base64EncodedWireTransaction, {
maxRetries: 0n,
skipPreflight: true,
encoding: 'base64'
}).send();
const statuses = await rpc.getSignatureStatuses([signature]).send();
if (statuses.value[0]) {
if (!statuses.value[0].err) {
elizaLogger.log(`Transaction confirmed: ${signature}`);
return signature
} else {
throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
}
}
} catch (error) {
elizaLogger.error(`Error sending transaction: ${error}`);
// Optionally handle retry logic or exit
}
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise(resolve => setTimeout(resolve, remainingTime));
}
}

Comment on lines +8 to +35
constructor(teeMode?: string) {
let endpoint: string | undefined;

// Both LOCAL and DOCKER modes use the simulator, just with different endpoints
switch (teeMode) {
case TEEMode.LOCAL:
endpoint = "http://localhost:8090";
console.log(
"TEE: Connecting to local simulator at localhost:8090"
);
break;
case TEEMode.DOCKER:
endpoint = "http://host.docker.internal:8090";
console.log(
"TEE: Connecting to simulator via Docker at host.docker.internal:8090"
);
break;
case TEEMode.PRODUCTION:
endpoint = undefined;
console.log(
"TEE: Running in production mode without simulator"
);
break;
default:
throw new Error(
`Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION`
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle undefined TEE_MODE setting

If TEE_MODE is undefined, the constructor will throw an error. Since TEE_MODE might not be set, provide a default value or handle the undefined case to prevent unexpected errors.

Apply this diff to set a default TEE_MODE:

+ teeMode = teeMode || TEEMode.PRODUCTION;
 switch (teeMode) {
   case TEEMode.LOCAL:
     endpoint = "http://localhost:8090";
     console.log(
       "TEE: Connecting to local simulator at localhost:8090"
     );
     break;
   // existing cases...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
constructor(teeMode?: string) {
let endpoint: string | undefined;
// Both LOCAL and DOCKER modes use the simulator, just with different endpoints
switch (teeMode) {
case TEEMode.LOCAL:
endpoint = "http://localhost:8090";
console.log(
"TEE: Connecting to local simulator at localhost:8090"
);
break;
case TEEMode.DOCKER:
endpoint = "http://host.docker.internal:8090";
console.log(
"TEE: Connecting to simulator via Docker at host.docker.internal:8090"
);
break;
case TEEMode.PRODUCTION:
endpoint = undefined;
console.log(
"TEE: Running in production mode without simulator"
);
break;
default:
throw new Error(
`Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION`
);
}
constructor(teeMode?: string) {
let endpoint: string | undefined;
// Both LOCAL and DOCKER modes use the simulator, just with different endpoints
teeMode = teeMode || TEEMode.PRODUCTION;
switch (teeMode) {
case TEEMode.LOCAL:
endpoint = "http://localhost:8090";
console.log(
"TEE: Connecting to local simulator at localhost:8090"
);
break;
case TEEMode.DOCKER:
endpoint = "http://host.docker.internal:8090";
console.log(
"TEE: Connecting to simulator via Docker at host.docker.internal:8090"
);
break;
case TEEMode.PRODUCTION:
endpoint = undefined;
console.log(
"TEE: Running in production mode without simulator"
);
break;
default:
throw new Error(
`Invalid TEE_MODE: ${teeMode}. Must be one of: LOCAL, DOCKER, PRODUCTION`
);
}


return FetchedPositionsStatistics
} catch (error) {
throw new Error("Error during feching positions");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in error message.

There's a typo at line 92: "feching" should be "fetching".

Apply this diff to correct the typo:

-            throw new Error("Error during feching positions");
+            throw new Error("Error during fetching positions");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw new Error("Error during feching positions");
throw new Error("Error during fetching positions");

🛠️ Refactor suggestion

Include original error message when rethrowing exception.

Including the original error message aids in debugging.

Apply this diff to include the original error:

-            throw new Error("Error during fetching positions");
+            throw new Error(`Error during fetching positions: ${error.message}`);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +71 to +75
if (!path || !subject) {
console.error(
"Path and Subject are required for key derivation"
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Throw an error when required parameters are missing.

After logging the error, the function should throw an exception to prevent further execution.

Apply this diff:

             console.error(
                 "Path and Subject are required for key derivation"
             );
+            throw new Error("Missing path or subject for key derivation");
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!path || !subject) {
console.error(
"Path and Subject are required for key derivation"
);
}
if (!path || !subject) {
console.error(
"Path and Subject are required for key derivation"
);
throw new Error("Missing path or subject for key derivation");
}

Comment on lines +25 to +38
const BASE_URL = "http://localhost:3000";
const response = await fetch(`${BASE_URL}/${agentId}/message`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`Failed to send message: ${response.statusText}`);
}
const data = (await response.json()) as TextResponse[];
return data;
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling and add request timeout.

The current implementation lacks robust error handling and request timeout configuration.

+        const timeout = process.env.REQUEST_TIMEOUT_MS || 5000;
+        const controller = new AbortController();
+        const timeoutId = setTimeout(() => controller.abort(), timeout);
         const response = await fetch(`${BASE_URL}/${agentId}/message`, {
             method: "POST",
             headers: {
                 "Content-Type": "application/json",
             },
-            body: JSON.stringify(requestBody)
+            body: JSON.stringify(requestBody),
+            signal: controller.signal
         });
+        clearTimeout(timeoutId);
         if (!response.ok) {
-            throw new Error(`Failed to send message: ${response.statusText}`);
+            throw new Error(`Failed to send message: ${response.status} ${response.statusText}`);
         }
         const data = (await response.json()) as TextResponse[];
         return data;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const BASE_URL = "http://localhost:3000";
const response = await fetch(`${BASE_URL}/${agentId}/message`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`Failed to send message: ${response.statusText}`);
}
const data = (await response.json()) as TextResponse[];
return data;
} catch (error) {
const BASE_URL = "http://localhost:3000";
const timeout = process.env.REQUEST_TIMEOUT_MS || 5000;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(`${BASE_URL}/${agentId}/message`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`Failed to send message: ${response.status} ${response.statusText}`);
}
const data = (await response.json()) as TextResponse[];
return data;
} catch (error) {

Comment on lines +40 to +44
await new Promise((resolve) => setTimeout(resolve, instervalMs));
sendMessage({
agentId: runtime.agentId,
text: message.content.text, // Reuse the original message text
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement proper timeout cleanup.

The current implementation could lead to memory leaks if the runtime is terminated while the timeout is active.

-        await new Promise((resolve) => setTimeout(resolve, instervalMs));
+        const timeoutPromise = new Promise((resolve, reject) => {
+            const timeoutId = setTimeout(() => {
+                resolve(true);
+            }, intervalMs);
+            runtime.onShutdown(() => {
+                clearTimeout(timeoutId);
+                reject(new Error('Runtime shutdown'));
+            });
+        });
+        await timeoutPromise;
         sendMessage({
             agentId: runtime.agentId,
             text: message.content.text, // Reuse the original message text
         });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +1 to +30
{
"name": "@elizaos/plugin-solana-v2",
"version": "0.1.7-alpha.2",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"@elizaos/plugin-tee": "workspace:*",
"@orca-so/whirlpools": "^1.0.2",
"@orca-so/whirlpools-core": "^1.0.2",
"@orca-so/whirlpools-client": "1.0.2",
"@solana-program/compute-budget": "^0.6.1",
"@solana-program/system": "^0.6.2",
"@solana-program/token-2022": "^0.3.1",
"@solana/codecs": "^2.0.0",
"@solana/web3.js": "^2.0.0",
"@types/bs58": "^4.0.4",
"bs58": "^6.0.0"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch"
},
"devDependencies": {
"@types/node": "^22.8.7",
"tsup": "8.3.5",
"vitest": "2.1.4"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add missing package metadata and update dependencies.

The package.json is missing essential fields and contains outdated dependencies.

 {
     "name": "@elizaos/plugin-solana-v2",
     "version": "0.1.7-alpha.2",
     "main": "dist/index.js",
     "type": "module",
     "types": "dist/index.d.ts",
+    "license": "MIT",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/elizaOS/eliza.git"
+    },
+    "peerDependencies": {
+        "@solana/web3.js": "^2.0.0"
+    },
     "dependencies": {
     ...
     },
     "devDependencies": {
-        "@types/node": "^22.8.7",
+        "@types/node": "^20.11.0",
         "tsup": "8.3.5",
         "vitest": "2.1.4"
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +82 to +84
SOLANA_PRIVATE_KEY=
SOLANA_PUBLIC_KEY=
RPC_URL=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add environment variable specifications

Please enhance the environment variables section with:

  • Required format for SOLANA_PRIVATE_KEY (base58, hex, etc.)
  • Supported Solana networks (mainnet, testnet, devnet)
  • Example RPC_URL format

Comment on lines +31 to +33
#### Trusted Execution Environment (TEE)
- For Trusted Execution Environment (TEE) functionality, this plugin transitions from `Keypair` (used in v1) to `CryptoKeyPair` from the Web Crypto API. This change aligns with `@solana/web3.js` v2's zero-dependency, modern JavaScript architecture.
- A modified implementation for TEE integration is included in `src/utils/`, following the same patterns used in `plugin-tee`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add security warnings for private key handling

The TEE section should include explicit security warnings about:

  • Safe private key management practices
  • Risks of exposing private keys in environment variables
  • Recommendations for production deployments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants