From c9adffdb30034649e105fcc670926c3747415c84 Mon Sep 17 00:00:00 2001 From: AIFlow_ML Date: Thu, 30 Jan 2025 13:08:20 +0700 Subject: [PATCH] Fixed all the typing errors, all the logical error, refactored the action --- .../src/actions/swapAction.ts | 102 ++++++++++-------- .../src/hooks/useGetAccount.ts | 7 +- .../src/providers/walletProvider.ts | 96 +++++++++++------ 3 files changed, 130 insertions(+), 75 deletions(-) diff --git a/packages/plugin-holdstation/src/actions/swapAction.ts b/packages/plugin-holdstation/src/actions/swapAction.ts index 129473a358..66496bff2f 100644 --- a/packages/plugin-holdstation/src/actions/swapAction.ts +++ b/packages/plugin-holdstation/src/actions/swapAction.ts @@ -1,30 +1,47 @@ import { - Action, - IAgentRuntime, - Memory, - HandlerCallback, - State, + type Action, + type IAgentRuntime, + type Memory, + type HandlerCallback, + type State, composeContext, ModelClass, elizaLogger, - ActionExample, + type ActionExample, generateObjectDeprecated, } from "@elizaos/core"; import { swapTemplate } from "../templates"; -import { SendTransactionParams, SwapParams } from "../types"; +import type { SendTransactionParams, SwapParams } from "../types"; import { initWalletProvider, - WalletProvider, + type WalletProvider, } from "../providers/walletProvider"; import { validateHoldStationConfig } from "../environment"; import { HOLDSTATION_ROUTER_ADDRESS, NATIVE_ADDRESS } from "../constants"; -import { parseUnits } from "viem"; +import { parseUnits, type Hex, type Address } from "viem"; + +// ------------------------------------------------------------------------------------------------ +// Interfaces +// ------------------------------------------------------------------------------------------------ +interface SwapResult { + hash: Hex; + inputTokenCA?: Address; + inputTokenSymbol?: string; + outputTokenCA?: Address; + outputTokenSymbol?: string; + amount: bigint; + slippage?: number; + text?: string; +} +// ------------------------------------------------------------------------------------------------ +// Core Action Class +// ------------------------------------------------------------------------------------------------ export class SwapAction { constructor(private walletProvider: WalletProvider) {} - async swap(params: SwapParams): Promise { + async swap(params: SwapParams): Promise { const { items: tokens } = await this.walletProvider.fetchPortfolio(); if (!params.inputTokenCA && !params.inputTokenSymbol) { @@ -36,7 +53,7 @@ export class SwapAction { ? t.address === params.inputTokenCA : t.symbol === params.inputTokenSymbol?.toUpperCase(); }); - if (filters.length != 1) { + if (filters.length !== 1) { throw new Error( "Multiple tokens or no tokens found with the symbol" ); @@ -61,7 +78,7 @@ export class SwapAction { ? t.address === params.outputTokenCA : t.symbol === params.outputTokenSymbol?.toUpperCase(); }); - if (filters.length != 1) { + if (filters.length !== 1) { throw new Error( "Multiple tokens or no tokens found with the symbol" ); @@ -127,8 +144,12 @@ export class SwapAction { } } +// ------------------------------------------------------------------------------------------------ +// Core Action Implementation +// ------------------------------------------------------------------------------------------------ export const swapAction: Action = { name: "TOKEN_SWAP_BY_HOLDSTATION", + description: "Perform swapping of tokens on ZKsync by HoldStation swap.", similes: [ "SWAP_TOKEN", "SWAP_TOKEN_BY_HOLDSTATION_SWAP", @@ -137,33 +158,31 @@ export const swapAction: Action = { "CONVERT_TOKENS", "CONVERT_TOKENS_BY_HOLDSTATION_SWAP", ], - validate: async (runtime: IAgentRuntime, _message: Memory) => { + validate: async (runtime: IAgentRuntime, _message: Memory): Promise => { await validateHoldStationConfig(runtime); return true; }, - description: "Perform swapping of tokens on ZKsync by HoldStation swap.", handler: async ( runtime: IAgentRuntime, message: Memory, - state: State, - _options: any, - callback: HandlerCallback - ) => { + state?: State, + _options?: Record, + callback?: HandlerCallback + ): Promise => { elizaLogger.log("Starting HoldStation Wallet TOKEN_SWAP handler..."); const walletProvider = await initWalletProvider(runtime); const action = new SwapAction(walletProvider); - // compose state - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); + // Initialize or update state + let currentState = state ?? await runtime.composeState(message) as State; + if (state) { + currentState = await runtime.updateRecentMessageState(currentState); } // compose swap context const swapContext = composeContext({ - state, + state: currentState, template: swapTemplate, }); @@ -186,28 +205,27 @@ export const swapAction: Action = { amount, } = await action.swap(content); - elizaLogger.success( - `Swap completed successfully from ${amount} ${inputTokenSymbol} (${inputTokenCA}) to ${outputTokenSymbol} (${outputTokenCA})!\nTransaction Hash: ${hash}` - ); + const successMessage = `Swap completed successfully from ${amount} ${inputTokenSymbol} (${inputTokenCA}) to ${outputTokenSymbol} (${outputTokenCA})!\nTransaction Hash: ${hash}`; + elizaLogger.success(successMessage); - if (callback) { - callback({ - text: `Swap completed successfully from ${amount} ${inputTokenSymbol} (${inputTokenCA}) to ${outputTokenSymbol} (${outputTokenCA})!\nTransaction Hash: ${hash}`, - content: { - success: true, - hash: hash, - }, - }); - } + callback?.({ + text: successMessage, + content: { + success: true, + hash: hash, + }, + }); + return true; } catch (error) { elizaLogger.error("Error during token swap:", error); - if (callback) { - callback({ - text: `Error during token swap: ${error.message}`, - content: { error: error.message }, - }); - } + const errorMessage = error instanceof Error ? error.message : "Unknown error"; + + callback?.({ + text: `Error during token swap: ${errorMessage}`, + content: { error: errorMessage }, + }); + return false; } }, diff --git a/packages/plugin-holdstation/src/hooks/useGetAccount.ts b/packages/plugin-holdstation/src/hooks/useGetAccount.ts index d00ec44be9..e39a6d756a 100644 --- a/packages/plugin-holdstation/src/hooks/useGetAccount.ts +++ b/packages/plugin-holdstation/src/hooks/useGetAccount.ts @@ -3,6 +3,9 @@ import type { PrivateKeyAccount } from "viem/accounts"; import { privateKeyToAccount } from "viem/accounts"; export const useGetAccount = (runtime: IAgentRuntime): PrivateKeyAccount => { - const PRIVATE_KEY = runtime.getSetting("HOLDSTATION_PRIVATE_KEY")!; - return privateKeyToAccount(`0x${PRIVATE_KEY}`); + const privateKey = runtime.getSetting("HOLDSTATION_PRIVATE_KEY"); + if (!privateKey) { + throw new Error("HOLDSTATION_PRIVATE_KEY not found in settings"); + } + return privateKeyToAccount(`0x${privateKey}`); }; diff --git a/packages/plugin-holdstation/src/providers/walletProvider.ts b/packages/plugin-holdstation/src/providers/walletProvider.ts index 1e0e397dc8..3f72b030a6 100644 --- a/packages/plugin-holdstation/src/providers/walletProvider.ts +++ b/packages/plugin-holdstation/src/providers/walletProvider.ts @@ -1,44 +1,67 @@ import { - Provider, - IAgentRuntime, - Memory, - State, + type Provider, + type IAgentRuntime, + type Memory, + type State, elizaLogger, } from "@elizaos/core"; import { - Address, + type Address, createPublicClient, erc20Abi, - PublicClient, + type PublicClient, http, - WalletClient, - HttpTransport, - Account, - Chain, - SendTransactionParameters, + type WalletClient, + type HttpTransport, + type Account, + type Chain, + type SendTransactionParameters, + type Hex, } from "viem"; import { zksync } from "viem/chains"; -import { PrivateKeyAccount } from "viem/accounts"; +import type { PrivateKeyAccount } from "viem/accounts"; import { useGetAccount, useGetWalletClient } from "../hooks"; -import { Item, SendTransactionParams, WalletPortfolio } from "../types"; +import type { Item, SendTransactionParams, WalletPortfolio } from "../types"; import NodeCache from "node-cache"; +// Add interface for portfolio API response +interface PortfolioItem { + contract_name: string; + contract_address: string; + contract_ticker_symbol: string; + contract_decimals: number; +} + +// Add interface for token API response +interface TokenApiItem { + name: string; + address: string; + symbol: string; + decimals: number; +} + +// Update interface to match the actual return type +interface HoldstationWalletResponse { + message: string; + error?: string; +} + export class WalletProvider { private cache: NodeCache; account: PrivateKeyAccount; walletClient: WalletClient; - publicClient: PublicClient; + publicClient: PublicClient; constructor(account: PrivateKeyAccount) { this.account = account; this.walletClient = useGetWalletClient(); - this.publicClient = createPublicClient({ + this.publicClient = createPublicClient({ chain: zksync, transport: http(), - }) as PublicClient; + }) as PublicClient; this.cache = new NodeCache({ stdTTL: 300 }); } @@ -54,7 +77,7 @@ export class WalletProvider { tokenAddress: Address, owner: Address, spender: Address - ): Promise { + ): Promise { return this.publicClient.readContract({ address: tokenAddress, abi: erc20Abi, @@ -68,7 +91,7 @@ export class WalletProvider { tokenAddress: Address, amount: bigint ) { - const result = await this.walletClient.writeContract({ + await this.walletClient.writeContract({ account: this.account, address: tokenAddress, abi: erc20Abi, @@ -78,12 +101,14 @@ export class WalletProvider { }); } - async sendTransaction(req: SendTransactionParams): Promise { + async sendTransaction(req: SendTransactionParams): Promise { const txRequest: SendTransactionParameters = { ...req, account: this.account, chain: zksync, kzg: undefined, + data: req.data ? (req.data as `0x${string}`) : undefined, + to: req.to ? (req.to as `0x${string}`) : undefined, }; const tx = await this.walletClient.sendTransaction(txRequest); console.log("sendTransaction txhash:", tx); @@ -115,9 +140,11 @@ export class WalletProvider { const items: Array = portfolioData.data.map( - (item: any): Item => ({ + (item: PortfolioItem): Item => ({ name: item.contract_name, - address: item.contract_address, + address: item.contract_address.startsWith('0x') + ? item.contract_address as `0x${string}` + : `0x${item.contract_address}` as `0x${string}`, symbol: item.contract_ticker_symbol, decimals: item.contract_decimals, }) @@ -134,7 +161,7 @@ export class WalletProvider { async fetchAllTokens(): Promise> { try { - const cacheKey = `all-hswallet-tokens`; + const cacheKey = 'all-hswallet-tokens'; const cachedValue = this.cache.get>(cacheKey); if (cachedValue) { elizaLogger.log("Cache hit for fetch all"); @@ -142,7 +169,7 @@ export class WalletProvider { } elizaLogger.log("Cache miss for fetch all"); - const fetchUrl = `https://tokens.coingecko.com/zksync/all.json`; + const fetchUrl = 'https://tokens.coingecko.com/zksync/all.json'; const tokensResp = await fetch(fetchUrl); const tokensData = await tokensResp.json(); @@ -157,9 +184,11 @@ export class WalletProvider { const tokens: Array = tokensData.tokens.map( - (item: any): Item => ({ + (item: TokenApiItem): Item => ({ name: item.name, - address: item.address, + address: item.address.startsWith('0x') + ? item.address as `0x${string}` + : `0x${item.address}` as `0x${string}`, symbol: item.symbol, decimals: item.decimals, }) @@ -183,16 +212,21 @@ export const initWalletProvider = async (runtime: IAgentRuntime) => { export const holdstationWalletProvider: Provider = { get: async ( runtime: IAgentRuntime, - message: Memory, - state: State - ): Promise => { + _message: Memory, + state?: State + ): Promise => { try { const walletProvider = await initWalletProvider(runtime); - const agentName = state.agentName || "The agent"; - return `${agentName}'s HoldStation Wallet address: ${walletProvider.getAddress()}`; + const agentName = state?.agentName || "The agent"; + return { + message: `${agentName}'s HoldStation Wallet address: ${walletProvider.getAddress()}` + }; } catch (error) { console.error("Error in HoldStation Wallet provider:", error); - return null; + return { + message: "Failed to get wallet address", + error: error instanceof Error ? error.message : "Unknown error" + }; } }, };