Skip to content

Commit 62504d3

Browse files
committed
Add tutorial for creating an AI agent with the SDK
1 parent 1679316 commit 62504d3

File tree

8 files changed

+334
-0
lines changed

8 files changed

+334
-0
lines changed

docusaurus.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ const config = {
357357
prism: {
358358
theme: codeTheme,
359359
additionalLanguages: ['csharp', 'gradle', 'bash', 'json'],
360+
magicComments: [
361+
{
362+
className: "git-diff-remove",
363+
line: "remove-next-line",
364+
block: { start: "remove-start", end: "remove-end" },
365+
},
366+
{
367+
className: "git-diff-add",
368+
line: "add-next-line",
369+
block: { start: "add-start", end: "add-end" },
370+
},
371+
],
360372
},
361373
algolia: {
362374
// The application ID provided by Algolia

sdk-sidebar.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ const sidebar = {
5656
},
5757
],
5858
},
59+
{
60+
type:'category',
61+
label: 'Tutorials',
62+
collapsible: false,
63+
collapsed: false,
64+
items: ['tutorials/create-wallet-ai-agent'],
65+
},
5966
{
6067
type: 'category',
6168
label: 'Reference',
54.2 KB
Loading
75.6 KB
Loading
83.2 KB
Loading

sdk/_assets/sdk-ai-agent.png

48.8 KB
Loading
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
---
2+
description: Create a wallet AI agent using MetaMask SDK and Vercel's AI SDK.
3+
---
4+
5+
import Tabs from "@theme/Tabs";
6+
import TabItem from "@theme/TabItem";
7+
8+
# Create a wallet AI agent
9+
10+
This tutorial walks you through creating an AI agent dapp that can display your wallet balance and initiate transactions from your wallet.
11+
You will use a provided template, which sets up MetaMask SDK and [Vercel's AI SDK](https://sdk.vercel.ai/) with a [Next.js](https://nextjs.org/docs) and [Wagmi](https://wagmi.sh/) dapp.
12+
13+
## Prerequisites
14+
15+
- [Node.js](https://nodejs.org/) version 18 or later
16+
- An [OpenAI](https://platform.openai.com/docs/overview) API key
17+
- [MetaMask](https://metamask.io/) installed, with an account that has Linea Sepolia ETH
18+
:::note
19+
You can use the [MetaMask faucet](/developer-tools/faucet) to get Linea Sepolia ETH.
20+
:::
21+
22+
## Steps
23+
24+
### 1. Set up the project
25+
26+
Clone the [`Consensys/wallet-agent`](https://github.com/Consensys/wallet-agent/tree/main) repository, switch to the `initial-setup` branch, and install dependencies:
27+
28+
```bash
29+
git clone git@github.com:Consensys/wallet-agent.git
30+
cd wallet-agent
31+
git switch initial-setup
32+
npm install
33+
```
34+
35+
In the root of your project, create a `.env.local` file:
36+
37+
```bash
38+
touch .env.local
39+
```
40+
41+
In `.env.local`, add an `OPENAI_API_KEY` environment variable, replacing `<YOUR-API-KEY>` with your [OpenAI](https://platform.openai.com/docs/overview) API key.
42+
Vercel's AI SDK will use this environment variable to authenticate your dapp with the OpenAI service.
43+
44+
```text title=".env.local"
45+
OPENAI_API_KEY=<YOUR-API-KEY>
46+
```
47+
48+
### 2. Create the dapp interface
49+
50+
In `app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the Wagmi [MetaMask SDK connector](https://wagmi.sh/react/api/connectors/metaMask) to create a button to connect and disconnect your MetaMask wallet.
51+
52+
Use the `Chat` component to display the AI agent chat interface.
53+
54+
```tsx title="page.tsx"
55+
// add-next-line
56+
+ "use client";
57+
58+
// add-start
59+
+ import { useAccount, useConnect, useDisconnect } from "wagmi";
60+
+ import { metaMask } from "wagmi/connectors";
61+
+ import { Button } from "@/components/ui/button";
62+
+ import { Chat } from "@/components/Chat";
63+
// add-end
64+
import Image from "next/image";
65+
66+
// add-start
67+
+ const ConnectButton = () => {
68+
+ const { connect } = useConnect();
69+
+ const { address, isConnected } = useAccount();
70+
+ const { disconnect } = useDisconnect();
71+
+
72+
+ return (
73+
+ <div className="mx-auto">
74+
+ {isConnected ? (
75+
+ <Button onClick={() => disconnect()}>Disconnect {address}</Button>
76+
+ ) : (
77+
+ <Button onClick={() => connect({ connector: metaMask() })}>Connect</Button>
78+
+ )}
79+
+ </div>
80+
+ );
81+
+ };
82+
// add-end
83+
84+
export default function Home() {
85+
// add-next-line
86+
+ const { isConnected } = useAccount();
87+
return (
88+
<div className="h-screen w-full overflow-y-auto grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
89+
<main className="gap-8 row-start-2 sm:items-start h-full w-full">
90+
<h1>Wallet Agent setup</h1>
91+
// add-start
92+
+ <ConnectButton />
93+
+ { isConnected ? <Chat /> : null}
94+
// add-end
95+
</main>
96+
// ...
97+
```
98+
99+
To test the interface, run the development server and navigate to [`http://localhost:3000`](http://localhost:3000/):
100+
101+
```bash
102+
npm run dev
103+
```
104+
105+
Test that the button works to connect and disconnect from your MetaMask wallet.
106+
When connected, the AI agent chat interface displays with your connected wallet address.
107+
You can test the AI functionality by sending messages in the chat:
108+
109+
<p align="center">
110+
<img src={require("../_assets/sdk-ai-agent.png").default} alt="SDK AI agent initial setup" class="appScreen" />
111+
</p>
112+
113+
### 3. Create a Public Client
114+
115+
In `wagmi.config.ts`, initialize a [Viem Public Client](https://viem.sh/docs/clients/public.html)
116+
with the Linea Sepolia chain.
117+
This Public Client will enable the AI agent to access public JSON-RPC API methods such as retrieving balances:
118+
119+
```ts title="wagmi.config.ts"
120+
// add-next-line
121+
+ import { createPublicClient } from "viem";
122+
import { createConfig, http, cookieStorage, createStorage } from "wagmi";
123+
import { lineaSepolia, linea, mainnet } from "wagmi/chains";
124+
import { metaMask } from "wagmi/connectors";
125+
126+
// add-start
127+
+ export const publicClient = createPublicClient({
128+
+ chain: lineaSepolia,
129+
+ transport: http(),
130+
+ });
131+
// add-end
132+
133+
export function getConfig() {
134+
// ...
135+
```
136+
137+
### 4. Create a tool to get the balance
138+
139+
Use the AI SDK's [tools](https://sdk.vercel.ai/docs/foundations/tools) feature to enable the AI agent to perform specific tasks.
140+
141+
In `ai/tools.ts`, update or remove the example tool.
142+
Use the [`getBalance`](https://viem.sh/docs/actions/public/getBalance) method of your configured Public Client, and Viem's [`formatEther`](https://viem.sh/docs/utilities/formatEther.html) function to create a tool that retrieves the ether balance of the connected wallet:
143+
144+
```ts title="tools.ts"
145+
// add-start
146+
+ import { publicClient } from "@/wagmi.config";
147+
+ import { formatEther } from "viem";
148+
// add-end
149+
import { tool as createTool } from "ai";
150+
import { z } from "zod";
151+
152+
// remove-start
153+
- const tool = createTool({
154+
- description: "Example tool",
155+
- parameters: z.object({
156+
- name: z.string().describe("The name of the user"),
157+
- }),
158+
- execute: async ({ name }) => {
159+
- return { name };
160+
- },
161+
- });
162+
//remove-end
163+
// add-start
164+
+ const balanceTool = createTool({
165+
+ description: "Get the balance of the connected wallet",
166+
+ parameters: z.object({
167+
+ address: z.string().describe("The address of the user"),
168+
+ }),
169+
+ execute: async ({ address }) => {
170+
+ const balance = await publicClient.getBalance({
171+
+ address: address as `0x${string}`,
172+
+ });
173+
+ return { balance: formatEther(balance) };
174+
+ },
175+
+ });
176+
// add-end
177+
178+
export const tools = {
179+
// remove-next-line
180+
- example: tool,
181+
// add-next-line
182+
+ displayBalance: balanceTool,
183+
};
184+
```
185+
186+
In the development server, test that this tool works to get your current Linea Sepolia ETH balance:
187+
188+
<p align="center">
189+
<img src={require("../_assets/sdk-ai-agent-get-balance.png").default} alt="SDK AI agent get balance" class="appScreen" />
190+
</p>
191+
192+
### 5. Create a tool to send transactions
193+
194+
In `ai/tools.ts`, create another tool to send transactions.
195+
In this example, the tool and the `Chat` component are configured to initiate a transaction and provide a button for you to send the transaction:
196+
197+
<Tabs>
198+
<TabItem value="tools.ts">
199+
200+
```ts
201+
import { publicClient } from "@/wagmi.config";
202+
import { formatEther } from "viem";
203+
import { tool as createTool } from "ai";
204+
import { z } from "zod";
205+
206+
const balanceTool = createTool({
207+
// ...
208+
});
209+
210+
// add-start
211+
+ const sendTransactionTool = createTool({
212+
+ description: "Initiate a transaction to the provided wallet address",
213+
+ parameters: z.object({
214+
+ to: z.string().describe("The wallet address of the user"),
215+
+ amount: z.string().describe("The amount of ether to send"),
216+
+ }),
217+
+ execute: async ({ to, amount }) => {
218+
+ return { to, amount };
219+
+ },
220+
+ });
221+
// add-end
222+
223+
export const tools = {
224+
displayBalance: balanceTool,
225+
// add-next-line
226+
+ sendTransaction: sendTransactionTool,
227+
};
228+
```
229+
230+
</TabItem>
231+
<TabItem value="Chat.tsx">
232+
233+
```tsx
234+
// ...
235+
if (toolName === "sendTransaction") {
236+
const {
237+
result,
238+
}: { result: { to: string; amount: string } } =
239+
toolInvocation;
240+
241+
if (isLoading) {
242+
return (
243+
<div key={toolCallId}>
244+
<p>Loading...</p>
245+
</div>
246+
);
247+
}
248+
249+
return (
250+
<div key={toolCallId}>
251+
<Button
252+
className="bg-orange-600 text-orange-100 py-2 px-5 rounded-sm w-fit"
253+
onClick={() =>
254+
sendTransaction({
255+
to: result.to as `0x${string}`,
256+
value: parseEther(result.amount),
257+
})
258+
}
259+
>
260+
Send Transaction
261+
</Button>
262+
<p>
263+
{hash
264+
? `Transaction sent: ${hash}`
265+
: "Transaction not sent"}
266+
</p>
267+
</div>
268+
);
269+
}
270+
// ...
271+
```
272+
273+
</TabItem>
274+
</Tabs>
275+
276+
In the development server, test that this tool works to send Linea Sepolia ETH from your connected address to the address you provide.
277+
278+
When you request the agent to send a transaction, it will provide a button for you to send the transaction, but it will not send it for you:
279+
280+
<p align="center">
281+
<img src={require("../_assets/sdk-ai-agent-txn-not-sent.png").default} alt="NFT confirmation" class="appScreen" />
282+
</p>
283+
284+
When you select the button and confirm the transaction in MetaMask, the transaction will be sent:
285+
286+
<p align="center">
287+
<img src={require("../_assets/sdk-ai-agent-txn-sent.png").default} alt="Multiple NFTs confirmation" class="appScreen" />
288+
</p>
289+
290+
You can check the status of the transaction in the [Linea Sepolia block explorer](https://sepolia.lineascan.build/).
291+
292+
:::note
293+
You can configure the AI agent to directly send the transaction using a [Viem Wallet Client](https://viem.sh/docs/clients/wallet).
294+
:::
295+
296+
## Resources
297+
298+
- View the main branch of the [`Consensys/wallet-agent`](https://github.com/Consensys/wallet-agent) template for the completed implementation of this tutorial.
299+
- Watch the [live coding session](https://www.youtube.com/watch?v=ZVuOaKuAhBQ) on YouTube, in which the MetaMask DevRel team walks through creating a wallet AI agent from the initial template.

src/scss/custom.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,3 +624,19 @@ ol {
624624
max-width: 80rem;
625625
}
626626
}
627+
628+
.git-diff-remove {
629+
background-color: #ff000020;
630+
display: block;
631+
margin: 0 calc(-1 * var(--ifm-pre-padding));
632+
padding: 0 var(--ifm-pre-padding);
633+
border-left: 3px solid #ff000080;
634+
}
635+
636+
.git-diff-add {
637+
background-color: #1aff0020;
638+
display: block;
639+
margin: 0 calc(-1 * var(--ifm-pre-padding));
640+
padding: 0 var(--ifm-pre-padding);
641+
border-left: 3px solid #1aff0080;
642+
}

0 commit comments

Comments
 (0)