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

chore: pass env variables when setting up GOAT and update GOAT readme #898

Merged
merged 2 commits into from
Dec 10, 2024
Merged
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
10 changes: 7 additions & 3 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
validateCharacterConfig,
} from "@ai16z/eliza";
import { zgPlugin } from "@ai16z/plugin-0g";
import { goatPlugin } from "@ai16z/plugin-goat";
import createGoatPlugin from "@ai16z/plugin-goat";
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
// import { buttplugPlugin } from "@ai16z/plugin-buttplug";
import {
Expand Down Expand Up @@ -353,7 +353,7 @@ function getSecret(character: Character, secret: string) {

let nodePlugin: any | undefined;

export function createAgent(
export async function createAgent(
character: Character,
db: IDatabaseAdapter,
cache: ICacheManager,
Expand All @@ -367,6 +367,10 @@ export function createAgent(

nodePlugin ??= createNodePlugin();

const goatPlugin = await createGoatPlugin((secret) =>
getSecret(character, secret)
);

return new AgentRuntime({
databaseAdapter: db,
token,
Expand Down Expand Up @@ -454,7 +458,7 @@ async function startAgent(character: Character, directClient) {
await db.init();

const cache = intializeDbCache(character, db);
const runtime = createAgent(character, db, cache, token);
const runtime = await createAgent(character, db, cache, token);

await runtime.initialize();

Expand Down
51 changes: 39 additions & 12 deletions packages/plugin-goat/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
# Goat Plugin
Example plugin setup of how you can integrate [Goat](https://ohmygoat.dev/) tools and plugins with Eliza.

Adds onchain capabilities to your agent to send and check balances of ETH and USDC. Add all the capabilities you need by adding more plugins!

## Setup
1. Configure your wallet (key pair, smart wallet, etc. see all available wallets at [https://ohmygoat.dev/wallets](https://ohmygoat.dev/wallets))
2. Add the plugins you need (uniswap, zora, polymarket, etc. see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))
3. Select a chain (see all available chains at [https://ohmygoat.dev/chains](https://ohmygoat.dev/chains))
4. Import and add the plugin to your Eliza agent
5. Build the project
6. Add the necessary environment variables to set up your wallet and plugins
# GOAT Plugin
[GOAT](https://ohmygoat.dev/) 🐐 (Great Onchain Agent Toolkit) is an open-source framework for adding blockchain tools such as wallets, being able to hold or trade tokens, or interacting with blockchain smart contracts, to your AI agent.

This plugin integrates GOAT with Eliza, giving your agent the ability to interact with many different protocols. The current setup adds onchain capabilities to your agent to send and check balances of ETH and USDC. Add all the capabilities you need by adding more plugins (read below for more information)!

## Configure GOAT for your use case
1. Configure the chain you want to use by updating the `wallet.ts` file (see all available chains at [https://ohmygoat.dev/chains](https://ohmygoat.dev/chains))
2. Add the plugins you need to your `getOnChainActions` function (uniswap, polymarket, etc. see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))
3. Build the project running `pnpm build`
4. Add the necessary environment variables to set up your wallet and plugins
5. Run your agent!

## Common Issues
1. **Agent not executing an action**:
- If you are also using the EVM Plugin, sometimes the agent might confuse the action name with an EVM Plugin action name instead of the GOAT Plugin action. Removing the EVM Plugin should fix this issue. There is no need for you to use both plugins at the same time.
- If you are using Trump as a character it might be tricky to get them to perform any action since the character is full of prompts that aim to change the topic of the conversation. To fix this try using a different character or create your own with prompts that are more suitable to what the agent is supposed to do.

## Plugins
GOAT itself has several plugins for interacting with different protocols such as Polymarket, Uniswap, and more. (see all available plugins at [https://ohmygoat.dev/chains-wallets-plugins](https://ohmygoat.dev/chains-wallets-plugins))

You can easily add them by installing them and adding them to the `getOnChainActions` function:

```typescript
const tools = getOnChainActions({
wallet: walletClient,
plugins: [
sendETH(),
erc20({ tokens: [USDC, PEPE] }),
polymarket(),
uniswap(),
// ...
],
})
```

## Wallets
GOAT supports many different wallets from key pairs to [Crossmint Smart Wallets](https://docs.crossmint.com/wallets/smart-wallets/overview) and Coinbase.

Read more about wallets at [https://ohmygoat.dev/wallets](https://ohmygoat.dev/wallets).
44 changes: 14 additions & 30 deletions packages/plugin-goat/src/actions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
type WalletClient,
type Plugin,
getDeferredTools,
addParametersToDescription,
type ChainForWalletClient,
type DeferredTool,
type Tool,
getTools,
} from "@goat-sdk/core";
import {
type Action,
Expand All @@ -19,10 +18,8 @@ import {
} from "@ai16z/eliza";

type GetOnChainActionsParams<TWalletClient extends WalletClient> = {
chain: ChainForWalletClient<TWalletClient>;
getWalletClient: (runtime: IAgentRuntime) => Promise<TWalletClient>;
wallet: TWalletClient;
plugins: Plugin<TWalletClient>[];
supportsSmartWallets?: boolean;
};

/**
Expand All @@ -32,30 +29,24 @@ type GetOnChainActionsParams<TWalletClient extends WalletClient> = {
* @returns
*/
export async function getOnChainActions<TWalletClient extends WalletClient>({
getWalletClient,
wallet,
plugins,
chain,
supportsSmartWallets,
}: GetOnChainActionsParams<TWalletClient>): Promise<Action[]> {
const tools = await getDeferredTools<TWalletClient>({
const tools = await getTools<TWalletClient>({
wallet,
plugins,
wordForTool: "action",
chain,
supportsSmartWallets,
});

return tools
.map((action) => ({
...action,
name: action.name.toUpperCase(),
}))
.map((tool) => createAction(tool, getWalletClient));
.map((tool) => createAction(tool));
}

function createAction<TWalletClient extends WalletClient>(
tool: DeferredTool<TWalletClient>,
getWalletClient: (runtime: IAgentRuntime) => Promise<TWalletClient>
): Action {
function createAction(tool: Tool): Action {
return {
name: tool.name,
similes: [],
Expand All @@ -69,7 +60,6 @@ function createAction<TWalletClient extends WalletClient>(
callback?: HandlerCallback
): Promise<boolean> => {
try {
const walletClient = await getWalletClient(runtime);
let currentState =
state ?? (await runtime.composeState(message));
currentState =
Expand All @@ -94,10 +84,7 @@ function createAction<TWalletClient extends WalletClient>(
return false;
}

const result = await tool.method(
walletClient,
parsedParameters.data
);
const result = await tool.method(parsedParameters.data);
const responseContext = composeResponseContext(
tool,
result,
Expand All @@ -124,10 +111,7 @@ function createAction<TWalletClient extends WalletClient>(
};
}

function composeParameterContext<TWalletClient extends WalletClient>(
tool: DeferredTool<TWalletClient>,
state: State
): string {
function composeParameterContext(tool: Tool, state: State): string {
const contextTemplate = `{{recentMessages}}

Given the recent messages, extract the following information for the action "${tool.name}":
Expand All @@ -136,10 +120,10 @@ ${addParametersToDescription("", tool.parameters)}
return composeContext({ state, template: contextTemplate });
}

async function generateParameters<TWalletClient extends WalletClient>(
async function generateParameters(
runtime: IAgentRuntime,
context: string,
tool: DeferredTool<TWalletClient>
tool: Tool
): Promise<unknown> {
const { object } = await generateObjectV2({
runtime,
Expand All @@ -151,8 +135,8 @@ async function generateParameters<TWalletClient extends WalletClient>(
return object;
}

function composeResponseContext<TWalletClient extends WalletClient>(
tool: DeferredTool<TWalletClient>,
function composeResponseContext(
tool: Tool,
result: unknown,
state: State
): string {
Expand Down
54 changes: 29 additions & 25 deletions packages/plugin-goat/src/index.ts
odilitime marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import type { Plugin } from '@ai16z/eliza'
import { getOnChainActions } from './actions';
import { erc20, USDC } from '@goat-sdk/plugin-erc20';
import { chain, getWalletClient, walletProvider } from './provider';
import { sendETH } from '@goat-sdk/core';
import type { Plugin } from "@ai16z/eliza";
import { getOnChainActions } from "./actions";
import { erc20, USDC } from "@goat-sdk/plugin-erc20";
import { sendETH } from "@goat-sdk/core";
import { getWalletClient, getWalletProvider } from "./wallet";

export const goatPlugin: Plugin = {
name: "[GOAT] Onchain Actions",
description: "Base integration plugin",
providers: [walletProvider],
evaluators: [],
services: [],
actions: [
...(await getOnChainActions({
getWalletClient,
// Add plugins here based on what actions you want to use
// See all available plugins at https://ohmygoat.dev/chains-wallets-plugins#plugins
plugins: [sendETH(), erc20({ tokens: [USDC] })],
chain: {
type: "evm",
id: chain.id,
},
})),
],
};
async function createGoatPlugin(
getSetting: (key: string) => string | undefined
): Promise<Plugin> {
const walletClient = getWalletClient(getSetting);
const actions = await getOnChainActions({
wallet: walletClient,
// Add plugins here based on what actions you want to use
// See all available plugins at https://ohmygoat.dev/chains-wallets-plugins#plugins
plugins: [
sendETH(),
erc20({ tokens: [USDC] }),
],
});

export default goatPlugin
return {
name: "[GOAT] Onchain Actions",
description: "Base integration plugin",
providers: [getWalletProvider(walletClient)],
evaluators: [],
services: [],
actions: actions,
};
}

export default createGoatPlugin;
54 changes: 0 additions & 54 deletions packages/plugin-goat/src/provider.ts

This file was deleted.

42 changes: 42 additions & 0 deletions packages/plugin-goat/src/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { WalletClient } from "@goat-sdk/core";
import { viem } from "@goat-sdk/wallet-viem";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

// Add the chain you want to use, remember to update also
// the EVM_PROVIDER_URL to the correct one for the chain
export const chain = base;

export function getWalletClient(
getSetting: (key: string) => string | undefined
) {
const privateKey = getSetting("EVM_PRIVATE_KEY");
if (!privateKey) return null;

const provider = getSetting("EVM_PROVIDER_URL");
if (!provider) throw new Error("EVM_PROVIDER_URL not configured");

const wallet = createWalletClient({
account: privateKeyToAccount(privateKey as `0x${string}`),
chain: chain,
transport: http(provider),
});

return viem(wallet);
}

export function getWalletProvider(walletClient: WalletClient) {
return {
async get(): Promise<string | null> {
try {
const address = walletClient.getAddress();
const balance = await walletClient.balanceOf(address);
return `EVM Wallet Address: ${address}\nBalance: ${balance} ETH`;
} catch (error) {
console.error("Error in EVM wallet provider:", error);
return null;
}
},
};
}