Skip to content

Commit

Permalink
Merge pull request #3045 from edwin-finance/plugin-edwin
Browse files Browse the repository at this point in the history
feat: Add edwin plugin to eliza
  • Loading branch information
samarth30 authored Feb 3, 2025
2 parents ea23ea1 + 97be0fd commit 9ab8f5a
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 0 deletions.
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@elizaos/plugin-cosmos": "workspace:*",
"@elizaos/plugin-echochambers": "workspace:*",
"@elizaos/plugin-evm": "workspace:*",
"@elizaos/plugin-edwin": "workspace:*",
"@elizaos/plugin-flow": "workspace:*",
"@elizaos/plugin-gelato": "workspace:*",
"@elizaos/plugin-giphy": "workspace:*",
Expand Down
5 changes: 5 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import { confluxPlugin } from "@elizaos/plugin-conflux";
import { createCosmosPlugin } from "@elizaos/plugin-cosmos";
import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm";
import { evmPlugin } from "@elizaos/plugin-evm";
import { edwinPlugin } from "@elizaos/plugin-edwin";
import { flowPlugin } from "@elizaos/plugin-flow";
import { fuelPlugin } from "@elizaos/plugin-fuel";
import { genLayerPlugin } from "@elizaos/plugin-genlayer";
Expand Down Expand Up @@ -1067,6 +1068,10 @@ export async function createAgent(
getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
? evmPlugin
: null,
(getSecret(character, "EVM_PRIVATE_KEY") ||
getSecret(character, "SOLANA_PRIVATE_KEY"))
? edwinPlugin
: null,
(getSecret(character, "EVM_PUBLIC_KEY") ||
getSecret(character, "INJECTIVE_PUBLIC_KEY")) &&
getSecret(character, "INJECTIVE_PRIVATE_KEY")
Expand Down
67 changes: 67 additions & 0 deletions packages/plugin-edwin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# @elizaos/plugin-edwin

Edwin plugin for Eliza that enables interaction with Edwin tools for DeFi operations.

## About

See full info and docs at [Edwin docs](https://docs.edwin.finance).
## Setup

1. Install dependencies:

```bash
pnpm install
```

2. Configure environment variables for chains you want to support:

```env
EVM_PRIVATE_KEY=<YOUR_EVM_PRIVATE_KEY>
SOLANA_PRIVATE_KEY=<YOUR_SOLANA_PRIVATE_KEY>
```

## Available Tools

The plugin provides access to the following Edwin tools:

- supply
- withdraw
- stake
- addLiquidity
- removeLiquidity

## Usage Examples

1. Supply on AAVE:

```
Supply 100 USDC to AAVE
```

2. Add liquidity on Meteora:

```
Find a meteora pool with high liquidity and add to td 10 USDC and 0.01 SOL.
```

## Development

1. Build the plugin:

```bash
pnpm build
```

2. Run in development mode:

```bash
pnpm dev
```

## Dependencies

- edwin-sdk

## License

MIT
17 changes: 17 additions & 0 deletions packages/plugin-edwin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@elizaos/plugin-edwin",
"version": "0.1.0",
"description": "Edwin plugin for elizaos agent",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"edwin-sdk": "0.3.4",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch"
}
}
138 changes: 138 additions & 0 deletions packages/plugin-edwin/src/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
type Action,
generateText,
type HandlerCallback,
type IAgentRuntime,
type Memory,
ModelClass,
type State,
composeContext,
generateObjectDeprecated,
} from "@elizaos/core";

import { Edwin, EdwinAction } from "edwin-sdk";

type GetEdwinActionsParams = {
getClient: () => Promise<Edwin>;
};

/**
* Get all edwin actions
*/
export async function getEdwinActions({
getClient,
}: GetEdwinActionsParams): Promise<Action[]> {
const edwin = await getClient();
const edwinActions = await edwin.getActions();
const actions = edwinActions.map((action: EdwinAction) => ({
name: action.name.toUpperCase(),
description: action.description,
similes: [],
validate: async () => true,
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State | undefined,
options?: Record<string, unknown>,
callback?: HandlerCallback
): Promise<boolean> => {
try {
const client = await getClient();
if (!state) {
state = (await runtime.composeState(message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
}
const parameterContext = composeContext({
state,
template: action.template,
});
const parameters = await generateObjectDeprecated({
runtime,
context: parameterContext,
modelClass: ModelClass.LARGE,
});
const result = await executeAction(action, parameters, client);
const responseContext = composeResponseContext(
action,
result,
state
);
const response = await generateResponse(
runtime,
responseContext
);
callback?.({ text: response, content: result });
return true;
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
callback?.({
text: `Error executing action ${action.name}: ${errorMessage}`,
content: { error: errorMessage },
});
return false;
}
},
examples: [],
}));
return actions;
}

async function executeAction(
action: EdwinAction,
parameters: any,
edwin: Edwin
): Promise<unknown> {
const result = await action.execute(parameters);
return result;
}

function composeResponseContext(
action: EdwinAction,
result: unknown,
state: State
): string {
const responseTemplate = `
# Action Examples
{{actionExamples}}
# Knowledge
{{knowledge}}
# Task: Generate dialog and actions for the character {{agentName}}.
About {{agentName}}:
{{bio}}
{{lore}}
{{providers}}
{{attachments}}
# Capabilities
Note that {{agentName}} is capable of reading/seeing/hearing various forms of media, including images, videos, audio, plaintext and PDFs. Recent attachments have been included above under the "Attachments" section.
The action "${action.name}" was executed successfully.
Here is the result:
${JSON.stringify(result)}
{{actions}}
Respond to the message knowing that the action was successful and these were the previous messages:
{{recentMessages}}
`;
const context = composeContext({ state, template: responseTemplate });
return context;
}

async function generateResponse(
runtime: IAgentRuntime,
context: string
): Promise<string> {
const response = await generateText({
runtime,
context,
modelClass: ModelClass.LARGE,
});
return response;
}
28 changes: 28 additions & 0 deletions packages/plugin-edwin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Plugin } from "@elizaos/core";
import { edwinProvider, getEdwinClient } from "./provider";
import { getEdwinActions } from "./actions";

// Initial banner
console.log("\n┌═════════════════════════════════════┐");
console.log("│ EDWIN PLUGIN │");
console.log("│ ,_, │");
console.log("│ (o,o) │");
console.log("│ {`\"'} │");
console.log("│ -\"-\"- │");
console.log("├─────────────────────────────────────┤");
console.log("│ Initializing Edwin Plugin... │");
console.log("│ Version: 0.0.1 │");
console.log("└═════════════════════════════════════┘");

export const edwinPlugin: Plugin = {
name: "[Edwin] Integration",
description: "Edwin integration plugin",
providers: [edwinProvider],
evaluators: [],
services: [],
actions: await getEdwinActions({
getClient: getEdwinClient,
}),
};

export default edwinPlugin;
34 changes: 34 additions & 0 deletions packages/plugin-edwin/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Provider, IAgentRuntime } from "@elizaos/core";
import { Edwin } from "edwin-sdk";
import { EdwinConfig } from "edwin-sdk";

// Static variable to hold the singleton instance
let edwinRunningInstance: Edwin | null = null;

export async function getEdwinClient(): Promise<Edwin> {
// If instance exists, return it
if (edwinRunningInstance) {
return edwinRunningInstance;
}
// Otherwise create new instance
const edwinConfig: EdwinConfig = {
evmPrivateKey: process.env.EVM_PRIVATE_KEY as `0x${string}`,
solanaPrivateKey: process.env.SOLANA_PRIVATE_KEY as string,
actions: ["supply", "withdraw", "stake", "getPools", "addLiquidity"],
};

edwinRunningInstance = new Edwin(edwinConfig);
return edwinRunningInstance;
}

export const edwinProvider: Provider = {
async get(runtime: IAgentRuntime): Promise<string | null> {
try {
const edwin = await getEdwinClient();
return edwin.getPortfolio();
} catch (error) {
console.error("Error in Edwin provider:", error);
return null;
}
},
};
9 changes: 9 additions & 0 deletions packages/plugin-edwin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "./src",
"declaration": true
},
"include": ["src"]
}
21 changes: 21 additions & 0 deletions packages/plugin-edwin/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"], // Ensure you're targeting CommonJS
external: [
"dotenv", // Externalize dotenv to prevent bundling
"fs", // Externalize fs to use Node.js built-in module
"path", // Externalize other built-ins if necessary
"@reflink/reflink",
"@node-llama-cpp",
"https",
"http",
"agentkeepalive",
"viem",
"@lifi/sdk",
],
});

0 comments on commit 9ab8f5a

Please sign in to comment.