How to Build Web3-Enabled AI Agents with Eliza | Quicknode Guides
How to Build Web3-Enabled AI Agents with Eliza
Eliza is an open-source framework for building AI agents with integrated Web3 capabilities. In this guide, we will cover the core concepts of the Eliza framework, then show you how to create your own agent character and then use the EVM plugin to demonstrate blockchain interactions such as ETH transfers.
Overview
What You Will Do
- Set up an Agent character
- Review code that covers actions like transfers and swaps
- Interact with the agent to conduct ETH transfers on Ethereum Sepolia (or EVM network of your choice)
- Lay out future suggestions to build upon
What You Will Need
- Intermediate understanding of programming concepts
- Bun installed (Bun is required for ElizaOS), Node.js, TypeScript and pnpm installed (using nvm is recommended)
- An EVM wallet (with some ETH to simulate transfers, swaps and pay gas fees)
- A Quicknode endpoint (create one here)
- An Anthropic or OpenAI API Key (local model options (ollama) are also available)
| Dependency | Version |
|---|---|
| node | 23.3.0 |
| bun | latest |
What is a16z Eliza?
Eliza is a TypeScript-based framework for building and deploying autonomous AI agents. It provides pre-built systems character definition, runtime management, and cross-platform interactions. Using Eliza, you can create agents with consistent personalities that interact through platforms like Discord, Telegram, or custom interfaces while maintaining shared memory and state management.
Eliza & Web3 Integration
Eliza integrates with blockchain networks through a plugin system that extends the core functionality. These plugins enable AI agents to interact with various blockchains, manage crypto wallets, create transactions, and monitor blockchain events - all while maintaining the agent's personality and conversation abilities.
Some popular web3 protocols that have already implemented Eliza are:
- Solana Plugin (
@eliza/plugin-solana): Handles Solana blockchain interactions with built-in wallet management and trust scoring - Coinbase Plugin (
@eliza/plugin-coinbase): Complete suite for managing crypto payments, mass payouts, and token contracts across multiple chains - Token Contract Plugin (
@eliza/plugin-coinbase): Deploys and interacts with ERC20, ERC721, and ERC1155 smart contracts - MassPayments Plugin (
@eliza/plugin-coinbase): Processes bulk crypto payments with automatic charity contributions - Webhook Plugin (
@eliza/plugin-coinbase-webhooks): Creates and manages blockchain event listeners for real-time notifications - Fuel Plugin (
@elizaos/plugin-fuel): Interfaces with the Fuel Ignition blockchain for ETH transfers - TEE Plugin (
@elizaos/plugin-tee): Enables secure key management in trusted execution environments for both Ethereum and Solana
Check out the full list of Web3 plugins on ElizaOS repository.
Eliza Framework: Concepts
The Eliza framework can be split into 4 concepts:
- Characters: JSON config files defining AI personality and behavior
- Agents: Runtime components managing memory and executing behaviors
- Providers: Data connectors injecting context into interactions
- Actions: Executable behaviors that agents can perform
Characters
Characters in Eliza are JSON configurations that define your AI agent's personality and behavior. Think of them as the DNA of your agent - they contain everything from basic personality traits to complex interaction patterns.
Agents
Agents are the runtime components that bring your characters to life. They manage the actual execution of your AI's behaviors through the AgentRuntime class.
Providers
Providers handle specialized functionality like wallet integrations and data access. Here's how providers are implemented using an EVM wallet example:
export const evmWalletProvider: Provider = {
async get(
runtime: IAgentRuntime,
_message: Memory,
state?: State
): Promise<string | null> {
try {
const walletProvider = await initWalletProvider(runtime);
const address = walletProvider.getAddress();
const balance = await walletProvider.getWalletBalance();
const chain = walletProvider.getCurrentChain();
return `${state?.agentName || "The agent"}'s EVM Wallet Address: ${address}\nBalance: ${balance} ${chain.nativeCurrency.symbol}\nChain ID: ${chain.id}, Name: ${chain.name}`;
} catch (error) {
console.error("Error in EVM wallet provider:", error);
return null;
}
},
};
Actions
Actions define the specific behaviors agents can perform. Here's a typical action implementation for token transfers:
export const transferAction: Action = {
name: "transfer",
description: "Transfer tokens between addresses on the same chain",
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: any,
callback?: HandlerCallback
) => {
const walletProvider = await initWalletProvider(runtime);
const action = new TransferAction(walletProvider);
const paramOptions = await buildTransferDetails(state, runtime, walletProvider);
try {
const transferResp = await action.transfer(paramOptions);
if (callback) {
callback({
text: `Successfully transferred ${paramOptions.amount} tokens to ${paramOptions.toAddress}\nTransaction Hash: ${transferResp.hash}`,
content: { success: true, hash: transferResp.hash, ... }
});
}
return true;
} catch (error) {
console.error("Error during token transfer:", error);
return false;
}
}
};
Project Prerequisite: Create a Quicknode Endpoint
To communicate with the blockchain, you need access to a node. While we could run our own node, here at Quicknode, we make it quick and easy to fire up blockchain nodes. You can register for an account here. Once you boot up a node, retrieve the HTTP URL. It should look like this:
Project Prerequisite: Get ETH from Quicknode Multi-Chain Faucet
In order to conduct activity on-chain, you'll need ETH to pay for gas fees. Since we're using the Sepolia testnet, we can get some test ETH from the Multi-Chain Quicknode Faucet.
Installing ElizaOS CLI
Here's how to get started with the Eliza repository.
First, install the Eliza CLI:
bun i -g @elizaos/cli
Then, create a new agent:
elizaos create defi-agent
Installing Plugins
We'll be using the @elizaos/plugin-evm plug-in to demonstrate ETH transfers via our agent.
First, install the plugin:
elizaos plugins add @elizaos/plugin-evm
Building Your Character
Each character starts with a base configuration. Find examples in src/character.ts for reference.
Let's create a DeFi degen character who lived through the 2021 bull run and survived multiple rug pulls. This character will:
Create a file degen.json in the project root, then include the following JSON config:
{
"name": "YieldMaxoor",
"plugins": [
"@elizaos/plugin-bootstrap",
"@elizaos/plugin-sql",
"@elizaos/plugin-anthropic",
"@elizaos/plugin-openai",
"@elizaos/plugin-evm"
],
"settings": {
"model": "claude-3-5-sonnet-20241022",
"chains": {
"evm": [
"sepolia",
"base",
"arbitrum"
]
},
"secrets": {},
"voice": "en_US-hfc_male-medium"
},
"system": "Roleplay as YieldMaxoor, a battle-tested DeFi degen sharing crypto knowledge and farming wisdom.",
"bio": "YieldMaxoor is a battle-tested DeFi degen who's been farming since the 2020 'DeFi Summer'. Speaks in crypto-native slang and always DYOR-pilled.",
"messageExamples": [
[
{
"name": "{{user1}}",
"content": {
"text": "What do you think about this new farm?"
}
},
{
"name": "YieldMaxoor",
"content": {
"text": "ser, the APY is looking juicy af. audit's coming 'soonβ’' but team is based. probably not a rug. already threw in 2 ETH to test it out ngmi if you're not in this π",
"action": "ANALYZE_FARM"
}
}
],
[
{
"name": "{{user1}}",
"content": {
"text": "How do I avoid IL?"
}
},
{
"name": "YieldMaxoor",
"content": {
"text": "fren, IL is just a temporary state of mind. but if you're ngmi with that, stick to stables farming or single-sided staking. this is financial advice because i'm already poor π
"
}
}
]
],
"adjectives": [
"Based",
"Degen",
"Bullish",
"Battle-tested",
"Yield-pilled"
],
"topics": [
"yield_farming",
"defi_strategies",
"tokenomics",
"protocol_analysis",
"layer2_solutions",
"stablecoin_strategies",
"nft_financialization",
"dao_governance",
"mev_protection",
"lending_protocols"
]
}
EVM Plugin Overview
In this guide, we'll examine the EVM plugin's implementation to understand how it interacts within ElizaOS. You can explore the source code in the EVM plugin repository as we analyze key components.
Transfer Action
In the transfer.ts file, we have a TransferAction class that handles token transfers using a walletProvider to manage connections. The transfer method executes transactions specified amount, recipient address, and chain parameters, returning transaction details or throwing errors on failure.
// Exported for tests
export class TransferAction {
constructor(private walletProvider: WalletProvider) {}
async transfer(params: TransferParams): Promise<Transaction> {
const walletClient = this.walletProvider.getWalletClient(params.fromChain);
if (!walletClient.account) {
throw new Error('Wallet account is not available');
}
try {
const hash = await walletClient.sendTransaction({
account: walletClient.account,
to: params.toAddress,
value: parseEther(params.amount),
data: params.data as Hex,
chain: walletClient.chain,
});
return {
hash,
from: walletClient.account.address,
to: params.toAddress,
value: parseEther(params.amount),
data: params.data as Hex,
};
} catch (error: unknown) {
const errorMessage =
error instanceof Error ? error.message : String(error);
throw new Error(`Transfer failed: ${errorMessage}`);
}
}
}
Templates
Plugins contain templates which are predefined structures that help parse and validate user inputs for specific actions.
Agent File
At the heart of your agent is the agent-start.ts file which is the server initialization file that sets up and manages AI agents that can interact across different platforms (like Discord, Telegram, etc.) and blockchain networks (via plugins).
async function startAgent(
character: Character,
directClient: DirectClient
): Promise<AgentRuntime> {
let db: IDatabaseAdapter & IDatabaseCacheAdapter;
try {
character.id ??= stringToUuid(character.name);
character.username ??= character.name;
const token = getTokenForProvider(character.modelProvider, character);
const runtime: AgentRuntime = await createAgent(
character,
token
);
db = await findDatabaseAdapter(runtime);
runtime.databaseAdapter = db;
const cache = initializeCache(
process.env.CACHE_STORE ?? CacheStore.DATABASE,
character,
process.env.CACHE_DIR ?? "",
db
);
runtime.cacheManager = cache;
await runtime.initialize();
runtime.clients = await initializeClients(character, runtime);
directClient.registerAgent(runtime);
elizaLogger.debug(`Started ${character.name} as ${runtime.agentId}`);
return runtime;
} catch (error) {
elizaLogger.error(
`Error starting agent for character ${character.name}:`,
error
);
elizaLogger.error(error);
if (db) {
await db.close();
}
throw error;
}
}
Interacting with the Agent
Now we'll want to run the character and then the client. In your terminal, run the following command to start your agent character:
bun start --character degen.json
You can also confirm transaction activity by looking up the transaction hash on a block explorer.