Skip to content

Commit 6a04ab7

Browse files
authored
Merge branch 'develop' into develop
2 parents 66ee7b2 + 02a4b61 commit 6a04ab7

17 files changed

+1462
-401
lines changed

.github/workflows/integrationTests.yaml

+17-1
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,30 @@ jobs:
1515
steps:
1616
- uses: actions/checkout@v4
1717

18+
- name: Cache pnpm
19+
uses: actions/cache@v4
20+
with:
21+
path: |
22+
~/.pnpm-store
23+
**/node_modules
24+
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
25+
restore-keys: ${{ runner.os }}-pnpm-
26+
27+
- name: Cache plugin dist
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
**/packages/plugin-*/dist
32+
key: ${{ runner.os }}-plugin-dist-${{ hashFiles('**/pnpm-lock.yaml') }}
33+
restore-keys: ${{ runner.os }}-plugin-dist-
34+
1835
- uses: pnpm/action-setup@v3
1936
with:
2037
version: 9.15.0
2138

2239
- uses: actions/setup-node@v4
2340
with:
2441
node-version: "23.3.0"
25-
cache: "pnpm"
2642

2743
- name: Clean up
2844
run: pnpm clean

packages/plugin-abstract/package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
"dist"
2020
],
2121
"dependencies": {
22-
"@abstract-foundation/agw-client": "^0.1.7",
22+
"@abstract-foundation/agw-client": "1.0.1",
2323
"@elizaos/core": "workspace:*",
2424
"tsup": "^8.3.5",
2525
"viem": "2.22.2"
2626
},
2727
"scripts": {
28-
"build": "tsup --format esm --dts"
28+
"build": "tsup --format esm --dts",
29+
"dev": "tsup --format esm --dts --watch"
2930
},
3031
"peerDependencies": {
3132
"whatwg-url": "7.1.0"
3233
}
33-
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import type { Action } from "@elizaos/core";
2+
import {
3+
type ActionExample,
4+
type Content,
5+
type HandlerCallback,
6+
type IAgentRuntime,
7+
type Memory,
8+
ModelClass,
9+
type State,
10+
elizaLogger,
11+
composeContext,
12+
generateObject,
13+
stringToUuid,
14+
} from "@elizaos/core";
15+
import { validateAbstractConfig } from "../environment";
16+
import { parseEther, type Hash } from "viem";
17+
import { abstractTestnet } from "viem/chains";
18+
import {
19+
type AbstractClient,
20+
createAbstractClient,
21+
} from "@abstract-foundation/agw-client";
22+
import { z } from "zod";
23+
import { useGetAccount, useGetWalletClient } from "../hooks";
24+
import basicToken from "../constants/contracts/basicToken.json";
25+
import { abstractPublicClient } from "../utils/viemHelpers";
26+
27+
const DeploySchema = z.object({
28+
name: z.string(),
29+
symbol: z.string(),
30+
initialSupply: z.string(),
31+
useAGW: z.boolean(),
32+
});
33+
34+
const validatedSchema = z.object({
35+
name: z.string().min(1, "Name is required"),
36+
symbol: z
37+
.string()
38+
.min(1, "Symbol is required")
39+
.max(5, "Symbol must be 5 characters or less"),
40+
initialSupply: z
41+
.string()
42+
.refine((val) => !Number.isNaN(Number(val)) && Number(val) > 0, {
43+
message: "Initial supply must be a positive number",
44+
}),
45+
useAGW: z.boolean(),
46+
});
47+
48+
export interface DeployContent extends Content {
49+
name: string;
50+
symbol: string;
51+
initialSupply: string;
52+
useAGW: boolean;
53+
}
54+
55+
const deployTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
56+
57+
Example response:
58+
\`\`\`json
59+
{
60+
"name": "My Token",
61+
"symbol": "MTK",
62+
"initialSupply": "1000000",
63+
"useAGW": true
64+
}
65+
\`\`\`
66+
67+
User message:
68+
"{{currentMessage}}"
69+
70+
Given the message, extract the following information about the requested token deployment:
71+
- Token name
72+
- Token symbol (usually 3-4 characters)
73+
- Initial supply amount
74+
- Whether to use Abstract Global Wallet aka AGW
75+
76+
If the user did not specify "global wallet", "AGW", "agw", or "abstract global wallet" in their message, set useAGW to false, otherwise set it to true.
77+
78+
Respond with a JSON markdown block containing only the extracted values.`;
79+
80+
export const deployTokenAction: Action = {
81+
name: "DEPLOY_TOKEN",
82+
similes: [
83+
"CREATE_TOKEN",
84+
"DEPLOY_NEW_TOKEN",
85+
"CREATE_NEW_TOKEN",
86+
"LAUNCH_TOKEN",
87+
],
88+
validate: async (runtime: IAgentRuntime) => {
89+
await validateAbstractConfig(runtime);
90+
return true;
91+
},
92+
description: "Deploy a new ERC20 token contract",
93+
handler: async (
94+
runtime: IAgentRuntime,
95+
message: Memory,
96+
state: State,
97+
_options: { [key: string]: unknown },
98+
callback?: HandlerCallback,
99+
): Promise<boolean> => {
100+
elizaLogger.log("Starting Abstract DEPLOY_TOKEN handler...");
101+
102+
if (!state) {
103+
state = (await runtime.composeState(message)) as State;
104+
} else {
105+
state = await runtime.updateRecentMessageState(state);
106+
}
107+
108+
state.currentMessage = `${state.recentMessagesData[1].content.text}`;
109+
const deployContext = composeContext({
110+
state,
111+
template: deployTemplate,
112+
});
113+
114+
const content = (
115+
await generateObject({
116+
runtime,
117+
context: deployContext,
118+
modelClass: ModelClass.SMALL,
119+
schema: DeploySchema,
120+
})
121+
).object as DeployContent;
122+
123+
// Validate deployment content
124+
const result = validatedSchema.safeParse(content);
125+
if (!result.success) {
126+
elizaLogger.error("Invalid content for DEPLOY_TOKEN action.", {
127+
errors: result.error.errors,
128+
});
129+
if (callback) {
130+
callback({
131+
text: "Unable to process token deployment request. Invalid parameters provided.",
132+
content: { error: "Invalid deployment parameters" },
133+
});
134+
}
135+
return false;
136+
}
137+
138+
try {
139+
const account = useGetAccount(runtime);
140+
const supply = parseEther(content.initialSupply);
141+
let hash: Hash;
142+
143+
if (content.useAGW) {
144+
const abstractClient = (await createAbstractClient({
145+
chain: abstractTestnet,
146+
signer: account,
147+
})) as any; // type being exported as never
148+
149+
hash = await abstractClient.deployContract({
150+
abi: basicToken.abi,
151+
bytecode: basicToken.bytecode,
152+
args: [result.data.name, result.data.symbol, supply],
153+
});
154+
} else {
155+
const walletClient = useGetWalletClient();
156+
157+
hash = await walletClient.deployContract({
158+
chain: abstractTestnet,
159+
account,
160+
abi: basicToken.abi,
161+
bytecode: basicToken.bytecode,
162+
args: [result.data.name, result.data.symbol, supply],
163+
kzg: undefined,
164+
});
165+
}
166+
167+
// Wait for transaction receipt
168+
const receipt = await abstractPublicClient.waitForTransactionReceipt({
169+
hash,
170+
});
171+
const contractAddress = receipt.contractAddress;
172+
173+
elizaLogger.success(
174+
`Token deployment completed! Contract address: ${contractAddress}. Transaction hash: ${hash}`,
175+
);
176+
if (callback) {
177+
callback({
178+
text: `Token "${result.data.name}" (${result.data.symbol}) deployed successfully! Contract address: ${contractAddress} and transaction hash: ${hash}`,
179+
content: {
180+
hash,
181+
tokenName: result.data.name,
182+
tokenSymbol: result.data.symbol,
183+
contractAddress,
184+
transactionHash: hash,
185+
},
186+
});
187+
}
188+
189+
const metadata = {
190+
tokenAddress: contractAddress,
191+
name: result.data.name,
192+
symbol: result.data.symbol,
193+
initialSupply: String(result.data.initialSupply),
194+
};
195+
196+
await runtime.messageManager.createMemory({
197+
id: stringToUuid(`${result.data.symbol}-${runtime.agentId}`),
198+
userId: runtime.agentId,
199+
content: {
200+
text: `Token deployed: ${result.data.name}, symbol: ${result.data.symbol} and contract address: ${contractAddress}`,
201+
...metadata,
202+
source: "abstract_token_deployment",
203+
},
204+
agentId: runtime.agentId,
205+
roomId: stringToUuid(`tokens-${runtime.agentId}`),
206+
createdAt: Date.now(),
207+
});
208+
elizaLogger.success("memory saved for token deployment", metadata);
209+
210+
return true;
211+
} catch (error) {
212+
elizaLogger.error("Error during token deployment:", error);
213+
if (callback) {
214+
callback({
215+
text: `Error deploying token: ${error.message}`,
216+
content: { error: error.message },
217+
});
218+
}
219+
return false;
220+
}
221+
},
222+
223+
examples: [
224+
[
225+
{
226+
user: "{{user1}}",
227+
content: {
228+
text: "Deploy a new token called MyToken with symbol MTK and initial supply of 1000000",
229+
},
230+
},
231+
{
232+
user: "{{agent}}",
233+
content: {
234+
text: "I'll deploy your new token now.",
235+
action: "DEPLOY_TOKEN",
236+
},
237+
},
238+
{
239+
user: "{{agent}}",
240+
content: {
241+
text: "Successfully deployed MyToken (MTK) with 1000000 initial supply.\nContract address: 0xdde850f9257365fffffc11324726ebdcf5b90b01c6eec9b3e7ab3e81fde6f14b\nTransaction hash: 0xdde850f9257365fffffc11324726ebdcf5b90b01c6eec9b3e7ab3e81fde6f14b",
242+
},
243+
},
244+
],
245+
[
246+
{
247+
user: "{{user1}}",
248+
content: {
249+
text: "Create a new token using AGW with name TestCoin, symbol TEST, and 5000 supply",
250+
},
251+
},
252+
{
253+
user: "{{agent}}",
254+
content: {
255+
text: "I'll deploy your token using the Abstract Global Wallet.",
256+
action: "DEPLOY_TOKEN",
257+
},
258+
},
259+
{
260+
user: "{{agent}}",
261+
content: {
262+
text: "Successfully deployed TestCoin (TEST) with 5000 initial supply using AGW.\nContract address: 0xdde850f9257365fffffc11324726ebdcf5b90b01c6eec9b3e7ab3e81fde6f14b\nTransaction: 0x4fed598033f0added272c3ddefd4d83a521634a738474400b27378db462a76ec",
263+
},
264+
},
265+
],
266+
] as ActionExample[][],
267+
};

0 commit comments

Comments
 (0)