GOAT plugins are extensions that enable your agent to interact with various blockchain protocols.

Instead of having a generic function to execute arbitrary transactions, plugins provide specific tools for common protocols to reduce the risk of hallucinations.

Key Benefits

  • Modularity: Easily add or remove functionalities.
  • Reusability: Leverage community-built plugins.
  • Customization: Design plugins to meet the unique needs.

Chain Compatibility

Plugins can be EVM/Solana specific or chain-agnostic. If a plugin is chain-specific it will fail to compile when being used with a wallet of a different chain.

See all available plugins here.


Plugin Architecture

Plugins extend the PluginBase class defined in the core package:

import { PluginBase } from "@goat-sdk/core";

export class YourPlugin extends PluginBase<EVMWalletClient> {
    constructor(params: YourPluginParams) {
        super("yourPlugin", []);
    }

    supportsChain = (chain: Chain) => chain.type === "evm";
}

export const yourPlugin = (params: YourPluginParams) => new YourPlugin(params);

Plugins get passed a WalletClient that can be chain-specific or chain-agnostic.

The WalletClient abstracts the underlying wallet implementation and provides a common interface to:

  1. Get wallet information
  2. Sign messages
  3. Send transactions

This allows plugins to:

  1. Work with any wallet implementation that implements the WalletClient interface, from key pairs to smart wallets.
  2. Focus on the specific communication with the protocol without worrying about handling transactions, message signing, etc. for each wallet implementation.

Creating your own GOAT plugin

Building a custom GOAT plugin is straightforward. Below, we’ll walk you through creating a plugin that signs messages with “BAAAA” 🐐 prefixed to them.

1

Define the plugin interface

Start by defining your plugin extending the PluginBase class.

Since we are just signing messages, we will create a chain-agnostic plugin that works both on EVM and Solana chains.

import { PluginBase, WalletClientBase } from "@goat-sdk/core";

// Since we are creating a chain-agnostic plugin, we can use the WalletClientBase interface
export class BAAAASigner extends PluginBase<WalletClientBase> {
    constructor() {
        // We define the name of the plugin
        super("baaaSigner", []);
    }

    // We define the chain support for the plugin, in this case we support all chains
    supportsChain = (chain: Chain) => true;
}

// We export a factory function to create a new instance of the plugin
export const baaaSigner = () => new BAAAASigner();
2

Add tools to the plugin

There are two ways to add tools to the plugin:

  1. Using the getTools and createTool functions
  2. Using the @Tool decorator on our own class

Option 1: Using the getTools and createTool functions

We will start by implementing the getTools method in our plugin class.

Inside the method, we will return an array of tools created using the createTool function.

import { PluginBase, WalletClientBase, createTool } from "@goat-sdk/core";

// Since we are creating a chain-agnostic plugin, we can use the WalletClientBase interface
export class BAAAASigner extends PluginBase<WalletClientBase> {
    constructor() {
        // We define the name of the plugin
        super("baaaSigner", []);
    }

    // We define the chain support for the plugin, in this case we support all chains
    supportsChain = (chain: Chain) => true;

    getTools(walletClient: WalletClientBase) {
        return [
            // Create tool requires two arguments:
            // 1. The tool metadata (name, description, parameters)
            // 2. The tool method (the function that will be executed when the tool is used)
            createTool(
                {
                    name: "sign_message_baaaa",
                    description: "Sign a message with 'BAAAA' prefix",
                    parameters: z.object({
                        message: z.string(),
                    }),
                },
                async (parameters) => {
                    const originalMessage: string = parameters.message;
                    const prefixedMessage = `BAAAA${originalMessage}`;
                    const signed = await walletClient.signMessage(prefixedMessage);
                    return signed.signedMessage;
                },
            ),
        ];
    }
}

// We export a factory function to create a new instance of the plugin
export const baaaSigner = () => new BAAAASigner();

Option 2: Using the @Tool decorator

The @Tool decorator is a way to create tools in a more declarative way. You can create a class and decorate its methods with the @Tool decorator to create tools. The tool methods will receive the wallet client as the first argument and the parameters as the second argument.

import { Tool } from "@goat-sdk/core";

class MyTools {
    @Tool({
        name: "sign_message_baaaa",
        description: "Sign a message with 'BAAAA' prefix",
        parameters: z.object({
            message: z.string(),
        }),
    })
    async signMessage(walletClient: WalletClientBase, parameters: z.infer<typeof parameters>) {
        const originalMessage: string = parameters.message;
        const prefixedMessage = `BAAAA${originalMessage}`;
        const signed = await walletClient.signMessage(prefixedMessage);
        return signed.signedMessage;
    }
}

Once we have our class we now need to import it in our plugin class.

   import { PluginBase, WalletClientBase } from "@goat-sdk/core";
   import { MyTools } from "./my-tools.service.ts";

export class BAAAASigner extends PluginBase<WalletClientBase> {
       constructor() {
           // Import the tools we created in the previous step here
           super("baaaSigner", [new MyTools()]);
       }

       supportsChain = (chain: Chain) => true;
}

export const baaaSigner = () => new BAAAASigner();
3

Add the plugin to the agent

import { getOnChainTools } from '@goat-sdk/adapter-vercel-ai';
import { baaaSigner } from './your-plugin-path/signMessagePlugin'; // Path to your plugin

const wallet = /* Initialize your wallet client */;

const tools = getOnChainTools({
wallet: viem(wallet), // or smartwallet(wallet), solana(wallet), etc.
plugins: [
    baaaSigner(),
    // ...other plugins
],
});

// Prompt: Sign the message "Go out and eat grass 🐐"

Next steps