From 2f4f8b5b8a97ba8489a00d45871189322349dd2e Mon Sep 17 00:00:00 2001 From: 0xbbjoker <0xbbjoker@proton.me> Date: Tue, 15 Jul 2025 19:45:13 +0200 Subject: [PATCH] add defi plugins --- docs.json | 25 + plugins/defi/evm.mdx | 148 ++++ plugins/defi/evm/complete-documentation.mdx | 444 +++++++++++ plugins/defi/evm/defi-operations-flow.mdx | 108 +++ plugins/defi/evm/examples.mdx | 147 ++++ plugins/defi/evm/testing-guide.mdx | 123 +++ plugins/defi/solana.mdx | 167 ++++ .../defi/solana/complete-documentation.mdx | 746 ++++++++++++++++++ plugins/defi/solana/defi-operations-flow.mdx | 116 +++ plugins/defi/solana/examples.mdx | 143 ++++ plugins/defi/solana/testing-guide.mdx | 120 +++ plugins/overview.mdx | 56 +- 12 files changed, 2342 insertions(+), 1 deletion(-) create mode 100644 plugins/defi/evm.mdx create mode 100644 plugins/defi/evm/complete-documentation.mdx create mode 100644 plugins/defi/evm/defi-operations-flow.mdx create mode 100644 plugins/defi/evm/examples.mdx create mode 100644 plugins/defi/evm/testing-guide.mdx create mode 100644 plugins/defi/solana.mdx create mode 100644 plugins/defi/solana/complete-documentation.mdx create mode 100644 plugins/defi/solana/defi-operations-flow.mdx create mode 100644 plugins/defi/solana/examples.mdx create mode 100644 plugins/defi/solana/testing-guide.mdx diff --git a/docs.json b/docs.json index bcc34ab..64f7030 100644 --- a/docs.json +++ b/docs.json @@ -296,6 +296,31 @@ ] } ] + }, + { + "group": "DeFi Plugins", + "pages": [ + { + "group": "EVM", + "pages": [ + "plugins/defi/evm", + "plugins/defi/evm/complete-documentation", + "plugins/defi/evm/defi-operations-flow", + "plugins/defi/evm/examples", + "plugins/defi/evm/testing-guide" + ] + }, + { + "group": "Solana", + "pages": [ + "plugins/defi/solana", + "plugins/defi/solana/complete-documentation", + "plugins/defi/solana/defi-operations-flow", + "plugins/defi/solana/examples", + "plugins/defi/solana/testing-guide" + ] + } + ] } ] } diff --git a/plugins/defi/evm.mdx b/plugins/defi/evm.mdx new file mode 100644 index 0000000..21f3bfe --- /dev/null +++ b/plugins/defi/evm.mdx @@ -0,0 +1,148 @@ +--- +title: "Overview" +description: "Integrate EVM blockchain capabilities into your AI agent" +--- + +The EVM plugin enables AI agents to interact with Ethereum Virtual Machine (EVM) compatible blockchains, supporting token transfers, swaps, bridging, and governance operations across 30+ networks. + +## Features + +- **Multi-chain Support**: Works with Ethereum, Base, Arbitrum, Optimism, Polygon, BSC, Avalanche, and many more +- **Token Operations**: Transfer native tokens and ERC20 tokens +- **DeFi Integration**: Swap tokens and bridge across chains using LiFi and Bebop +- **Governance**: Create proposals, vote, queue, and execute governance actions +- **Wallet Management**: Multi-chain balance tracking with automatic updates +- **TEE Support**: Secure wallet derivation in Trusted Execution Environments + +## Installation + +```bash +bun install @elizaos/plugin-evm +``` + +## Configuration + +The plugin requires the following environment variables: + +```env +# Required +EVM_PRIVATE_KEY=your_private_key_here + +# Optional - Custom RPC endpoints +ETHEREUM_PROVIDER_ETHEREUM=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY +ETHEREUM_PROVIDER_BASE=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY + +# Optional - TEE Configuration +TEE_MODE=true +WALLET_SECRET_SALT=your_secret_salt +``` + +## Usage + +```typescript +import { evmPlugin } from '@elizaos/plugin-evm'; +import { AgentRuntime } from '@elizaos/core'; + +// Initialize the agent with EVM plugin +const runtime = new AgentRuntime({ + plugins: [evmPlugin], + // ... other configuration +}); +``` + +## Actions + +### Transfer Tokens +Transfer native tokens or ERC20 tokens between addresses. + +Example prompts: +- "Send 0.1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e" +- "Transfer 100 USDC to vitalik.eth on Base" +- "Send 50 DAI to 0x123... on Polygon" + +### Swap Tokens +Exchange tokens on the same chain using optimal routes. + +Example prompts: +- "Swap 1 ETH for USDC" +- "Exchange 100 USDT for DAI on Arbitrum" +- "Trade my WETH for USDC on Base" + +### Bridge Tokens +Transfer tokens across different chains. + +Example prompts: +- "Bridge 100 USDC from Ethereum to Arbitrum" +- "Move 0.5 ETH from Base to Optimism" +- "Transfer DAI from Polygon to Ethereum" + +### Governance Actions +Participate in DAO governance using OpenZeppelin Governor contracts. + +Example prompts: +- "Create a proposal to increase the treasury allocation" +- "Vote FOR on proposal #42" +- "Queue proposal #37 for execution" +- "Execute the queued proposal #35" + +## Providers + +The plugin includes providers that give your agent awareness of: +- **Wallet balances** across all configured chains +- **Token metadata** and current prices +- **Transaction history** and status + +## Supported Chains + +The plugin supports all chains available in viem, including: +- Ethereum Mainnet +- Layer 2s: Arbitrum, Optimism, Base, zkSync +- Alternative L1s: Polygon, BSC, Avalanche +- And many more... + +## Advanced Features + +### Custom Chain Configuration +Add custom RPC endpoints for any supported chain: + +```env +ETHEREUM_PROVIDER_OPTIMISM=https://opt-mainnet.g.alchemy.com/v2/YOUR_KEY +ETHEREUM_PROVIDER_ARBITRUM=https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY +``` + +### TEE Wallet Derivation +For enhanced security, enable TEE mode to derive wallets in Trusted Execution Environments: + +```env +TEE_MODE=true +WALLET_SECRET_SALT=your_unique_salt +``` + +### Multi-Aggregator Swaps +The plugin automatically finds the best swap routes using multiple aggregators: +- Primary: LiFi SDK +- Secondary: Bebop + +## Error Handling + +The plugin includes comprehensive error handling for common scenarios: +- Insufficient balance +- Network congestion +- Failed transactions +- Invalid addresses +- Slippage protection + +## Security Considerations + +- Never hardcode private keys in your code +- Use environment variables for sensitive data +- Validate all user inputs +- Set appropriate slippage tolerances +- Monitor gas prices and limits + +## Next Steps + +- [Complete Documentation →](./evm/complete-documentation) +- [DeFi Operations Flow →](./evm/defi-operations-flow) +- [Examples →](./evm/examples) +- [Testing Guide →](./evm/testing-guide) \ No newline at end of file diff --git a/plugins/defi/evm/complete-documentation.mdx b/plugins/defi/evm/complete-documentation.mdx new file mode 100644 index 0000000..5f71d6d --- /dev/null +++ b/plugins/defi/evm/complete-documentation.mdx @@ -0,0 +1,444 @@ +--- +title: "Developer Guide" +description: "Comprehensive guide to the EVM plugin architecture, implementation, and usage" +--- + +This guide provides an in-depth look at the EVM plugin's architecture, components, and implementation details. + +## Architecture Overview + +The EVM plugin follows a modular architecture with clear separation of concerns: + +``` +┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Actions │────▶│ Service │────▶│ Blockchain │ +│ (User Intent) │ │ (EVMService)│ │ (Viem) │ +└─────────────────┘ └──────────────┘ └──────────────┘ + │ │ + ▼ ▼ +┌─────────────────┐ ┌──────────────┐ +│ Templates │ │ Providers │ +│ (AI Prompts) │ │ (Data Supply)│ +└─────────────────┘ └──────────────┘ +``` + +## Core Components + +### EVMService + +The central service that manages blockchain connections and wallet data: + +```typescript +export class EVMService extends Service { + static serviceType = 'evm-service'; + + private walletProvider: WalletProvider; + private intervalId: NodeJS.Timeout | null = null; + + async initialize(runtime: IAgentRuntime): Promise { + // Initialize wallet provider with chain configuration + this.walletProvider = await initWalletProvider(runtime); + + // Set up periodic balance refresh + this.intervalId = setInterval( + () => this.refreshWalletData(), + 60000 // 1 minute + ); + } + + async refreshWalletData(): Promise { + await this.walletProvider.getChainConfigs(); + // Update cached balance data + } +} +``` + +### Actions + +#### Transfer Action + +Handles native and ERC20 token transfers: + +```typescript +export const transferAction: Action = { + name: 'EVM_TRANSFER', + description: 'Transfer tokens on EVM chains', + + validate: async (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting('EVM_PRIVATE_KEY'); + return !!privateKey || runtime.getSetting('WALLET_PUBLIC_KEY'); + }, + + handler: async (runtime, message, state, options, callback) => { + // 1. Extract parameters using AI + const params = await extractTransferParams(runtime, message, state); + + // 2. Validate inputs + if (!isAddress(params.toAddress)) { + throw new Error('Invalid recipient address'); + } + + // 3. Execute transfer + const result = await executeTransfer(params); + + // 4. Return response + callback?.({ + text: `Transferred ${params.amount} ${params.token} to ${params.toAddress}`, + content: { hash: result.hash } + }); + } +}; +``` + +#### Swap Action + +Integrates with multiple DEX aggregators: + +```typescript +export const swapAction: Action = { + name: 'EVM_SWAP', + description: 'Swap tokens on the same chain', + + handler: async (runtime, message, state, options, callback) => { + // 1. Extract swap parameters + const params = await extractSwapParams(runtime, message, state); + + // 2. Get quotes from aggregators + const quotes = await Promise.all([ + getLiFiQuote(params), + getBebopQuote(params) + ]); + + // 3. Select best route + const bestQuote = selectBestQuote(quotes); + + // 4. Execute swap + const result = await executeSwap(bestQuote); + + callback?.({ + text: `Swapped ${params.fromAmount} ${params.fromToken} for ${result.toAmount} ${params.toToken}`, + content: result + }); + } +}; +``` + +#### Bridge Action + +Cross-chain token transfers using LiFi: + +```typescript +export const bridgeAction: Action = { + name: 'EVM_BRIDGE', + description: 'Bridge tokens across chains', + + handler: async (runtime, message, state, options, callback) => { + const params = await extractBridgeParams(runtime, message, state); + + // Get bridge route + const route = await lifi.getRoutes({ + fromChainId: params.fromChain, + toChainId: params.toChain, + fromTokenAddress: params.fromToken, + toTokenAddress: params.toToken, + fromAmount: params.amount + }); + + // Execute bridge transaction + const result = await lifi.executeRoute(route.routes[0]); + + callback?.({ + text: `Bridging ${params.amount} from ${params.fromChain} to ${params.toChain}`, + content: { hash: result.hash, route: route.routes[0] } + }); + } +}; +``` + +### Providers + +#### Wallet Provider + +Supplies wallet balance information across all chains: + +```typescript +export const walletProvider: Provider = { + name: 'evmWalletProvider', + + get: async (runtime: IAgentRuntime) => { + const service = runtime.getService('evm-service'); + const data = await service.getCachedData(); + + if (!data?.walletInfo) return null; + + // Format balance information + const balances = data.walletInfo.chains + .map(chain => `${chain.name}: ${chain.nativeBalance} ${chain.symbol}`) + .join('\n'); + + return `Wallet balances:\n${balances}\n\nTotal value: $${data.walletInfo.totalValueUsd}`; + } +}; +``` + +#### Token Balance Provider + +Dynamic provider for checking specific token balances: + +```typescript +export const tokenBalanceProvider: Provider = { + name: 'evmTokenBalance', + + get: async (runtime: IAgentRuntime, message: Memory) => { + const tokenAddress = extractTokenAddress(message); + const chain = extractChain(message); + + const balance = await getTokenBalance( + runtime, + tokenAddress, + chain + ); + + return `Token balance: ${balance}`; + } +}; +``` + +### Templates + +AI prompt templates for parameter extraction: + +```typescript +export const transferTemplate = `Given the recent messages and wallet information: + +{{recentMessages}} + +{{walletInfo}} + +Extract the transfer details: +- Amount to transfer (number only) +- Recipient address or ENS name +- Token symbol (or 'native' for ETH/BNB/etc) +- Chain name + +Respond with: + + string | null + string | null + string | null + string | null +`; +``` + +## Chain Configuration + +The plugin supports dynamic chain configuration: + +```typescript +interface ChainConfig { + chainId: number; + name: string; + chain: Chain; + rpcUrl: string; + nativeCurrency: { + symbol: string; + decimals: number; + }; + walletClient?: WalletClient; + publicClient?: PublicClient; +} + +// Chains are configured based on environment variables +const configureChains = (runtime: IAgentRuntime): ChainConfig[] => { + const chains: ChainConfig[] = []; + + // Check for custom RPC endpoints + Object.entries(viemChains).forEach(([name, chain]) => { + const customRpc = runtime.getSetting(`ETHEREUM_PROVIDER_${name.toUpperCase()}`); + + chains.push({ + chainId: chain.id, + name: chain.name, + chain, + rpcUrl: customRpc || chain.rpcUrls.default.http[0], + nativeCurrency: chain.nativeCurrency + }); + }); + + return chains; +}; +``` + +## Token Resolution + +The plugin automatically resolves token symbols to addresses: + +```typescript +async function resolveTokenAddress( + symbol: string, + chainId: number +): Promise
{ + // Check common tokens first + const commonTokens = { + 'USDC': { + 1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', + // ... other chains + }, + 'USDT': { + 1: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + // ... other chains + } + }; + + if (commonTokens[symbol]?.[chainId]) { + return commonTokens[symbol][chainId]; + } + + // Fallback to LiFi token list + const tokens = await lifi.getTokens({ chainId }); + const token = tokens.find(t => + t.symbol.toLowerCase() === symbol.toLowerCase() + ); + + if (!token) { + throw new Error(`Token ${symbol} not found on chain ${chainId}`); + } + + return token.address; +} +``` + +## Governance Implementation + +The plugin includes comprehensive DAO governance support: + +```typescript +// Propose Action +export const proposeAction: Action = { + name: 'EVM_GOV_PROPOSE', + description: 'Create a governance proposal', + + handler: async (runtime, message, state, options, callback) => { + const params = await extractProposalParams(runtime, message, state); + + const governorContract = getGovernorContract(params.chain); + + const tx = await governorContract.propose( + params.targets, + params.values, + params.calldatas, + params.description + ); + + callback?.({ + text: `Created proposal: ${params.description}`, + content: { hash: tx.hash } + }); + } +}; + +// Vote Action +export const voteAction: Action = { + name: 'EVM_GOV_VOTE', + description: 'Vote on a governance proposal', + + handler: async (runtime, message, state, options, callback) => { + const params = await extractVoteParams(runtime, message, state); + + const voteValue = { + 'for': 1, + 'against': 0, + 'abstain': 2 + }[params.support.toLowerCase()]; + + const tx = await governorContract.castVote( + params.proposalId, + voteValue + ); + + callback?.({ + text: `Voted ${params.support} on proposal ${params.proposalId}`, + content: { hash: tx.hash } + }); + } +}; +``` + +## Error Handling + +Comprehensive error handling for common scenarios: + +```typescript +export async function handleTransactionError( + error: any, + context: string +): Promise { + if (error.code === 'INSUFFICIENT_FUNDS') { + throw new Error(`Insufficient funds for ${context}`); + } + + if (error.code === 'NONCE_TOO_LOW') { + // Handle nonce issues + await resetNonce(); + throw new Error('Transaction nonce issue, please retry'); + } + + if (error.message?.includes('gas required exceeds allowance')) { + throw new Error(`Gas estimation failed for ${context}`); + } + + // Log unknown errors + logger.error(`Unknown error in ${context}:`, error); + throw new Error(`Transaction failed: ${error.message}`); +} +``` + +## Testing + +The plugin includes comprehensive test coverage: + +```typescript +describe('EVM Transfer Action', () => { + it('should transfer native tokens', async () => { + const runtime = await createTestRuntime(); + const message = createMessage('Send 0.1 ETH to 0x123...'); + + const result = await transferAction.handler( + runtime, + message, + state, + {}, + callback + ); + + expect(result).toBe(true); + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + text: expect.stringContaining('Transferred 0.1 ETH') + }) + ); + }); +}); +``` + +## Best Practices + +1. **Always validate addresses** before executing transactions +2. **Use gas buffers** (typically 20%) for reliable execution +3. **Implement retry logic** for network failures +4. **Cache frequently accessed data** to reduce RPC calls +5. **Use simulation** before executing expensive operations +6. **Monitor gas prices** and adjust limits accordingly +7. **Handle slippage** appropriately for swaps +8. **Validate token approvals** before transfers + +## Troubleshooting + +Common issues and solutions: + +- **"Insufficient funds"**: Check wallet balance includes gas costs +- **"Invalid address"**: Ensure address is checksummed correctly +- **"Gas estimation failed"**: Try with a fixed gas limit +- **"Nonce too low"**: Reset nonce or wait for pending transactions +- **"Network error"**: Check RPC endpoint availability \ No newline at end of file diff --git a/plugins/defi/evm/defi-operations-flow.mdx b/plugins/defi/evm/defi-operations-flow.mdx new file mode 100644 index 0000000..5590de8 --- /dev/null +++ b/plugins/defi/evm/defi-operations-flow.mdx @@ -0,0 +1,108 @@ +--- +title: "Operations Flow" +description: "How DeFi operations work in the EVM plugin" +--- + +## Overview + +The EVM plugin handles DeFi operations through a structured flow: + +``` +User Message → Action Recognition → Parameter Extraction → Execution → Response +``` + +## Transfer Flow + +### 1. User Intent +``` +User: Send 0.1 ETH to alice.eth +``` + +### 2. Action Recognition +The plugin identifies this as a transfer action based on keywords (send, transfer, pay). + +### 3. Parameter Extraction +Using AI, the plugin extracts: +- Amount: 0.1 +- Token: ETH +- Recipient: alice.eth (will resolve to address) +- Chain: Detected from context or defaults + +### 4. Execution +- Validates recipient address +- Checks balance +- Builds transaction +- Estimates gas +- Sends transaction +- Waits for confirmation + +### 5. Response +``` +Agent: Successfully transferred 0.1 ETH to alice.eth +Transaction: https://etherscan.io/tx/[hash] +``` + +## Swap Flow + +### 1. User Intent +``` +User: Swap 1 ETH for USDC +``` + +### 2. Route Discovery +- Queries multiple DEX aggregators (LiFi, Bebop) +- Compares routes for best output +- Considers gas costs + +### 3. Execution +- Approves token if needed +- Executes swap transaction +- Monitors for completion + +## Bridge Flow + +### 1. User Intent +``` +User: Bridge 100 USDC from Ethereum to Base +``` + +### 2. Bridge Route +- Finds available bridge routes +- Estimates fees and time +- Selects optimal path + +### 3. Multi-Step Execution +- Source chain transaction +- Wait for bridge confirmation +- Destination chain completion + +## Governance Flow + +### Proposal Creation +``` +User: Create a proposal to increase treasury allocation +→ Plugin creates proposal transaction with targets, values, and description +``` + +### Voting +``` +User: Vote FOR on proposal 42 +→ Plugin casts vote with correct proposal ID and support value +``` + +## Error Handling + +The plugin handles common errors gracefully: + +- **Insufficient Balance**: Checks before attempting transaction +- **Network Issues**: Retries with exponential backoff +- **Invalid Addresses**: Validates all addresses before use +- **High Slippage**: Warns user if slippage exceeds tolerance + +## Key Features + +1. **Natural Language Processing**: Understands various ways to express intents +2. **Multi-Chain Support**: Automatically handles chain selection +3. **Gas Optimization**: Estimates and optimizes gas usage +4. **Safety Checks**: Validates all parameters before execution +5. **Real-Time Feedback**: Provides transaction status updates \ No newline at end of file diff --git a/plugins/defi/evm/examples.mdx b/plugins/defi/evm/examples.mdx new file mode 100644 index 0000000..d3ca80d --- /dev/null +++ b/plugins/defi/evm/examples.mdx @@ -0,0 +1,147 @@ +--- +title: "Examples" +description: "Practical examples for configuring and using the EVM plugin" +--- + +## Configuration + +### Character Configuration + +Add the EVM plugin to your character file: + +```typescript +// character.ts +import { type Character } from '@elizaos/core'; + +export const character: Character = { + name: 'DeFiAgent', + plugins: [ + // Core plugins + '@elizaos/plugin-sql', + '@elizaos/plugin-bootstrap', + + // DeFi plugin + ...(process.env.EVM_PRIVATE_KEY?.trim() ? ['@elizaos/plugin-evm'] : []), + + // Platform plugins + ...(process.env.DISCORD_API_TOKEN?.trim() ? ['@elizaos/plugin-discord'] : []), + ], + settings: { + secrets: {}, + }, + // ... rest of character configuration +}; +``` + +### Environment Variables + +```env +# Required +EVM_PRIVATE_KEY=your_private_key_here + +# Optional - Custom RPC endpoints +ETHEREUM_PROVIDER_ETHEREUM=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY +ETHEREUM_PROVIDER_BASE=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY +ETHEREUM_PROVIDER_ARBITRUM=https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY + +# Optional - TEE Mode +TEE_MODE=true +WALLET_SECRET_SALT=your_salt_here +``` + +## Usage Examples + +### Transfer Operations + +The agent understands natural language for transfers: + +``` +User: Send 0.1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e +Agent: I'll send 0.1 ETH to that address right away. + +User: Transfer 100 USDC to vitalik.eth on Base +Agent: Transferring 100 USDC to vitalik.eth on Base network. + +User: Pay alice.eth 50 DAI on Arbitrum +Agent: Sending 50 DAI to alice.eth on Arbitrum. +``` + +### Swap Operations + +``` +User: Swap 1 ETH for USDC +Agent: I'll swap 1 ETH for USDC using the best available route. + +User: Exchange 100 USDC for DAI with 0.5% slippage +Agent: Swapping 100 USDC for DAI with 0.5% slippage tolerance. +``` + +### Bridge Operations + +``` +User: Bridge 100 USDC from Ethereum to Base +Agent: I'll bridge 100 USDC from Ethereum to Base network. + +User: Move 0.5 ETH from Arbitrum to Optimism +Agent: Bridging 0.5 ETH from Arbitrum to Optimism. +``` + +### Governance Operations + +``` +User: Create a proposal to increase the treasury allocation to 10% +Agent: I'll create a governance proposal for increasing treasury allocation. + +User: Vote FOR on proposal 42 +Agent: Casting your vote FOR proposal #42. + +User: Execute proposal 35 +Agent: Executing proposal #35 after the timelock period. +``` + +## Custom Plugin Integration + +If you need to import the plugin directly in a ProjectAgent: + +```typescript +// index.ts +import { type ProjectAgent } from '@elizaos/core'; +import evmPlugin from '@elizaos/plugin-evm'; +import { character } from './character'; + +export const projectAgent: ProjectAgent = { + character, + plugins: [evmPlugin], // Import custom plugins here + init: async (runtime) => { + // Custom initialization if needed + } +}; +``` + +## Common Patterns + +### Checking Wallet Balance + +``` +User: What's my wallet balance? +Agent: [Agent will use the wallet provider to show balances across all configured chains] +``` + +### Gas Price Awareness + +``` +User: Send 0.1 ETH to alice.eth when gas is low +Agent: I'll monitor gas prices and execute when they're favorable. +``` + +### Multi-Chain Operations + +The plugin automatically detects the chain from context: + +``` +User: Send 100 USDC on Base +Agent: Sending 100 USDC on Base network. + +User: Swap MATIC for USDC on Polygon +Agent: Swapping MATIC for USDC on Polygon network. +``` \ No newline at end of file diff --git a/plugins/defi/evm/testing-guide.mdx b/plugins/defi/evm/testing-guide.mdx new file mode 100644 index 0000000..4bb070d --- /dev/null +++ b/plugins/defi/evm/testing-guide.mdx @@ -0,0 +1,123 @@ +--- +title: "Testing Guide" +description: "How to test the EVM plugin safely on real networks" +--- + +## Testing Philosophy + +The best way to test DeFi plugins is with small amounts on real networks. Test networks often have reliability issues and don't reflect real-world conditions. + +## Safe Testing Practices + +### 1. Start Small + +Always test with minimal amounts first: +- 0.001 ETH for transfers +- $1-5 worth of tokens for swaps +- Smallest viable amounts for bridges + +### 2. Test on Low-Cost Chains First + +Start testing on chains with low transaction fees: +- Polygon: ~$0.01 per transaction +- Base: ~$0.05 per transaction +- Arbitrum: ~$0.10 per transaction + +### 3. Progressive Testing + +``` +1. Test basic transfers first +2. Test token transfers +3. Test swaps with small amounts +4. Test bridges last (they're most complex) +``` + +## Testing Checklist + +### Environment Setup + +```env +# Use a dedicated test wallet +EVM_PRIVATE_KEY=test_wallet_private_key + +# Start with one chain +ETHEREUM_PROVIDER_BASE=https://base-mainnet.g.alchemy.com/v2/YOUR_KEY +``` + +### Basic Tests + +1. **Wallet Connection** + ``` + User: What's my wallet address? + Agent: [Should show your wallet address] + ``` + +2. **Balance Check** + ``` + User: What's my balance? + Agent: [Should show balances across configured chains] + ``` + +3. **Small Transfer** + ``` + User: Send 0.001 ETH to [another test address] + Agent: [Should execute the transfer] + ``` + +4. **Token Transfer** + ``` + User: Send 1 USDC to [test address] + Agent: [Should handle ERC20 transfer] + ``` + +### Swap Testing + +Test swaps with minimal amounts: +``` +User: Swap 0.01 ETH for USDC +Agent: [Should find best route and execute] +``` + +### Error Handling + +Test error scenarios: +- Insufficient balance +- Invalid addresses +- Network issues +- High slippage + +## Monitoring Results + +1. **Transaction Verification** + - Check block explorers (Etherscan, BaseScan, etc.) + - Verify transaction status + - Confirm balances updated + +2. **Gas Usage** + - Monitor gas costs + - Ensure reasonable gas estimates + - Check for failed transactions + +## Common Issues + +### "Insufficient funds for gas" +- Ensure you have native tokens for gas +- Each chain needs its native token (ETH, MATIC, etc.) + +### "Transaction underpriced" +- RPC may be congested +- Try alternative RPC endpoints + +### "Nonce too low" +- Previous transaction may be pending +- Wait for confirmation or reset nonce + +## Production Readiness + +Before using in production: +1. Test all intended operations +2. Verify error handling works +3. Ensure proper logging +4. Set appropriate gas limits +5. Configure slippage tolerances +6. Test with your expected volumes \ No newline at end of file diff --git a/plugins/defi/solana.mdx b/plugins/defi/solana.mdx new file mode 100644 index 0000000..01094e7 --- /dev/null +++ b/plugins/defi/solana.mdx @@ -0,0 +1,167 @@ +--- +title: "Overview" +description: "Enable high-performance Solana blockchain interactions for your AI agent" +--- + +The Solana plugin provides comprehensive integration with the Solana blockchain, enabling AI agents to manage wallets, transfer tokens, perform swaps, and track portfolios with real-time market data. + +## Features + +- **Native SOL & SPL Tokens**: Transfer SOL and any SPL token +- **DeFi Integration**: Token swaps via Jupiter aggregator +- **Portfolio Management**: Real-time balance tracking with USD valuations +- **Market Data**: Live price feeds for SOL, BTC, ETH, and SPL tokens +- **AI-Powered**: Natural language understanding for all operations +- **WebSocket Support**: Real-time account monitoring and updates + +## Installation + +```bash +bun install @elizaos/plugin-solana +``` + +## Configuration + +The plugin requires the following environment variables: + +```env +# Required - Wallet Configuration +SOLANA_PRIVATE_KEY=your_base58_private_key_here +# OR +SOLANA_PUBLIC_KEY=your_public_key_here # For read-only mode + +# Optional - RPC Configuration +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com +HELIUS_API_KEY=your_helius_api_key + +# Optional - Market Data +BIRDEYE_API_KEY=your_birdeye_api_key + +# Optional - AI Service +OPENAI_API_KEY=your_openai_api_key # For enhanced parsing +``` + +## Usage + +```typescript +import { solanaPlugin } from '@elizaos/plugin-solana'; +import { AgentRuntime } from '@elizaos/core'; + +// Initialize the agent with Solana plugin +const runtime = new AgentRuntime({ + plugins: [solanaPlugin], + // ... other configuration +}); +``` + +## Actions + +### Transfer Tokens +Send SOL or SPL tokens to any Solana address. + +Example prompts: +- "Send 1 SOL to 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU" +- "Transfer 100 USDC to alice.sol" +- "Send 50 BONK tokens to Bob's wallet" + +### Swap Tokens +Exchange tokens using Jupiter's aggregator for best prices. + +Example prompts: +- "Swap 10 SOL for USDC" +- "Exchange all my BONK for SOL" +- "Trade 100 USDC for RAY with 1% slippage" + +## Providers + +The plugin includes a comprehensive wallet provider that gives your agent awareness of: +- **Total portfolio value** in USD and SOL +- **Individual token balances** with current prices +- **Real-time updates** via WebSocket subscriptions +- **Token metadata** including symbols and decimals + +## Key Features + +### AI-Powered Intent Parsing +The plugin uses advanced prompt engineering to understand natural language: + +```typescript +// The AI understands various ways to express the same intent: +"Send 1 SOL to alice.sol" +"Transfer 1 SOL to alice" +"Pay alice 1 SOL" +"Give 1 SOL to alice.sol" +``` + +### Automatic Token Resolution +No need to specify token addresses - just use symbols: +- Automatically resolves token symbols to mint addresses +- Fetches current token metadata +- Validates token existence before transactions + +### Real-Time Portfolio Tracking +- Updates every 2 minutes automatically +- WebSocket subscriptions for instant updates +- Comprehensive USD valuations using Birdeye API + +### High-Performance Architecture +- Connection pooling for optimal RPC usage +- Intelligent caching to minimize API calls +- Retry logic with exponential backoff +- Transaction simulation before execution + +## Advanced Configuration + +### Using Helius RPC +For enhanced performance and reliability: + +```env +SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY +HELIUS_API_KEY=your_helius_api_key +``` + +### Custom Network Configuration +Connect to devnet or custom networks: + +```env +SOLANA_RPC_URL=https://api.devnet.solana.com +SOLANA_CLUSTER=devnet +``` + +### Public Key Only Mode +For read-only operations without a private key: + +```env +SOLANA_PUBLIC_KEY=7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU +``` + +## Error Handling + +The plugin includes robust error handling for: +- Insufficient balance errors +- Network timeouts and failures +- Invalid addresses or tokens +- Slippage tolerance exceeded +- Transaction simulation failures + +## Security Considerations + +- Private keys support both base58 and base64 formats +- Never expose private keys in logs or responses +- Use public key mode when write access isn't needed +- Validate all user inputs before execution +- Set appropriate slippage for swaps + +## Performance Tips + +- Use Helius or other premium RPCs for production +- Enable WebSocket connections for real-time updates +- Configure appropriate cache TTLs +- Monitor rate limits on external APIs + +## Next Steps + +- [Complete Documentation →](./solana/complete-documentation) +- [DeFi Operations Flow →](./solana/defi-operations-flow) +- [Examples →](./solana/examples) +- [Testing Guide →](./solana/testing-guide) \ No newline at end of file diff --git a/plugins/defi/solana/complete-documentation.mdx b/plugins/defi/solana/complete-documentation.mdx new file mode 100644 index 0000000..84f97b4 --- /dev/null +++ b/plugins/defi/solana/complete-documentation.mdx @@ -0,0 +1,746 @@ +--- +title: "Developer Guide" +description: "In-depth technical documentation for the Solana blockchain plugin" +--- + +This guide provides comprehensive documentation of the Solana plugin's architecture, implementation, and advanced features. + +## Architecture Overview + +The Solana plugin follows a modular architecture optimized for high-performance blockchain interactions: + +``` +┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Actions │────▶│ SolanaService│────▶│ Solana RPC │ +│ (User Intent) │ │ (Core Logic)│ │ Connection │ +└─────────────────┘ └──────────────┘ └──────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ +│ AI Templates │ │ Providers │ │ Birdeye API │ +│ (NLP Parsing) │ │ (Wallet Data)│ │ (Price Data) │ +└─────────────────┘ └──────────────┘ └──────────────┘ +``` + +## Core Components + +### SolanaService + +The central service managing all Solana blockchain interactions: + +```typescript +export class SolanaService extends Service { + static serviceType = 'solana-service'; + + private connection: Connection; + private keypair?: Keypair; + private wallet?: Wallet; + private cache: Map = new Map(); + private subscriptions: number[] = []; + + async initialize(runtime: IAgentRuntime): Promise { + // Initialize connection + const rpcUrl = runtime.getSetting('SOLANA_RPC_URL') || 'https://api.mainnet-beta.solana.com'; + this.connection = new Connection(rpcUrl, { + commitment: 'confirmed', + wsEndpoint: rpcUrl.replace('https', 'wss') + }); + + // Initialize wallet + const privateKey = runtime.getSetting('SOLANA_PRIVATE_KEY'); + if (privateKey) { + this.keypair = await loadKeypair(privateKey); + this.wallet = new Wallet(this.keypair); + } + + // Start portfolio monitoring + this.startPortfolioTracking(); + + // Register with trader service if available + this.registerWithTraderService(runtime); + } + + private async startPortfolioTracking(): Promise { + // Initial fetch + await this.fetchPortfolioData(); + + // Set up periodic refresh (2 minutes) + setInterval(() => this.fetchPortfolioData(), 120000); + + // Set up WebSocket subscriptions + if (this.keypair) { + this.setupAccountSubscriptions(); + } + } +} +``` + +### Actions + +#### Transfer Action + +Handles SOL and SPL token transfers with intelligent parsing: + +```typescript +export const transferAction: Action = { + name: 'TRANSFER_SOLANA', + description: 'Transfer SOL or SPL tokens on Solana', + + validate: async (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting('SOLANA_PRIVATE_KEY'); + return !!privateKey; + }, + + handler: async (runtime, message, state, options, callback) => { + try { + // Extract parameters using AI + const params = await extractTransferParams(runtime, message, state); + + // Get service instance + const service = runtime.getService('solana-service'); + + // Execute transfer + const result = await executeTransfer(service, params); + + callback?.({ + text: `Successfully transferred ${params.amount} ${params.token} to ${params.recipient}`, + content: { + success: true, + signature: result.signature, + amount: params.amount, + token: params.token, + recipient: params.recipient + } + }); + } catch (error) { + callback?.({ + text: `Transfer failed: ${error.message}`, + content: { error: error.message } + }); + } + }, + + examples: [ + [ + { + name: 'user', + content: { text: 'Send 1 SOL to 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU' } + }, + { + name: 'assistant', + content: { text: "I'll send 1 SOL to that address right away." } + } + ] + ], + + similes: ['SEND_SOL', 'SEND_TOKEN_SOLANA', 'TRANSFER_SOL', 'PAY_SOL'] +}; +``` + +#### Swap Action + +Token swapping using Jupiter aggregator: + +```typescript +export const swapAction: Action = { + name: 'SWAP_SOLANA', + description: 'Swap tokens on Solana using Jupiter', + + handler: async (runtime, message, state, options, callback) => { + // Extract swap parameters + const params = await extractSwapParams(runtime, message, state); + + // Get Jupiter quote + const quote = await getJupiterQuote({ + inputMint: params.fromToken, + outputMint: params.toToken, + amount: params.amount, + slippageBps: params.slippage * 100 // Convert to basis points + }); + + // Execute swap + const result = await executeJupiterSwap( + service.connection, + service.wallet, + quote + ); + + callback?.({ + text: `Swapped ${params.fromAmount} ${params.fromSymbol} for ${formatAmount(quote.outAmount)} ${params.toSymbol}`, + content: { + success: true, + signature: result.signature, + fromAmount: params.fromAmount, + toAmount: formatAmount(quote.outAmount), + route: quote.routePlan + } + }); + } +}; +``` + +### Providers + +#### Wallet Provider + +Supplies comprehensive wallet and portfolio data: + +```typescript +export const walletProvider: Provider = { + name: 'solana-wallet', + description: 'Provides Solana wallet information and portfolio data', + + get: async (runtime: IAgentRuntime, message?: Memory, state?: State) => { + const service = runtime.getService('solana-service'); + const portfolioData = await service.getCachedPortfolioData(); + + if (!portfolioData) { + return 'Wallet data unavailable'; + } + + // Format portfolio for AI context + const summary = formatPortfolioSummary(portfolioData); + const tokenList = formatTokenBalances(portfolioData.tokens); + + return `Solana Wallet Portfolio: +Total Value: $${portfolioData.totalUsd.toFixed(2)} (${portfolioData.totalSol.toFixed(4)} SOL) + +Token Balances: +${tokenList} + +SOL Price: $${portfolioData.solPrice.toFixed(2)} +Last Updated: ${new Date(portfolioData.lastUpdated).toLocaleString()}`; + } +}; +``` + +### Templates + +AI prompt templates for natural language understanding: + +```typescript +export const transferTemplate = `Given the recent messages: +{{recentMessages}} + +And wallet information: +{{walletInfo}} + +Extract the following for a Solana transfer: +- Amount to send (number only) +- Token to send (SOL or token symbol/address) +- Recipient address or domain + +Respond with: + + string + string + string +`; + +export const swapTemplate = `Given the swap request: +{{recentMessages}} + +And available tokens: +{{walletInfo}} + +Extract swap details: +- Input token (symbol or address) +- Input amount (or "all" for max) +- Output token (symbol or address) +- Slippage tolerance (percentage, default 1%) + + + string + string + string + number +`; +``` + +## Advanced Features + +### Keypair Management + +The plugin supports multiple key formats and secure handling: + +```typescript +export async function loadKeypair(privateKey: string): Promise { + try { + // Try base58 format first + const decoded = bs58.decode(privateKey); + if (decoded.length === 64) { + return Keypair.fromSecretKey(decoded); + } + } catch (e) { + // Not base58, try base64 + } + + try { + // Try base64 format + const decoded = Buffer.from(privateKey, 'base64'); + if (decoded.length === 64) { + return Keypair.fromSecretKey(decoded); + } + } catch (e) { + // Not base64 + } + + // Try JSON format (Solana CLI) + try { + const parsed = JSON.parse(privateKey); + if (Array.isArray(parsed)) { + return Keypair.fromSecretKey(Uint8Array.from(parsed)); + } + } catch (e) { + // Not JSON + } + + throw new Error('Invalid private key format'); +} +``` + +### WebSocket Subscriptions + +Real-time account monitoring for instant updates: + +```typescript +private setupAccountSubscriptions(): void { + if (!this.keypair) return; + + // Subscribe to account changes + const accountSub = this.connection.onAccountChange( + this.keypair.publicKey, + (accountInfo) => { + elizaLogger.info('Account balance changed:', { + lamports: accountInfo.lamports, + sol: accountInfo.lamports / LAMPORTS_PER_SOL + }); + + // Trigger portfolio refresh + this.fetchPortfolioData(); + }, + 'confirmed' + ); + + this.subscriptions.push(accountSub); + + // Subscribe to token accounts + this.subscribeToTokenAccounts(); +} + +private async subscribeToTokenAccounts(): Promise { + const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner( + this.keypair.publicKey, + { programId: TOKEN_PROGRAM_ID } + ); + + tokenAccounts.value.forEach(({ pubkey }) => { + const sub = this.connection.onAccountChange( + pubkey, + () => { + elizaLogger.info('Token balance changed'); + this.fetchPortfolioData(); + }, + 'confirmed' + ); + + this.subscriptions.push(sub); + }); +} +``` + +### Portfolio Data Management + +Efficient caching and data fetching: + +```typescript +interface PortfolioData { + totalUsd: number; + totalSol: number; + solPrice: number; + tokens: TokenBalance[]; + lastUpdated: number; +} + +private async fetchPortfolioData(): Promise { + const cacheKey = 'portfolio_data'; + const cached = this.cache.get(cacheKey); + + // Return cached data if fresh (2 minutes) + if (cached && Date.now() - cached.timestamp < 120000) { + return cached.data; + } + + try { + // Fetch from Birdeye API + const response = await fetch( + `https://api.birdeye.so/v1/wallet/portfolio?wallet=${this.keypair.publicKey.toBase58()}`, + { + headers: { + 'X-API-KEY': this.runtime.getSetting('BIRDEYE_API_KEY') + } + } + ); + + const data = await response.json(); + + // Process and cache + const portfolioData = this.processPortfolioData(data); + this.cache.set(cacheKey, { + data: portfolioData, + timestamp: Date.now() + }); + + return portfolioData; + } catch (error) { + elizaLogger.error('Failed to fetch portfolio data:', error); + return cached?.data || this.getEmptyPortfolio(); + } +} +``` + +### Transaction Building + +Optimized transaction construction with priority fees: + +```typescript +async function buildTransferTransaction( + connection: Connection, + sender: PublicKey, + recipient: PublicKey, + amount: number, + token?: string +): Promise { + const transaction = new Transaction(); + + // Add priority fee for faster processing + const priorityFee = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 1000 // 0.001 SOL per compute unit + }); + transaction.add(priorityFee); + + if (!token || token.toUpperCase() === 'SOL') { + // Native SOL transfer + transaction.add( + SystemProgram.transfer({ + fromPubkey: sender, + toPubkey: recipient, + lamports: amount * LAMPORTS_PER_SOL + }) + ); + } else { + // SPL token transfer + const mint = await resolveTokenMint(connection, token); + const senderAta = await getAssociatedTokenAddress(mint, sender); + const recipientAta = await getAssociatedTokenAddress(mint, recipient); + + // Check if recipient ATA exists + const recipientAccount = await connection.getAccountInfo(recipientAta); + if (!recipientAccount) { + // Create ATA for recipient + transaction.add( + createAssociatedTokenAccountInstruction( + sender, + recipientAta, + recipient, + mint + ) + ); + } + + // Add transfer instruction + transaction.add( + createTransferInstruction( + senderAta, + recipientAta, + sender, + amount * Math.pow(10, await getTokenDecimals(connection, mint)) + ) + ); + } + + // Get latest blockhash + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); + transaction.recentBlockhash = blockhash; + transaction.lastValidBlockHeight = lastValidBlockHeight; + transaction.feePayer = sender; + + return transaction; +} +``` + +### Token Resolution + +Intelligent token symbol to mint address resolution: + +```typescript +async function resolveTokenMint( + connection: Connection, + tokenIdentifier: string +): Promise { + // Check if it's already a valid public key + try { + const pubkey = new PublicKey(tokenIdentifier); + // Verify it's a token mint + const accountInfo = await connection.getAccountInfo(pubkey); + if (accountInfo?.owner.equals(TOKEN_PROGRAM_ID)) { + return pubkey; + } + } catch (e) { + // Not a valid public key, continue + } + + // Common token mappings + const commonTokens: Record = { + 'USDC': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + 'USDT': 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', + 'BONK': 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263', + 'RAY': '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R', + 'JTO': 'jtojtomepa8beP8AuQc6eXt5FriJwfFMwQx2v2f9mCL', + // Add more as needed + }; + + const upperToken = tokenIdentifier.toUpperCase(); + if (commonTokens[upperToken]) { + return new PublicKey(commonTokens[upperToken]); + } + + // Try to fetch from token list or registry + throw new Error(`Unknown token: ${tokenIdentifier}`); +} +``` + +### Jupiter Integration + +Advanced swap execution with route optimization: + +```typescript +interface JupiterSwapParams { + inputMint: PublicKey; + outputMint: PublicKey; + amount: number; + slippageBps: number; + userPublicKey: PublicKey; +} + +async function getJupiterQuote(params: JupiterSwapParams): Promise { + const url = new URL('https://quote-api.jup.ag/v6/quote'); + url.searchParams.append('inputMint', params.inputMint.toBase58()); + url.searchParams.append('outputMint', params.outputMint.toBase58()); + url.searchParams.append('amount', params.amount.toString()); + url.searchParams.append('slippageBps', params.slippageBps.toString()); + url.searchParams.append('onlyDirectRoutes', 'false'); + url.searchParams.append('asLegacyTransaction', 'false'); + + const response = await fetch(url.toString()); + if (!response.ok) { + throw new Error(`Jupiter quote failed: ${response.statusText}`); + } + + return response.json(); +} + +async function executeJupiterSwap( + connection: Connection, + wallet: Wallet, + quote: QuoteResponse +): Promise<{ signature: string }> { + // Get serialized transaction from Jupiter + const swapResponse = await fetch('https://quote-api.jup.ag/v6/swap', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + quoteResponse: quote, + userPublicKey: wallet.publicKey.toBase58(), + wrapAndUnwrapSol: true, + prioritizationFeeLamports: 'auto' + }) + }); + + const { swapTransaction } = await swapResponse.json(); + + // Deserialize and sign + const transaction = VersionedTransaction.deserialize( + Buffer.from(swapTransaction, 'base64') + ); + transaction.sign([wallet.payer]); + + // Send with confirmation + const signature = await connection.sendTransaction(transaction, { + skipPreflight: false, + maxRetries: 3 + }); + + // Wait for confirmation + const confirmation = await connection.confirmTransaction({ + signature, + blockhash: transaction.message.recentBlockhash, + lastValidBlockHeight: transaction.message.lastValidBlockHeight + }); + + if (confirmation.value.err) { + throw new Error(`Swap failed: ${confirmation.value.err}`); + } + + return { signature }; +} +``` + +### Error Handling + +Comprehensive error handling with retry logic: + +```typescript +export async function withRetry( + operation: () => Promise, + options: { + maxAttempts?: number; + delay?: number; + backoff?: number; + onError?: (error: Error, attempt: number) => void; + } = {} +): Promise { + const { + maxAttempts = 3, + delay = 1000, + backoff = 2, + onError + } = options; + + let lastError: Error; + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error; + onError?.(error, attempt); + + if (attempt < maxAttempts) { + const waitTime = delay * Math.pow(backoff, attempt - 1); + elizaLogger.warn(`Attempt ${attempt} failed, retrying in ${waitTime}ms`, { + error: error.message + }); + await new Promise(resolve => setTimeout(resolve, waitTime)); + } + } + } + + throw lastError; +} + +// Usage +const result = await withRetry( + () => connection.sendTransaction(transaction), + { + maxAttempts: 3, + onError: (error, attempt) => { + if (error.message.includes('blockhash not found')) { + // Refresh blockhash + transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; + } + } + } +); +``` + +### Performance Optimizations + +#### Connection Pooling + +```typescript +class ConnectionPool { + private connections: Connection[] = []; + private currentIndex = 0; + + constructor(rpcUrls: string[], config?: ConnectionConfig) { + this.connections = rpcUrls.map(url => new Connection(url, config)); + } + + getConnection(): Connection { + const connection = this.connections[this.currentIndex]; + this.currentIndex = (this.currentIndex + 1) % this.connections.length; + return connection; + } + + async healthCheck(): Promise { + const checks = this.connections.map(async (conn, index) => { + try { + await conn.getVersion(); + return { index, healthy: true }; + } catch (error) { + return { index, healthy: false, error }; + } + }); + + const results = await Promise.all(checks); + const unhealthy = results.filter(r => !r.healthy); + + if (unhealthy.length > 0) { + elizaLogger.warn('Unhealthy connections:', unhealthy); + } + } +} +``` + +#### Batch Operations + +```typescript +async function batchGetMultipleAccounts( + connection: Connection, + publicKeys: PublicKey[] +): Promise<(AccountInfo | null)[]> { + const BATCH_SIZE = 100; + const results: (AccountInfo | null)[] = []; + + for (let i = 0; i < publicKeys.length; i += BATCH_SIZE) { + const batch = publicKeys.slice(i, i + BATCH_SIZE); + const batchResults = await connection.getMultipleAccountsInfo(batch); + results.push(...batchResults); + } + + return results; +} +``` + +## Security Considerations + +1. **Private Key Security** + - Never log or expose private keys + - Support multiple secure key formats + - Use environment variables only + +2. **Transaction Validation** + - Always simulate before sending + - Verify recipient addresses + - Check token mint addresses + +3. **Slippage Protection** + - Default 1% slippage + - Maximum 5% slippage + - User confirmation for high slippage + +4. **Rate Limiting** + - Implement request throttling + - Cache frequently accessed data + - Use WebSocket for real-time data + +## Monitoring & Logging + +The plugin provides detailed logging for debugging and monitoring: + +```typescript +// Transaction lifecycle +elizaLogger.info('Transfer initiated', { amount, token, recipient }); +elizaLogger.debug('Transaction built', { instructions: tx.instructions.length }); +elizaLogger.info('Transaction sent', { signature }); +elizaLogger.info('Transaction confirmed', { signature, slot }); + +// Performance metrics +elizaLogger.debug('RPC latency', { method, duration }); +elizaLogger.debug('Cache hit rate', { hits, misses, ratio }); + +// Error tracking +elizaLogger.error('Transaction failed', { error, context }); +elizaLogger.warn('Retry attempt', { attempt, maxAttempts }); +``` \ No newline at end of file diff --git a/plugins/defi/solana/defi-operations-flow.mdx b/plugins/defi/solana/defi-operations-flow.mdx new file mode 100644 index 0000000..0f8d12c --- /dev/null +++ b/plugins/defi/solana/defi-operations-flow.mdx @@ -0,0 +1,116 @@ +--- +title: "Operations Flow" +description: "How DeFi operations work in the Solana plugin" +--- + +## Overview + +The Solana plugin processes DeFi operations through this flow: + +``` +User Message → Action Recognition → AI Parameter Extraction → Execution → Response +``` + +## Transfer Flow + +### 1. User Intent +``` +User: Send 1 SOL to alice.sol +``` + +### 2. Action Recognition +The plugin identifies transfer keywords (send, transfer, pay). + +### 3. Parameter Extraction +AI extracts: +- Amount: 1 +- Token: SOL +- Recipient: alice.sol (resolves to address) + +### 4. Execution Steps +- Resolve .sol domain if needed +- Check balance +- Build transaction with priority fee +- Sign and send +- Wait for confirmation + +### 5. Response +``` +Agent: Successfully sent 1 SOL to alice.sol +Transaction: https://solscan.io/tx/[signature] +``` + +## Swap Flow + +### 1. User Intent +``` +User: Swap 10 SOL for USDC +``` + +### 2. Jupiter Integration +- Get quote from Jupiter API +- Calculate output amount +- Check price impact + +### 3. Execution +- Build swap transaction +- Add priority fees +- Execute and monitor + +### 4. Special Cases +- "Swap all" - calculates max balance +- Custom slippage - applies user preference +- Route selection - optimizes for best price + +## Portfolio Flow + +### 1. User Request +``` +User: What's my portfolio worth? +``` + +### 2. Data Aggregation +- Fetch SOL balance +- Get SPL token balances +- Query prices from Birdeye API + +### 3. Response Format +``` +Total Value: $X,XXX.XX (XX.XX SOL) + +Token Balances: +SOL: 10.5 ($850.50) +USDC: 250.25 ($250.25) +BONK: 1,000,000 ($45.20) +``` + +## Key Features + +### Real-Time Updates +- WebSocket subscriptions for balance changes +- Automatic portfolio refresh every 2 minutes +- Instant transaction notifications + +### Smart Token Resolution +- Common symbols (USDC, USDT, BONK) auto-resolved +- .sol domain support +- Token metadata caching + +### Transaction Optimization +- Priority fees for faster confirmation +- Compute unit optimization +- Automatic retry on failure + +## Error Handling + +### Common Errors +- **Insufficient Balance**: Pre-checks prevent failed transactions +- **Token Not Found**: Clear error messages for unknown tokens +- **Network Issues**: Automatic retry with backoff +- **High Slippage**: Warns before executing + +### Safety Features +1. Balance validation before execution +2. Address verification +3. Slippage protection +4. Transaction simulation when possible \ No newline at end of file diff --git a/plugins/defi/solana/examples.mdx b/plugins/defi/solana/examples.mdx new file mode 100644 index 0000000..cf3468c --- /dev/null +++ b/plugins/defi/solana/examples.mdx @@ -0,0 +1,143 @@ +--- +title: "Examples" +description: "Practical examples for configuring and using the Solana plugin" +--- + +## Configuration + +### Character Configuration + +Add the Solana plugin to your character file: + +```typescript +// character.ts +import { type Character } from '@elizaos/core'; + +export const character: Character = { + name: 'SolanaAgent', + plugins: [ + // Core plugins + '@elizaos/plugin-sql', + '@elizaos/plugin-bootstrap', + + // Solana plugin + ...(process.env.SOLANA_PRIVATE_KEY?.trim() ? ['@elizaos/plugin-solana'] : []), + + // Platform plugins + ...(process.env.DISCORD_API_TOKEN?.trim() ? ['@elizaos/plugin-discord'] : []), + ], + settings: { + secrets: {}, + }, + // ... rest of character configuration +}; +``` + +### Environment Variables + +```env +# Required - Choose one: +SOLANA_PRIVATE_KEY=your_base58_private_key_here +# OR for read-only mode: +SOLANA_PUBLIC_KEY=your_public_key_here + +# Optional - Enhanced RPC +SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY +HELIUS_API_KEY=your_helius_key + +# Optional - Market data +BIRDEYE_API_KEY=your_birdeye_key +``` + +## Usage Examples + +### Transfer Operations + +The agent understands natural language for transfers: + +``` +User: Send 1 SOL to 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU +Agent: I'll send 1 SOL to that address right away. + +User: Transfer 100 USDC to alice.sol +Agent: Transferring 100 USDC to alice.sol. + +User: Pay bob 50 BONK tokens +Agent: Sending 50 BONK to bob. +``` + +### Swap Operations + +``` +User: Swap 10 SOL for USDC +Agent: I'll swap 10 SOL for USDC using Jupiter. + +User: Exchange all my BONK for SOL +Agent: Swapping all your BONK tokens for SOL. + +User: Trade 100 USDC for JTO with 2% slippage +Agent: Swapping 100 USDC for JTO with 2% slippage tolerance. +``` + +### Portfolio Management + +``` +User: What's my wallet balance? +Agent: [Shows total portfolio value and individual token balances] + +User: How much is my portfolio worth? +Agent: Your total portfolio value is $X,XXX.XX (XX.XX SOL) +``` + +## Custom Plugin Integration + +If you need to import the plugin directly in a ProjectAgent: + +```typescript +// index.ts +import { type ProjectAgent } from '@elizaos/core'; +import solanaPlugin from '@elizaos/plugin-solana'; +import { character } from './character'; + +export const projectAgent: ProjectAgent = { + character, + plugins: [solanaPlugin], // Import custom plugins here + init: async (runtime) => { + // Custom initialization if needed + } +}; +``` + +## Common Patterns + +### Domain Name Resolution + +The plugin automatically resolves .sol domains: + +``` +User: Send 5 SOL to vitalik.sol +Agent: Sending 5 SOL to vitalik.sol [resolves to actual address] +``` + +### Token Symbol Resolution + +Common tokens are automatically recognized: + +``` +User: Send 100 USDC to alice +Agent: [Recognizes USDC token mint and handles transfer] +``` + +### All Balance Swaps + +``` +User: Swap all my BONK for USDC +Agent: [Calculates max balance and executes swap] +``` + +### Slippage Control + +``` +User: Swap with 0.5% slippage +Agent: [Sets custom slippage for the swap] +``` \ No newline at end of file diff --git a/plugins/defi/solana/testing-guide.mdx b/plugins/defi/solana/testing-guide.mdx new file mode 100644 index 0000000..f506f36 --- /dev/null +++ b/plugins/defi/solana/testing-guide.mdx @@ -0,0 +1,120 @@ +--- +title: "Testing Guide" +description: "How to test the Solana plugin safely on mainnet" +--- + +## Testing Philosophy + +Test with small amounts on mainnet. Solana devnet/testnet tokens have no value and often have different behavior than mainnet. + +## Safe Testing Practices + +### 1. Start Small + +Test with minimal amounts: +- 0.001 SOL for transfers (~$0.20) +- $1-5 worth of tokens for swaps +- Use common tokens (USDC, USDT) for reliability + +### 2. Transaction Costs + +Solana transactions are cheap (~$0.00025 per transaction), making mainnet testing affordable. + +### 3. Progressive Testing + +``` +1. Check wallet connection +2. Test SOL transfers +3. Test SPL token transfers +4. Test small swaps +5. Test larger operations +``` + +## Testing Checklist + +### Environment Setup + +```env +# Use a dedicated test wallet +SOLANA_PRIVATE_KEY=test_wallet_private_key + +# Optional - Use premium RPC for reliability +SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY +``` + +### Basic Tests + +1. **Wallet Connection** + ``` + User: What's my wallet address? + Agent: [Should show your Solana address] + ``` + +2. **Balance Check** + ``` + User: What's my balance? + Agent: [Should show SOL balance and token holdings] + ``` + +3. **Small SOL Transfer** + ``` + User: Send 0.001 SOL to [another address] + Agent: [Should execute the transfer] + ``` + +4. **Token Transfer** + ``` + User: Send 1 USDC to [test address] + Agent: [Should handle SPL token transfer] + ``` + +### Swap Testing + +Test swaps with small amounts: +``` +User: Swap 0.1 SOL for USDC +Agent: [Should execute via Jupiter] +``` + +### Portfolio Tracking + +``` +User: What's my portfolio worth? +Agent: [Should show total USD value and token breakdown] +``` + +## Monitoring Results + +1. **Transaction Verification** + - Check on Solscan.io or Solana Explorer + - Verify transaction succeeded + - Confirm balance changes + +2. **Common Token Addresses** + - USDC: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + - USDT: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB + - Use these for testing as they're widely supported + +## Common Issues + +### "Insufficient SOL for fees" +- Need ~0.001 SOL for transaction fees +- Keep some SOL for rent and fees + +### "Token account doesn't exist" +- First transfer to a new token creates the account +- Costs ~0.002 SOL for account creation + +### "Slippage tolerance exceeded" +- Increase slippage for volatile tokens +- Try smaller amounts + +## Production Readiness + +Before production use: +1. Test all operations you plan to use +2. Verify error handling +3. Test with your expected token types +4. Monitor transaction success rates +5. Set appropriate slippage (1-3% typical) +6. Ensure adequate SOL for fees \ No newline at end of file diff --git a/plugins/overview.mdx b/plugins/overview.mdx index 31198d0..de06b7b 100644 --- a/plugins/overview.mdx +++ b/plugins/overview.mdx @@ -10,7 +10,7 @@ The Eliza plugin system is a comprehensive extension mechanism that allows devel ## Core Plugins -ElizaOS includes two essential core plugins that provide foundational functionality: +ElizaOS includes essential core plugins that provide foundational functionality: @@ -20,6 +20,60 @@ ElizaOS includes two essential core plugins that provide foundational functional Database integration and management for ElizaOS. Features automatic schema migrations, multi-database support, and a sophisticated plugin architecture. + + + Advanced knowledge base and RAG system for ElizaOS. Provides semantic search, contextual embeddings, and intelligent document processing. + + + +## DeFi Plugins + +Blockchain and DeFi integrations for Web3 functionality: + + + + Multi-chain EVM support with token transfers, swaps, bridging, and governance across 30+ networks including Ethereum, Base, Arbitrum, and more. + + + + High-performance Solana blockchain integration with SOL/SPL transfers, Jupiter swaps, and real-time portfolio tracking. + + + +## Platform Integrations + +Connect your agent to popular platforms: + + + + Full Discord integration with voice, commands, and rich interactions. + + + + Telegram bot functionality with inline keyboards and media support. + + + + Twitter/X integration for posting, replying, and timeline management. + + + +## LLM Providers + +Choose from various language model providers: + + + + GPT-4, GPT-3.5, and other OpenAI models. + + + + Claude 3 and other Anthropic models. + + + + Self-hosted models for privacy and control. + ## 1. Complete Plugin Interface