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

fix: plugin-sei #2877

Merged
merged 4 commits into from
Jan 28, 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
73 changes: 54 additions & 19 deletions packages/plugin-sei/src/actions/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ByteArray, formatEther, parseEther, type Hex } from "viem";
import {
elizaLogger,
Action,
composeContext,
generateObjectDeprecated,
Expand Down Expand Up @@ -63,59 +64,82 @@ export class TransferAction {

async transfer(params: TransferParams): Promise<Transaction> {
const chain = this.walletProvider.getCurrentChain()
console.log(
elizaLogger.log(
`Transferring: ${params.amount} tokens to (${params.toAddress} on ${chain.name})`
);
// let recipientAddress
let recipientAddress: `0x${string}`; // Ensure it's a valid Ethereum address

let recipientAddress
if (params.toAddress.startsWith("sei")) {
const publicClient = this.walletProvider.getEvmPublicClient();
const evmAddress = await publicClient.readContract({
address: ADDRESS_PRECOMPILE_ADDRESS,
abi: ADDRESS_PRECOMPILE_ABI,
functionName: 'getEvmAddr',
args: [params.toAddress],
})
});

if (!evmAddress || !evmAddress.startsWith("0x")) {
throw new Error(`ERROR: Recipient does not have valid EVM address. Got: ${evmAddress}`);
}

console.log(`Translated address ${params.toAddress} to EVM address ${evmAddress}`)
recipientAddress = evmAddress
elizaLogger.log(`Translated address ${params.toAddress} to EVM address ${evmAddress}`);
recipientAddress = evmAddress as `0x${string}`; // Ensure it's a valid Ethereum address
} else {
recipientAddress = params.toAddress
if (!params.toAddress.startsWith("0x")) {
throw new Error(`ERROR: Recipient address must start with '0x'. Got: ${params.toAddress}`);
}
recipientAddress = params.toAddress as `0x${string}`; // Ensure it's a valid Ethereum address
}

const walletClient = this.walletProvider.getEvmWalletClient();
if (!walletClient.account) {
throw new Error("Wallet client account is undefined");
}

try {
const hash = await walletClient.sendTransaction({
account: walletClient.account,
to: recipientAddress,
value: parseEther(params.amount),
data: params.data as Hex,

kzg: {
blobToKzgCommitment: function (_: ByteArray): ByteArray {
blobToKzgCommitment: (_: ByteArray): ByteArray => {
throw new Error("Function not implemented.");
},
computeBlobKzgProof: function (
computeBlobKzgProof: (
_blob: ByteArray,
_commitment: ByteArray
): ByteArray {
): ByteArray => {
throw new Error("Function not implemented.");
},
},
maxFeePerBlobGas: BigInt(0), // Add required property
blobs: [], // Add required property
chain: undefined,
});
// kzg: {
// blobToKzgCommitment: function (_: ByteArray): ByteArray {
// throw new Error("Function not implemented.");
// },
// computeBlobKzgProof: function (
// _blob: ByteArray,
// _commitment: ByteArray
// ): ByteArray {
// throw new Error("Function not implemented.");
// },
// },


return {
hash,
from: walletClient.account.address,
from: walletClient.account.address, // Now guaranteed to be defined
to: params.toAddress,
value: parseEther(params.amount),
data: params.data as Hex,
};

} catch (error) {
throw new Error(`Transfer failed: ${error.message}`);
}
Expand All @@ -125,7 +149,7 @@ export class TransferAction {
const buildTransferDetails = async (
state: State,
runtime: IAgentRuntime,
wp: WalletProvider
_wp: WalletProvider
): Promise<TransferParams> => {
const context = composeContext({
state,
Expand All @@ -148,25 +172,36 @@ export const transferAction: Action = {
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: any,
_options: Record<string, unknown>, // Replace `any` with a safer type
callback?: HandlerCallback
) => {
if (!state) {
state = (await runtime.composeState(message)) as State;

// Create a new variable to avoid reassigning the parameter
let updatedState = state;

if (!updatedState) {
updatedState = (await runtime.composeState(message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
updatedState = await runtime.updateRecentMessageState(updatedState);
}

console.log("Transfer action handler called");
elizaLogger.debug("Transfer action handler called");
const walletProvider = await initWalletProvider(runtime);
const action = new TransferAction(walletProvider);

// Compose transfer context
const paramOptions = await buildTransferDetails(
state,
updatedState, // Use the new variable
runtime,
walletProvider
);

// // Compose transfer context
// const paramOptions = await buildTransferDetails(
// state,
// runtime,
// walletProvider
// );

try {
const transferResp = await action.transfer(paramOptions);
Expand All @@ -184,7 +219,7 @@ export const transferAction: Action = {
}
return true;
} catch (error) {
console.error("Error during token transfer:", error);
elizaLogger.error("Error during token transfer:", error);
if (callback) {
callback({
text: `Error transferring tokens: ${error.message}`,
Expand Down Expand Up @@ -217,4 +252,4 @@ export const transferAction: Action = {
],
],
similes: ["SEND_TOKENS", "TOKEN_TRANSFER", "MOVE_TOKENS", "SEND_SEI"],
};
};
2 changes: 1 addition & 1 deletion packages/plugin-sei/src/environment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IAgentRuntime } from "@elizaos/core";
import type { IAgentRuntime } from "@elizaos/core";
import { z } from "zod";

export const seiEnvSchema = z.object({
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-sei/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Plugin } from "@elizaos/core";
import type { Plugin } from "@elizaos/core";
import { evmWalletProvider } from "./providers/wallet.ts";

import { transferAction } from "./actions/transfer";
Expand Down
61 changes: 45 additions & 16 deletions packages/plugin-sei/src/providers/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type {
} from "viem";
import * as viemChains from "viem/chains";
import NodeCache from "node-cache";
import * as path from "path";
import * as path from "node:path";

import type { ChainWithName } from "../types";

Expand All @@ -37,7 +37,8 @@ export const seiChains = {

export class WalletProvider {
private cache: NodeCache;
private cacheKey: string = "evm/wallet";
// private cacheKey: string = "evm/wallet";
private cacheKey = "evm/wallet"; // Remove explicit type annotation
private currentChain: ChainWithName;
private CACHE_EXPIRY_SEC = 5;
account: PrivateKeyAccount;
Expand Down Expand Up @@ -97,12 +98,11 @@ export class WalletProvider {
}

async getWalletBalance(): Promise<string | null> {
const cacheKey = "seiWalletBalance_" + this.currentChain.name;
const cacheKey = `seiWalletBalance_${this.currentChain.name}`; // Fix: Use template literal
const cachedData = await this.getCachedData<string>(cacheKey);
if (cachedData) {
elizaLogger.log(
"Returning cached wallet balance for sei chain: " +
this.currentChain.name
`Returning cached wallet balance for sei chain: ${this.currentChain.name}` // Fix: Use template literal
);
return cachedData;
}
Expand All @@ -129,8 +129,15 @@ export class WalletProvider {
const cached = await this.cacheManager.get<T>(
path.join(this.cacheKey, key)
);
return cached;
return cached ?? null; // Fix: Return null if cached is undefined
}

// private async readFromCache<T>(key: string): Promise<T | null> {
// const cached = await this.cacheManager.get<T>(
// path.join(this.cacheKey, key)
// );
// return cached;
// }

private async writeToCache<T>(key: string, data: T): Promise<void> {
await this.cacheManager.set(path.join(this.cacheKey, key), data, {
Expand Down Expand Up @@ -212,28 +219,50 @@ export class WalletProvider {
return seiChain;
}
}

const genChainFromRuntime = (
runtime: IAgentRuntime
): ChainWithName => {
const sei_network = runtime.getSetting("SEI_NETWORK");
const validChains = Object.keys(seiChains)
if (typeof sei_network !== "string") { // Fix: Ensure sei_network is a string
throw new Error("SEI_NETWORK must be a string");
}

const validChains = Object.keys(seiChains);
if (!validChains.includes(sei_network)) {
throw new Error("Invalid SEI_NETWORK " + sei_network + " Must be one of " + validChains.join(", "));
throw new Error(`Invalid SEI_NETWORK ${sei_network}. Must be one of ${validChains.join(", ")}`);
}

let chain = seiChains[sei_network]
let chain = seiChains[sei_network];
const rpcurl = runtime.getSetting("SEI_RPC_URL");
if (rpcurl) {
chain = WalletProvider.genSeiChainFromName(
sei_network,
rpcurl
);
if (typeof rpcurl === "string") { // Fix: Ensure rpcurl is a string
chain = WalletProvider.genSeiChainFromName(sei_network, rpcurl);
}

return {name: sei_network, chain: chain};
return { name: sei_network, chain: chain }; // Fix: Ensure name is always a string
};

// const genChainFromRuntime = (
// runtime: IAgentRuntime
// ): ChainWithName => {
// const sei_network = runtime.getSetting("SEI_NETWORK");
// const validChains = Object.keys(seiChains)
// if (!validChains.includes(sei_network)) {
// // throw new Error("Invalid SEI_NETWORK " + sei_network + " Must be one of " + validChains.join(", "));
// throw new Error(`Invalid SEI_NETWORK ${sei_network}. Must be one of ${validChains.join(", ")}`);
// }

// let chain = seiChains[sei_network]
// const rpcurl = runtime.getSetting("SEI_RPC_URL");
// if (rpcurl) {
// chain = WalletProvider.genSeiChainFromName(
// sei_network,
// rpcurl
// );
// }

// return {name: sei_network, chain: chain};
// };

export const initWalletProvider = async (runtime: IAgentRuntime) => {

const chainData = genChainFromRuntime(runtime)
Expand Down
Loading