Skip to content

Commit 6e1548d

Browse files
authored
Merge pull request #1009 from nicky-ru/plugin-evm-multichain
feat: Plugin evm multichain
2 parents 08d8a0d + d88f3b5 commit 6e1548d

File tree

11 files changed

+570
-559
lines changed

11 files changed

+570
-559
lines changed

packages/plugin-evm/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
},
1616
"scripts": {
1717
"build": "tsup --format esm --dts",
18-
"dev": "tsup --format esm --dts --watch"
18+
"dev": "tsup --format esm --dts --watch",
19+
"test": "vitest run"
1920
},
2021
"peerDependencies": {
2122
"whatwg-url": "7.1.0"

packages/plugin-evm/src/actions/bridge.ts

+17-21
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import type { IAgentRuntime, Memory, State } from "@ai16z/eliza";
22
import {
3-
ChainId,
43
createConfig,
54
executeRoute,
65
ExtendedChain,
76
getRoutes,
87
} from "@lifi/sdk";
98
import { WalletProvider } from "../providers/wallet";
10-
import { getChainConfigs } from "../providers/chainConfigs";
119
import { bridgeTemplate } from "../templates";
1210
import type { BridgeParams, Transaction } from "../types";
1311

@@ -19,25 +17,23 @@ export class BridgeAction {
1917
constructor(private walletProvider: WalletProvider) {
2018
this.config = createConfig({
2119
integrator: "eliza",
22-
chains: Object.values(
23-
getChainConfigs(this.walletProvider.runtime)
24-
).map((config) => ({
25-
id: config.chainId,
20+
chains: Object.values(this.walletProvider.chains).map((config) => ({
21+
id: config.id,
2622
name: config.name,
2723
key: config.name.toLowerCase(),
2824
chainType: "EVM",
2925
nativeToken: {
3026
...config.nativeCurrency,
31-
chainId: config.chainId,
27+
chainId: config.id,
3228
address: "0x0000000000000000000000000000000000000000",
3329
coinKey: config.nativeCurrency.symbol,
3430
},
3531
metamask: {
36-
chainId: `0x${config.chainId.toString(16)}`,
32+
chainId: `0x${config.id.toString(16)}`,
3733
chainName: config.name,
3834
nativeCurrency: config.nativeCurrency,
39-
rpcUrls: [config.rpcUrl],
40-
blockExplorerUrls: [config.blockExplorerUrl],
35+
rpcUrls: [config.rpcUrls.default.http[0]],
36+
blockExplorerUrls: [config.blockExplorers.default.url],
4137
},
4238
diamondAddress: "0x0000000000000000000000000000000000000000",
4339
coin: config.nativeCurrency.symbol,
@@ -47,16 +43,15 @@ export class BridgeAction {
4743
}
4844

4945
async bridge(params: BridgeParams): Promise<Transaction> {
50-
const walletClient = this.walletProvider.getWalletClient();
46+
const walletClient = this.walletProvider.getWalletClient(
47+
params.fromChain
48+
);
5149
const [fromAddress] = await walletClient.getAddresses();
5250

5351
const routes = await getRoutes({
54-
fromChainId: getChainConfigs(this.walletProvider.runtime)[
55-
params.fromChain
56-
].chainId as ChainId,
57-
toChainId: getChainConfigs(this.walletProvider.runtime)[
58-
params.toChain
59-
].chainId as ChainId,
52+
fromChainId: this.walletProvider.getChainConfigs(params.fromChain)
53+
.id,
54+
toChainId: this.walletProvider.getChainConfigs(params.toChain).id,
6055
fromTokenAddress: params.fromToken,
6156
toTokenAddress: params.toToken,
6257
fromAmount: params.amount,
@@ -79,9 +74,7 @@ export class BridgeAction {
7974
to: routes.routes[0].steps[0].estimate
8075
.approvalAddress as `0x${string}`,
8176
value: BigInt(params.amount),
82-
chainId: getChainConfigs(this.walletProvider.runtime)[
83-
params.fromChain
84-
].chainId,
77+
chainId: this.walletProvider.getChainConfigs(params.fromChain).id,
8578
};
8679
}
8780
}
@@ -95,7 +88,10 @@ export const bridgeAction = {
9588
state: State,
9689
options: any
9790
) => {
98-
const walletProvider = new WalletProvider(runtime);
91+
const privateKey = runtime.getSetting(
92+
"EVM_PRIVATE_KEY"
93+
) as `0x${string}`;
94+
const walletProvider = new WalletProvider(privateKey);
9995
const action = new BridgeAction(walletProvider);
10096
return action.bridge(options);
10197
},

packages/plugin-evm/src/actions/swap.ts

+16-21
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
getRoutes,
88
} from "@lifi/sdk";
99
import { WalletProvider } from "../providers/wallet";
10-
import { getChainConfigs } from "../providers/chainConfigs";
1110
import { swapTemplate } from "../templates";
1211
import type { SwapParams, Transaction } from "../types";
1312

@@ -19,16 +18,14 @@ export class SwapAction {
1918
constructor(private walletProvider: WalletProvider) {
2019
this.config = createConfig({
2120
integrator: "eliza",
22-
chains: Object.values(
23-
getChainConfigs(this.walletProvider.runtime)
24-
).map((config) => ({
25-
id: config.chainId,
21+
chains: Object.values(this.walletProvider.chains).map((config) => ({
22+
id: config.id,
2623
name: config.name,
2724
key: config.name.toLowerCase(),
2825
chainType: "EVM" as const,
2926
nativeToken: {
3027
...config.nativeCurrency,
31-
chainId: config.chainId,
28+
chainId: config.id,
3229
address: "0x0000000000000000000000000000000000000000",
3330
coinKey: config.nativeCurrency.symbol,
3431
priceUSD: "0",
@@ -38,15 +35,15 @@ export class SwapAction {
3835
name: config.nativeCurrency.name,
3936
},
4037
rpcUrls: {
41-
public: { http: [config.rpcUrl] },
38+
public: { http: [config.rpcUrls.default.http[0]] },
4239
},
43-
blockExplorerUrls: [config.blockExplorerUrl],
40+
blockExplorerUrls: [config.blockExplorers.default.url],
4441
metamask: {
45-
chainId: `0x${config.chainId.toString(16)}`,
42+
chainId: `0x${config.id.toString(16)}`,
4643
chainName: config.name,
4744
nativeCurrency: config.nativeCurrency,
48-
rpcUrls: [config.rpcUrl],
49-
blockExplorerUrls: [config.blockExplorerUrl],
45+
rpcUrls: [config.rpcUrls.default.http[0]],
46+
blockExplorerUrls: [config.blockExplorers.default.url],
5047
},
5148
coin: config.nativeCurrency.symbol,
5249
mainnet: true,
@@ -56,16 +53,12 @@ export class SwapAction {
5653
}
5754

5855
async swap(params: SwapParams): Promise<Transaction> {
59-
const walletClient = this.walletProvider.getWalletClient();
56+
const walletClient = this.walletProvider.getWalletClient(params.chain);
6057
const [fromAddress] = await walletClient.getAddresses();
6158

6259
const routes = await getRoutes({
63-
fromChainId: getChainConfigs(this.walletProvider.runtime)[
64-
params.chain
65-
].chainId as ChainId,
66-
toChainId: getChainConfigs(this.walletProvider.runtime)[
67-
params.chain
68-
].chainId as ChainId,
60+
fromChainId: this.walletProvider.getChainConfigs(params.chain).id,
61+
toChainId: this.walletProvider.getChainConfigs(params.chain).id,
6962
fromTokenAddress: params.fromToken,
7063
toTokenAddress: params.toToken,
7164
fromAmount: params.amount,
@@ -92,8 +85,7 @@ export class SwapAction {
9285
.approvalAddress as `0x${string}`,
9386
value: BigInt(params.amount),
9487
data: process.data as `0x${string}`,
95-
chainId: getChainConfigs(this.walletProvider.runtime)[params.chain]
96-
.chainId,
88+
chainId: this.walletProvider.getChainConfigs(params.chain).id,
9789
};
9890
}
9991
}
@@ -109,7 +101,10 @@ export const swapAction = {
109101
callback?: any
110102
) => {
111103
try {
112-
const walletProvider = new WalletProvider(runtime);
104+
const privateKey = runtime.getSetting(
105+
"EVM_PRIVATE_KEY"
106+
) as `0x${string}`;
107+
const walletProvider = new WalletProvider(privateKey);
113108
const action = new SwapAction(walletProvider);
114109
return await action.swap(options);
115110
} catch (error) {

packages/plugin-evm/src/actions/transfer.ts

+94-16
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
1-
import { ByteArray, parseEther, type Hex } from "viem";
2-
import { WalletProvider } from "../providers/wallet";
1+
import { ByteArray, formatEther, parseEther, type Hex } from "viem";
2+
import {
3+
composeContext,
4+
generateObjectDEPRECATED,
5+
HandlerCallback,
6+
ModelClass,
7+
type IAgentRuntime,
8+
type Memory,
9+
type State,
10+
} from "@ai16z/eliza";
11+
12+
import { initWalletProvider, WalletProvider } from "../providers/wallet";
313
import type { Transaction, TransferParams } from "../types";
414
import { transferTemplate } from "../templates";
5-
import type { IAgentRuntime, Memory, State } from "@ai16z/eliza";
615

716
export { transferTemplate };
817
export class TransferAction {
918
constructor(private walletProvider: WalletProvider) {}
1019

11-
async transfer(
12-
runtime: IAgentRuntime,
13-
params: TransferParams
14-
): Promise<Transaction> {
15-
const walletClient = this.walletProvider.getWalletClient();
16-
const [fromAddress] = await walletClient.getAddresses();
20+
async transfer(params: TransferParams): Promise<Transaction> {
21+
console.log(
22+
`Transferring: ${params.amount} tokens to (${params.toAddress} on ${params.fromChain})`
23+
);
1724

18-
await this.walletProvider.switchChain(runtime, params.fromChain);
25+
const walletClient = this.walletProvider.getWalletClient(
26+
params.fromChain
27+
);
1928

2029
try {
2130
const hash = await walletClient.sendTransaction({
22-
account: fromAddress,
31+
account: walletClient.account,
2332
to: params.toAddress,
2433
value: parseEther(params.amount),
2534
data: params.data as Hex,
@@ -39,7 +48,7 @@ export class TransferAction {
3948

4049
return {
4150
hash,
42-
from: fromAddress,
51+
from: walletClient.account.address,
4352
to: params.toAddress,
4453
value: parseEther(params.amount),
4554
data: params.data as Hex,
@@ -50,18 +59,87 @@ export class TransferAction {
5059
}
5160
}
5261

62+
const buildTransferDetails = async (
63+
state: State,
64+
runtime: IAgentRuntime,
65+
wp: WalletProvider
66+
): Promise<TransferParams> => {
67+
const context = composeContext({
68+
state,
69+
template: transferTemplate,
70+
});
71+
72+
const chains = Object.keys(wp.chains);
73+
74+
const contextWithChains = context.replace(
75+
"SUPPORTED_CHAINS",
76+
chains.toString()
77+
);
78+
79+
const transferDetails = (await generateObjectDEPRECATED({
80+
runtime,
81+
context: contextWithChains,
82+
modelClass: ModelClass.SMALL,
83+
})) as TransferParams;
84+
85+
const existingChain = wp.chains[transferDetails.fromChain];
86+
87+
if (!existingChain) {
88+
throw new Error(
89+
"The chain " +
90+
transferDetails.fromChain +
91+
" not configured yet. Add the chain or choose one from configured: " +
92+
chains.toString()
93+
);
94+
}
95+
96+
return transferDetails;
97+
};
98+
5399
export const transferAction = {
54100
name: "transfer",
55101
description: "Transfer tokens between addresses on the same chain",
56102
handler: async (
57103
runtime: IAgentRuntime,
58104
message: Memory,
59105
state: State,
60-
options: any
106+
options: any,
107+
callback?: HandlerCallback
61108
) => {
62-
const walletProvider = new WalletProvider(runtime);
63-
const action = new TransferAction(walletProvider);
64-
return action.transfer(runtime, options);
109+
try {
110+
const walletProvider = initWalletProvider(runtime);
111+
const action = new TransferAction(walletProvider);
112+
const transferDetails = await buildTransferDetails(
113+
state,
114+
runtime,
115+
walletProvider
116+
);
117+
const tx = await action.transfer(transferDetails);
118+
119+
if (callback) {
120+
callback({
121+
text: `Successfully transferred ${formatEther(tx.value)} tokens to ${tx.to}\nTransaction hash: ${tx.hash}\nChain: ${transferDetails.fromChain}`,
122+
content: {
123+
success: true,
124+
hash: tx.hash,
125+
amount: formatEther(tx.value),
126+
recipient: tx.to,
127+
chain: transferDetails.fromChain,
128+
},
129+
});
130+
}
131+
132+
return true;
133+
} catch (error) {
134+
console.error("Error during token transfer:", error);
135+
if (callback) {
136+
callback({
137+
text: `Error transferring tokens: ${error.message}`,
138+
content: { error: error.message },
139+
});
140+
}
141+
return false;
142+
}
65143
},
66144
template: transferTemplate,
67145
validate: async (runtime: IAgentRuntime) => {

0 commit comments

Comments
 (0)