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

feat: Add AGW support to the Abstract plugin #2207

Merged
merged 11 commits into from
Jan 13, 2025
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
1 change: 1 addition & 0 deletions packages/plugin-abstract/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dist"
],
"dependencies": {
"@abstract-foundation/agw-client": "^0.1.7",
"@elizaos/core": "workspace:*",
"tsup": "^8.3.5",
"viem": "2.22.2"
Expand Down
135 changes: 100 additions & 35 deletions packages/plugin-abstract/src/actions/transferAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { validateAbstractConfig } from "../environment";

import {
Address,
createWalletClient,
erc20Abi,
http,
parseEther,
Expand All @@ -25,6 +24,7 @@ import {
} from "viem";
import { abstractTestnet, mainnet } from "viem/chains";
import { normalize } from "viem/ens";
import { createAbstractClient } from "@abstract-foundation/agw-client";
import { z } from "zod";
import { ValidateContext } from "../utils";
import { ETH_ADDRESS, ERC20_OVERRIDE_INFO } from "../constants";
Expand All @@ -39,12 +39,14 @@ const TransferSchema = z.object({
tokenAddress: z.string(),
recipient: z.string(),
amount: z.string(),
useAGW: z.boolean(),
});

export interface TransferContent extends Content {
tokenAddress: string;
recipient: string;
amount: string | number;
useAGW: boolean;
}

const transferTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
Expand All @@ -58,16 +60,21 @@ Example response:
{
"tokenAddress": "0x5A7d6b2F92C77FAD6CCaBd7EE0624E64907Eaf3E",
"recipient": "0xCCa8009f5e09F8C5dB63cb0031052F9CB635Af62",
"amount": "1000"
"amount": "1000",
"useAGW": true
}
\`\`\`

{{recentMessages}}
User message:
"{{currentMessage}}"

Given the recent messages, extract the following information about the requested token transfer:
Given the message, extract the following information about the requested token transfer:
- Token contract address
- Recipient wallet address
- Amount to transfer
- Whether to use Abstract Global Wallet aka AGW

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.

Respond with a JSON markdown block containing only the extracted values.`;

Expand Down Expand Up @@ -104,6 +111,7 @@ export const transferAction: Action = {
}

// Compose transfer context
state.currentMessage = `${state.recentMessagesData[1].content.text}`;
const transferContext = composeContext({
state,
template: transferTemplate,
Expand Down Expand Up @@ -138,7 +146,7 @@ export const transferAction: Action = {

// Validate transfer content
if (!ValidateContext.transferAction(content)) {
console.error("Invalid content for TRANSFER_TOKEN action.");
elizaLogger.error("Invalid content for TRANSFER_TOKEN action.");
if (callback) {
callback({
text: "Unable to process transfer request. Invalid content provided.",
Expand All @@ -150,40 +158,76 @@ export const transferAction: Action = {

try {
const account = useGetAccount(runtime);
const walletClient = useGetWalletClient();

let hash;

// Check if the token is native
if (
content.tokenAddress.toLowerCase() !== ETH_ADDRESS.toLowerCase()
) {
// Convert amount to proper token decimals
const tokenInfo =
ERC20_OVERRIDE_INFO[content.tokenAddress.toLowerCase()];
const decimals = tokenInfo?.decimals ?? 18; // Default to 18 decimals if not specified
const tokenAmount = parseUnits(
content.amount.toString(),
decimals
);

// Execute ERC20 transfer
hash = await walletClient.writeContract({
account,
if (content.useAGW) {
const abstractClient = await createAbstractClient({
chain: abstractTestnet,
address: content.tokenAddress as Address,
abi: erc20Abi,
functionName: "transfer",
args: [content.recipient as Address, tokenAmount],
signer: account,
});

// Handle AGW transfer based on token type
if (
content.tokenAddress.toLowerCase() !==
ETH_ADDRESS.toLowerCase()
) {
const tokenInfo =
ERC20_OVERRIDE_INFO[content.tokenAddress.toLowerCase()];
const decimals = tokenInfo?.decimals ?? 18;
const tokenAmount = parseUnits(
content.amount.toString(),
decimals
);

// @ts-ignore - will fix later
hash = await abstractClient.writeContract({
chain: abstractTestnet,
address: content.tokenAddress as Address,
abi: erc20Abi,
functionName: "transfer",
args: [content.recipient as Address, tokenAmount],
});
} else {
// @ts-ignore
hash = await abstractClient.sendTransaction({
chain: abstractTestnet,
to: content.recipient as Address,
value: parseEther(content.amount.toString()),
kzg: undefined,
});
}
} else {
hash = await walletClient.sendTransaction({
account: account,
chain: abstractTestnet,
to: content.recipient as Address,
value: parseEther(content.amount.toString()),
kzg: undefined,
});
const walletClient = useGetWalletClient();

// Handle regular wallet transfer based on token type
if (
content.tokenAddress.toLowerCase() !==
ETH_ADDRESS.toLowerCase()
) {
const tokenInfo =
ERC20_OVERRIDE_INFO[content.tokenAddress.toLowerCase()];
const decimals = tokenInfo?.decimals ?? 18;
const tokenAmount = parseUnits(
content.amount.toString(),
decimals
);

hash = await walletClient.writeContract({
account,
chain: abstractTestnet,
address: content.tokenAddress as Address,
abi: erc20Abi,
functionName: "transfer",
args: [content.recipient as Address, tokenAmount],
});
} else {
hash = await walletClient.sendTransaction({
account,
chain: abstractTestnet,
to: content.recipient as Address,
value: parseEther(content.amount.toString()),
kzg: undefined,
});
}
}

elizaLogger.success(
Expand Down Expand Up @@ -233,6 +277,27 @@ export const transferAction: Action = {
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Send 0.01 ETH to 0x114B242D931B47D5cDcEe7AF065856f70ee278C4 using your abstract global wallet",
},
},
{
user: "{{agent}}",
content: {
text: "Sure, I'll send 0.01 ETH to that address now using my AGW.",
action: "SEND_TOKEN",
},
},
{
user: "{{agent}}",
content: {
text: "Successfully sent 0.01 ETH to 0x114B242D931B47D5cDcEe7AF065856f70ee278C4\nTransaction: 0xdde850f9257365fffffc11324726ebdcf5b90b01c6eec9b3e7ab3e81fde6f14b using my AGW",
},
},
],
[
{
user: "{{user1}}",
Expand Down
Loading
Loading