Skip to content

Commit 43b7502

Browse files
authored
Add files via upload
1 parent da75124 commit 43b7502

14 files changed

Lines changed: 1424 additions & 1 deletion

File tree

README.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,93 @@
1-
# plugin-evm
1+
# `@elizaos/plugin-evm`
2+
3+
This plugin provides actions and providers for interacting with EVM-compatible chains.
4+
5+
---
6+
7+
## Configuration
8+
9+
### Default Setup
10+
11+
By default, **Ethereum mainnet** is enabled. To use it, simply add your private key to the `.env` file:
12+
13+
```env
14+
EVM_PRIVATE_KEY=your-private-key-here
15+
```
16+
17+
### Adding Support for Other Chains
18+
19+
To enable support for additional chains, add them to the character config like this:
20+
21+
```json
22+
"settings": {
23+
"chains": {
24+
"evm": [
25+
"base", "arbitrum", "iotex"
26+
]
27+
}
28+
}
29+
```
30+
31+
Note: The chain names must match those in the viem/chains.
32+
33+
### Custom RPC URLs
34+
35+
By default, the RPC URL is inferred from the `viem/chains` config. To use a custom RPC URL for a specific chain, add the following to your `.env` file:
36+
37+
```env
38+
ETHEREUM_PROVIDER_<CHAIN_NAME>=https://your-custom-rpc-url
39+
```
40+
41+
**Example usage:**
42+
43+
```env
44+
ETHEREUM_PROVIDER_IOTEX=https://iotex-network.rpc.thirdweb.com
45+
```
46+
47+
#### Custom RPC for Ethereum Mainnet
48+
49+
To set a custom RPC URL for Ethereum mainnet, use:
50+
51+
```env
52+
EVM_PROVIDER_URL=https://your-custom-mainnet-rpc-url
53+
```
54+
55+
## Provider
56+
57+
The **Wallet Provider** initializes with the **first chain in the list** as the default (or Ethereum mainnet if none are added). It:
58+
59+
- Provides the **context** of the currently connected address and its balance.
60+
- Creates **Public** and **Wallet clients** to interact with the supported chains.
61+
- Allows adding chains dynamically at runtime.
62+
63+
---
64+
65+
## Actions
66+
67+
### Transfer
68+
69+
Transfer tokens from one address to another on any EVM-compatible chain. Just specify the:
70+
71+
- **Amount**
72+
- **Chain**
73+
- **Recipient Address**
74+
75+
**Example usage:**
76+
77+
```bash
78+
Transfer 1 ETH to 0xRecipient on arbitrum.
79+
```
80+
81+
---
82+
83+
## Contribution
84+
85+
The plugin contains tests. Whether you're using **TDD** or not, please make sure to run the tests before submitting a PR.
86+
87+
### Running Tests
88+
89+
Navigate to the `plugin-evm` directory and run:
90+
91+
```bash
92+
pnpm test
93+
```

eslint.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import eslintGlobalConfig from "../../eslint.config.mjs";
2+
3+
export default [...eslintGlobalConfig];

package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@elizaos/plugin-evm",
3+
"version": "0.1.7-alpha.2",
4+
"main": "src/index.ts",
5+
"type": "module",
6+
"dependencies": {
7+
"@lifi/data-types": "5.15.5",
8+
"@lifi/sdk": "3.4.1",
9+
"@lifi/types": "16.3.0",
10+
"tsup": "8.3.5",
11+
"viem": "2.21.53"
12+
},
13+
"scripts": {
14+
"build": "tsup --format esm --dts",
15+
"dev": "tsup --format esm --dts --watch",
16+
"test": "vitest run",
17+
"lint": "eslint --fix --cache ."
18+
},
19+
"peerDependencies": {
20+
"whatwg-url": "7.1.0"
21+
}
22+
}

src/actions/bridge.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import type { IAgentRuntime, Memory, State } from "@elizaos/core";
2+
import {
3+
composeContext,
4+
generateObjectDeprecated,
5+
ModelClass,
6+
} from "@elizaos/core";
7+
import {
8+
createConfig,
9+
executeRoute,
10+
ExtendedChain,
11+
getRoutes,
12+
} from "@lifi/sdk";
13+
14+
import { initWalletProvider, WalletProvider } from "../providers/wallet";
15+
import { bridgeTemplate } from "../templates";
16+
import type { BridgeParams, Transaction } from "../types";
17+
import { parseEther } from "viem";
18+
19+
export { bridgeTemplate };
20+
21+
export class BridgeAction {
22+
private config;
23+
24+
constructor(private walletProvider: WalletProvider) {
25+
this.config = createConfig({
26+
integrator: "eliza",
27+
chains: Object.values(this.walletProvider.chains).map((config) => ({
28+
id: config.id,
29+
name: config.name,
30+
key: config.name.toLowerCase(),
31+
chainType: "EVM",
32+
nativeToken: {
33+
...config.nativeCurrency,
34+
chainId: config.id,
35+
address: "0x0000000000000000000000000000000000000000",
36+
coinKey: config.nativeCurrency.symbol,
37+
},
38+
metamask: {
39+
chainId: `0x${config.id.toString(16)}`,
40+
chainName: config.name,
41+
nativeCurrency: config.nativeCurrency,
42+
rpcUrls: [config.rpcUrls.default.http[0]],
43+
blockExplorerUrls: [config.blockExplorers.default.url],
44+
},
45+
diamondAddress: "0x0000000000000000000000000000000000000000",
46+
coin: config.nativeCurrency.symbol,
47+
mainnet: true,
48+
})) as ExtendedChain[],
49+
});
50+
}
51+
52+
async bridge(params: BridgeParams): Promise<Transaction> {
53+
const walletClient = this.walletProvider.getWalletClient(
54+
params.fromChain
55+
);
56+
const [fromAddress] = await walletClient.getAddresses();
57+
58+
const routes = await getRoutes({
59+
fromChainId: this.walletProvider.getChainConfigs(params.fromChain)
60+
.id,
61+
toChainId: this.walletProvider.getChainConfigs(params.toChain).id,
62+
fromTokenAddress: params.fromToken,
63+
toTokenAddress: params.toToken,
64+
fromAmount: parseEther(params.amount).toString(),
65+
fromAddress: fromAddress,
66+
toAddress: params.toAddress || fromAddress,
67+
});
68+
69+
if (!routes.routes.length) throw new Error("No routes found");
70+
71+
const execution = await executeRoute(routes.routes[0], this.config);
72+
const process = execution.steps[0]?.execution?.process[0];
73+
74+
if (!process?.status || process.status === "FAILED") {
75+
throw new Error("Transaction failed");
76+
}
77+
78+
return {
79+
hash: process.txHash as `0x${string}`,
80+
from: fromAddress,
81+
to: routes.routes[0].steps[0].estimate
82+
.approvalAddress as `0x${string}`,
83+
value: BigInt(params.amount),
84+
chainId: this.walletProvider.getChainConfigs(params.fromChain).id,
85+
};
86+
}
87+
}
88+
89+
export const bridgeAction = {
90+
name: "bridge",
91+
description: "Bridge tokens between different chains",
92+
handler: async (
93+
runtime: IAgentRuntime,
94+
_message: Memory,
95+
state: State,
96+
_options: any,
97+
callback?: any
98+
) => {
99+
console.log("Bridge action handler called");
100+
const walletProvider = initWalletProvider(runtime);
101+
const action = new BridgeAction(walletProvider);
102+
103+
// Compose bridge context
104+
const bridgeContext = composeContext({
105+
state,
106+
template: bridgeTemplate,
107+
});
108+
const content = await generateObjectDeprecated({
109+
runtime,
110+
context: bridgeContext,
111+
modelClass: ModelClass.LARGE,
112+
});
113+
114+
const bridgeOptions: BridgeParams = {
115+
fromChain: content.fromChain,
116+
toChain: content.toChain,
117+
fromToken: content.token,
118+
toToken: content.token,
119+
toAddress: content.toAddress,
120+
amount: content.amount,
121+
};
122+
123+
try {
124+
const bridgeResp = await action.bridge(bridgeOptions);
125+
if (callback) {
126+
callback({
127+
text: `Successfully bridge ${bridgeOptions.amount} ${bridgeOptions.fromToken} tokens from ${bridgeOptions.fromChain} to ${bridgeOptions.toChain}\nTransaction Hash: ${bridgeResp.hash}`,
128+
content: {
129+
success: true,
130+
hash: bridgeResp.hash,
131+
recipient: bridgeResp.to,
132+
chain: bridgeOptions.fromChain,
133+
},
134+
});
135+
}
136+
return true;
137+
} catch (error) {
138+
console.error("Error in bridge handler:", error.message);
139+
if (callback) {
140+
callback({ text: `Error: ${error.message}` });
141+
}
142+
return false;
143+
}
144+
},
145+
template: bridgeTemplate,
146+
validate: async (runtime: IAgentRuntime) => {
147+
const privateKey = runtime.getSetting("EVM_PRIVATE_KEY");
148+
return typeof privateKey === "string" && privateKey.startsWith("0x");
149+
},
150+
examples: [
151+
[
152+
{
153+
user: "user",
154+
content: {
155+
text: "Bridge 1 ETH from Ethereum to Base",
156+
action: "CROSS_CHAIN_TRANSFER",
157+
},
158+
},
159+
],
160+
],
161+
similes: ["CROSS_CHAIN_TRANSFER", "CHAIN_BRIDGE", "MOVE_CROSS_CHAIN"],
162+
}; // TODO: add more examples / similies

0 commit comments

Comments
 (0)