Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
node_modules
.turbo
dist
.env
.elizadb
.elizadb-test
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @elizaos-plugins/plugin-solana
# @elizaos/plugin-solana

Core Solana blockchain plugin for Eliza OS that provides essential services and actions for token operations, trading, and DeFi integrations.

Expand Down Expand Up @@ -55,7 +55,7 @@ The Solana plugin serves as a foundational component of Eliza OS, bridging Solan
## Installation

```bash
npm install @elizaos-plugins/plugin-solana
npm install @elizaos/plugin-solana
```

## Configuration
Expand All @@ -80,7 +80,7 @@ const solanaEnvSchema = {
### Basic Setup

```typescript
import { solanaPlugin } from '@elizaos-plugins/plugin-solana';
import { solanaPlugin } from '@elizaos/plugin-solana';

// Initialize the plugin
const runtime = await initializeRuntime({
Expand Down
185 changes: 185 additions & 0 deletions __tests__/actions/transfer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { Connection, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
import { createAssociatedTokenAccountInstruction, createTransferInstruction, getAssociatedTokenAddressSync } from '@solana/spl-token';
import transferToken from '../../src/actions/transfer';
import { getWalletKey } from '../../src/keypairUtils';
import type { IAgentRuntime, Memory, State, HandlerCallback } from '@elizaos/core';

// Mock dependencies
vi.mock('../../src/keypairUtils');
vi.mock('@solana/web3.js', () => ({
Connection: vi.fn(),
PublicKey: vi.fn().mockImplementation((key) => ({
toBase58: () => key,
toString: () => key
})),
SystemProgram: {
transfer: vi.fn().mockReturnValue({ type: 'transfer' })
},
TransactionMessage: vi.fn(),
VersionedTransaction: vi.fn().mockImplementation(() => ({
sign: vi.fn()
}))
}));
vi.mock('@solana/spl-token', () => ({
createAssociatedTokenAccountInstruction: vi.fn(),
createTransferInstruction: vi.fn(),
getAssociatedTokenAddressSync: vi.fn()
}));
vi.mock('@elizaos/core', async () => {
const actual = await vi.importActual('@elizaos/core');
return {
...actual,
ModelType: {
TEXT_LARGE: 'text-large'
},
composePromptFromState: vi.fn(),
parseJSONObjectFromText: vi.fn(),
logger: {
log: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
info: vi.fn()
}
};
});

const mockKeypair = {
publicKey: new PublicKey('11111111111111111111111111111112'),
secretKey: new Uint8Array(64)
};

const mockConnection = {
getLatestBlockhash: vi.fn(),
sendTransaction: vi.fn(),
getParsedAccountInfo: vi.fn(),
getAccountInfo: vi.fn()
};

const mockRuntime: Partial<IAgentRuntime> = {
getSetting: vi.fn(),
useModel: vi.fn()
};

const mockMessage: Memory = {
id: '12345678-1234-1234-1234-123456789012' as `${string}-${string}-${string}-${string}-${string}`,
entityId: '12345678-1234-1234-1234-123456789012' as `${string}-${string}-${string}-${string}-${string}`,
content: { text: 'Send 1 SOL to 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa' },
createdAt: Date.now(),
roomId: '12345678-1234-1234-1234-123456789012' as `${string}-${string}-${string}-${string}-${string}`
};

const mockState: State = {
agentName: 'TestAgent',
values: {},
data: {},
text: ''
};

describe('Transfer Action', () => {
beforeEach(() => {
vi.clearAllMocks();

// Setup default mocks
(getWalletKey as any).mockResolvedValue({ keypair: mockKeypair });
(Connection as any).mockImplementation(() => mockConnection);
mockConnection.getLatestBlockhash.mockResolvedValue({
blockhash: 'test-blockhash',
lastValidBlockHeight: 12345
});
mockConnection.sendTransaction.mockResolvedValue('test-signature');
(mockRuntime.getSetting as any).mockReturnValue('https://api.mainnet-beta.solana.com');
});

afterEach(() => {
vi.restoreAllMocks();
});

describe('Basic Properties', () => {
it('should have correct name and similes', () => {
expect(transferToken.name).toBe('TRANSFER_SOLANA');
expect(transferToken.similes).toContain('TRANSFER_SOL');
expect(transferToken.similes).toContain('SEND_TOKEN_SOLANA');
expect(transferToken.similes).toContain('PAY_SOL');
});

it('should have correct description', () => {
expect(transferToken.description).toBe('Transfer SOL or SPL tokens to another address on Solana.');
});

it('should have handler function', () => {
expect(typeof transferToken.handler).toBe('function');
});

it('should have validate function', () => {
expect(typeof transferToken.validate).toBe('function');
});

it('should have examples', () => {
expect(transferToken.examples).toBeDefined();
expect(Array.isArray(transferToken.examples)).toBe(true);
expect(transferToken.examples?.length).toBeGreaterThan(0);
});
});

describe('validate', () => {
it('should return true for valid message', async () => {
const result = await transferToken.validate(mockRuntime as IAgentRuntime, mockMessage);
expect(result).toBe(true);
});
});

describe('Examples', () => {
it('should have valid examples', () => {
expect(transferToken.examples).toBeDefined();
expect(Array.isArray(transferToken.examples)).toBe(true);
expect(transferToken.examples?.length).toBeGreaterThan(0);

// Check first example structure
const firstExample = transferToken.examples?.[0];
expect(Array.isArray(firstExample)).toBe(true);
expect(firstExample?.[0]).toHaveProperty('name');
expect(firstExample?.[0]).toHaveProperty('content');
expect(firstExample?.[1]).toHaveProperty('name');
expect(firstExample?.[1]).toHaveProperty('content');
});

it('should have examples with valid structure', () => {
if (transferToken.examples) {
for (const example of transferToken.examples) {
expect(Array.isArray(example)).toBe(true);
expect(example.length).toBeGreaterThan(0);

for (const step of example) {
expect(step).toHaveProperty('name');
expect(step).toHaveProperty('content');
expect(typeof step.name).toBe('string');
expect(typeof step.content).toBe('object');
}
}
}
});
});

describe('Similes', () => {
it('should have valid similes array', () => {
expect(Array.isArray(transferToken.similes)).toBe(true);
expect(transferToken.similes?.length).toBeGreaterThan(0);

if (transferToken.similes) {
for (const simile of transferToken.similes) {
expect(typeof simile).toBe('string');
expect(simile.length).toBeGreaterThan(0);
}
}
});

it('should include expected similes', () => {
const expectedSimiles = ['TRANSFER_SOL', 'SEND_TOKEN_SOLANA', 'PAY_SOL'];

for (const expected of expectedSimiles) {
expect(transferToken.similes).toContain(expected);
}
});
});
});
Loading