Skip to content

Commit 647092d

Browse files
committed
fix: multiple issues with swap, transfer, bridge
1 parent 5f6aaa5 commit 647092d

13 files changed

Lines changed: 2368 additions & 769 deletions

File tree

src/actions/bridge.ts

Lines changed: 591 additions & 39 deletions
Large diffs are not rendered by default.

src/actions/swap.ts

Lines changed: 372 additions & 62 deletions
Large diffs are not rendered by default.

src/actions/transfer.ts

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ import {
55
type Memory,
66
ModelType,
77
type State,
8-
composePrompt,
9-
} from "@elizaos/core";
10-
import { type ByteArray, type Hex, formatEther, parseEther } from "viem";
8+
parseKeyValueXml,
9+
composePromptFromState,
10+
} from '@elizaos/core';
11+
import { type Hex, formatEther, parseEther } from 'viem';
1112

12-
import { type WalletProvider, initWalletProvider } from "../providers/wallet";
13-
import { transferTemplate } from "../templates";
14-
import type { Transaction, TransferParams } from "../types";
13+
import { type WalletProvider, initWalletProvider } from '../providers/wallet';
14+
import { transferTemplate } from '../templates';
15+
import type { Transaction, TransferParams } from '../types';
1516

1617
// Exported for tests
1718
export class TransferAction {
1819
constructor(private walletProvider: WalletProvider) {}
1920

2021
async transfer(params: TransferParams): Promise<Transaction> {
2122
if (!params.data) {
22-
params.data = "0x";
23+
params.data = '0x';
2324
}
2425

2526
const walletClient = this.walletProvider.getWalletClient(params.fromChain);
@@ -34,12 +35,12 @@ export class TransferAction {
3435
to: params.toAddress,
3536
value: parseEther(params.amount),
3637
data: params.data as Hex,
37-
chain: undefined,
38-
} as any);
38+
chain: walletClient.chain,
39+
});
3940

4041
return {
4142
hash,
42-
from: walletClient.account?.address as `0x${string}`,
43+
from: walletClient.account.address,
4344
to: params.toAddress,
4445
value: parseEther(params.amount),
4546
data: params.data as Hex,
@@ -53,11 +54,11 @@ export class TransferAction {
5354

5455
const buildTransferDetails = async (
5556
state: State,
57+
_message: Memory,
5658
runtime: IAgentRuntime,
5759
wp: WalletProvider
5860
): Promise<TransferParams> => {
5961
const chains = wp.getSupportedChains();
60-
state.supportedChains = chains.map((item) => `"${item}"`).join("|");
6162

6263
// Add balances to state for better context in template
6364
const balances = await wp.getWalletBalances();
@@ -66,34 +67,52 @@ const buildTransferDetails = async (
6667
const chainConfig = wp.getChainConfigs(chain as any);
6768
return `${chain}: ${balance} ${chainConfig.nativeCurrency.symbol}`;
6869
})
69-
.join(", ");
70+
.join(', ');
7071

71-
const context = composePrompt({
72+
state = await runtime.composeState(_message, ['RECENT_MESSAGES'], true);
73+
state.supportedChains = chains.join(' | ');
74+
75+
const context = composePromptFromState({
7276
state,
7377
template: transferTemplate,
7478
});
7579

76-
const transferDetails = await runtime.useModel(ModelType.OBJECT_SMALL, {
77-
context,
80+
const xmlResponse = await runtime.useModel(ModelType.TEXT_SMALL, {
81+
prompt: context,
7882
});
7983

80-
const existingChain = wp.chains[transferDetails.fromChain];
84+
const parsedXml = parseKeyValueXml(xmlResponse);
85+
86+
if (!parsedXml) {
87+
throw new Error('Failed to parse XML response from LLM for transfer details.');
88+
}
89+
90+
const transferDetails = parsedXml as unknown as TransferParams;
91+
92+
// Normalize chain name to lowercase to handle case sensitivity issues
93+
const normalizedChainName = transferDetails.fromChain.toLowerCase();
94+
95+
// Check if the normalized chain name exists in the supported chains
96+
const existingChain = wp.chains[normalizedChainName];
8197

8298
if (!existingChain) {
8399
throw new Error(
84-
"The chain " +
100+
'The chain ' +
85101
transferDetails.fromChain +
86-
" not configured yet. Add the chain or choose one from configured: " +
102+
' not configured yet. Add the chain or choose one from configured: ' +
87103
chains.toString()
88104
);
89105
}
90106

107+
// Update the transferDetails with the normalized chain name
108+
transferDetails.fromChain = normalizedChainName as any;
109+
91110
return transferDetails;
92111
};
93112

94113
export const transferAction: Action = {
95-
name: "EVM_TRANSFER_TOKENS",
96-
description: "Transfer tokens between addresses on the same chain",
114+
name: 'EVM_TRANSFER_TOKENS',
115+
description: 'Transfer tokens between addresses on the same chain',
97116
handler: async (
98117
runtime: IAgentRuntime,
99118
message: Memory,
@@ -109,11 +128,7 @@ export const transferAction: Action = {
109128
const action = new TransferAction(walletProvider);
110129

111130
// Compose transfer context
112-
const paramOptions = await buildTransferDetails(
113-
state,
114-
runtime,
115-
walletProvider
116-
);
131+
const paramOptions = await buildTransferDetails(state, message, runtime, walletProvider);
117132

118133
try {
119134
const transferResp = await action.transfer(paramOptions);
@@ -143,31 +158,26 @@ export const transferAction: Action = {
143158
}
144159
},
145160
validate: async (runtime: IAgentRuntime) => {
146-
const privateKey = runtime.getSetting("EVM_PRIVATE_KEY");
147-
return typeof privateKey === "string" && privateKey.startsWith("0x");
161+
const privateKey = runtime.getSetting('EVM_PRIVATE_KEY');
162+
return typeof privateKey === 'string' && privateKey.startsWith('0x');
148163
},
149164
examples: [
150165
[
151166
{
152-
name: "assistant",
167+
name: 'assistant',
153168
content: {
154169
text: "I'll help you transfer 1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
155-
action: "SEND_TOKENS",
170+
action: 'SEND_TOKENS',
156171
},
157172
},
158173
{
159-
name: "user",
174+
name: 'user',
160175
content: {
161-
text: "Transfer 1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
162-
action: "SEND_TOKENS",
176+
text: 'Transfer 1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
177+
action: 'SEND_TOKENS',
163178
},
164179
},
165180
],
166181
],
167-
similes: [
168-
"EVM_TRANSFER",
169-
"EVM_SEND_TOKENS",
170-
"EVM_TOKEN_TRANSFER",
171-
"EVM_MOVE_TOKENS",
172-
],
182+
similes: ['EVM_TRANSFER', 'EVM_SEND_TOKENS', 'EVM_TOKEN_TRANSFER', 'EVM_MOVE_TOKENS'],
173183
};

src/service.ts

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { type IAgentRuntime, Service, elizaLogger } from "@elizaos/core";
1+
import { type IAgentRuntime, Service, logger } from '@elizaos/core';
22
import {
33
CACHE_REFRESH_INTERVAL_MS,
44
EVM_SERVICE_NAME,
55
EVM_WALLET_DATA_CACHE_KEY,
6-
} from "./constants";
7-
import { type WalletProvider, initWalletProvider } from "./providers/wallet";
8-
import type { SupportedChain } from "./types";
6+
} from './constants';
7+
import { type WalletProvider, initWalletProvider } from './providers/wallet';
8+
import type { SupportedChain } from './types';
99

1010
export interface EVMWalletData {
1111
address: string;
@@ -21,7 +21,7 @@ export interface EVMWalletData {
2121

2222
export class EVMService extends Service {
2323
static serviceType: string = EVM_SERVICE_NAME;
24-
capabilityDescription = "EVM blockchain wallet access";
24+
capabilityDescription = 'EVM blockchain wallet access';
2525

2626
private walletProvider: WalletProvider | null = null;
2727
private refreshInterval: NodeJS.Timeout | null = null;
@@ -32,7 +32,7 @@ export class EVMService extends Service {
3232
}
3333

3434
static async start(runtime: IAgentRuntime): Promise<EVMService> {
35-
elizaLogger.log("Initializing EVMService");
35+
logger.log('Initializing EVMService');
3636

3737
const evmService = new EVMService(runtime);
3838

@@ -52,14 +52,14 @@ export class EVMService extends Service {
5252
CACHE_REFRESH_INTERVAL_MS
5353
);
5454

55-
elizaLogger.log("EVM service initialized");
55+
logger.log('EVM service initialized');
5656
return evmService;
5757
}
5858

5959
static async stop(runtime: IAgentRuntime) {
6060
const service = runtime.getService(EVM_SERVICE_NAME);
6161
if (!service) {
62-
elizaLogger.error("EVMService not found");
62+
logger.error('EVMService not found');
6363
return;
6464
}
6565
await service.stop();
@@ -70,7 +70,7 @@ export class EVMService extends Service {
7070
clearInterval(this.refreshInterval);
7171
this.refreshInterval = null;
7272
}
73-
elizaLogger.log("EVM service shutdown");
73+
logger.log('EVM service shutdown');
7474
}
7575

7676
async refreshWalletData(): Promise<void> {
@@ -86,9 +86,7 @@ export class EVMService extends Service {
8686
const chainDetails = Object.entries(balances)
8787
.map(([chainName, balance]) => {
8888
try {
89-
const chain = this.walletProvider!.getChainConfigs(
90-
chainName as SupportedChain
91-
);
89+
const chain = this.walletProvider!.getChainConfigs(chainName as SupportedChain);
9290
return {
9391
chainName,
9492
balance,
@@ -97,52 +95,47 @@ export class EVMService extends Service {
9795
name: chain.name,
9896
};
9997
} catch (error) {
100-
elizaLogger.error(`Error formatting chain ${chainName}:`, error);
98+
logger.error(`Error formatting chain ${chainName}:`, error);
10199
return null;
102100
}
103101
})
104102
.filter(Boolean);
105103

106104
const walletData: EVMWalletData = {
107105
address,
108-
chains: chainDetails as EVMWalletData["chains"],
106+
chains: chainDetails as EVMWalletData['chains'],
109107
timestamp: Date.now(),
110108
};
111109

112110
// Cache the wallet data
113111
await this.runtime.setCache(EVM_WALLET_DATA_CACHE_KEY, walletData);
114112
this.lastRefreshTimestamp = walletData.timestamp;
115113

116-
elizaLogger.log(
117-
"EVM wallet data refreshed for chains:",
118-
chainDetails.map((c) => c?.chainName).join(", ")
114+
logger.log(
115+
'EVM wallet data refreshed for chains:',
116+
chainDetails.map((c) => c?.chainName).join(', ')
119117
);
120118
} catch (error) {
121-
elizaLogger.error("Error refreshing EVM wallet data:", error);
119+
logger.error('Error refreshing EVM wallet data:', error);
122120
}
123121
}
124122

125123
async getCachedData(): Promise<EVMWalletData | undefined> {
126124
try {
127-
const cachedData = await this.runtime.getCache<EVMWalletData>(
128-
EVM_WALLET_DATA_CACHE_KEY
129-
);
125+
const cachedData = await this.runtime.getCache<EVMWalletData>(EVM_WALLET_DATA_CACHE_KEY);
130126

131127
const now = Date.now();
132128
// If data is stale or doesn't exist, refresh it
133-
if (
134-
!cachedData ||
135-
now - cachedData.timestamp > CACHE_REFRESH_INTERVAL_MS
136-
) {
137-
elizaLogger.log("EVM wallet data is stale, refreshing...");
129+
if (!cachedData || now - cachedData.timestamp > CACHE_REFRESH_INTERVAL_MS) {
130+
logger.log('EVM wallet data is stale, refreshing...');
138131
await this.refreshWalletData();
139132
const refreshedData = await this.runtime.getCache<EVMWalletData>(EVM_WALLET_DATA_CACHE_KEY);
140-
return refreshedData || null;
133+
return refreshedData || undefined;
141134
}
142135

143136
return cachedData;
144137
} catch (error) {
145-
elizaLogger.error("Error getting cached EVM wallet data:", error);
138+
logger.error('Error getting cached EVM wallet data:', error);
146139
return undefined;
147140
}
148141
}

0 commit comments

Comments
 (0)