From 17d06f1a7378f7aae2eed2a6b65d1fa3c1fff448 Mon Sep 17 00:00:00 2001 From: threewebcode Date: Wed, 8 Jan 2025 09:49:43 +0800 Subject: [PATCH 01/16] feat: b2-network plugin --- packages/plugin-b2/.npmignore | 6 + packages/plugin-b2/README.md | 39 ++++++ packages/plugin-b2/eslint.config.mjs | 3 + packages/plugin-b2/package.json | 19 +++ .../plugin-b2/src/actions/sampleAction.ts | 111 ++++++++++++++++++ .../src/evaluators/sampleEvalutor.ts | 53 +++++++++ packages/plugin-b2/src/index.ts | 5 + .../plugin-b2/src/plugins/samplePlugin.ts | 15 +++ .../plugin-b2/src/providers/sampleProvider.ts | 14 +++ packages/plugin-b2/src/templates.ts | 60 ++++++++++ packages/plugin-b2/src/types.ts | 55 +++++++++ packages/plugin-b2/tsconfig.json | 9 ++ packages/plugin-b2/tsup.config.ts | 21 ++++ 13 files changed, 410 insertions(+) create mode 100644 packages/plugin-b2/.npmignore create mode 100644 packages/plugin-b2/README.md create mode 100644 packages/plugin-b2/eslint.config.mjs create mode 100644 packages/plugin-b2/package.json create mode 100644 packages/plugin-b2/src/actions/sampleAction.ts create mode 100644 packages/plugin-b2/src/evaluators/sampleEvalutor.ts create mode 100644 packages/plugin-b2/src/index.ts create mode 100644 packages/plugin-b2/src/plugins/samplePlugin.ts create mode 100644 packages/plugin-b2/src/providers/sampleProvider.ts create mode 100644 packages/plugin-b2/src/templates.ts create mode 100644 packages/plugin-b2/src/types.ts create mode 100644 packages/plugin-b2/tsconfig.json create mode 100644 packages/plugin-b2/tsup.config.ts diff --git a/packages/plugin-b2/.npmignore b/packages/plugin-b2/.npmignore new file mode 100644 index 00000000000..078562eceab --- /dev/null +++ b/packages/plugin-b2/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-b2/README.md b/packages/plugin-b2/README.md new file mode 100644 index 00000000000..3d396d51a84 --- /dev/null +++ b/packages/plugin-b2/README.md @@ -0,0 +1,39 @@ +# Sample Plugin for Eliza + +The Sample Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. + +## Description + +The Sample Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. + +## Actions + +- **createResourceAction**: This action enables the creation and management of generic resources. It can be customized to handle different types of resources and integrate with various data sources. + +## Providers + +- **sampleProvider**: This provider offers a mechanism to supply data or services to the plugin. It can be extended to include additional providers as needed. + +## Evaluators + +- **sampleEvaluator**: This evaluator provides a way to assess or analyze data within the plugin. It can be extended to include additional evaluators as needed. + +## Services + +- **[ServiceName]**: Description of the service and its functionality. This can be extended to include additional services as needed. + +## Clients + +- **[ClientName]**: Description of the client and its functionality. This can be extended to include additional clients as needed. + +## How to Extend + +To extend the Sample Plugin, you can add new actions, providers, evaluators, services, and clients by following the structure provided in the plugin. Each component can be customized to fit your specific requirements. + +1. **Actions**: Add new actions by defining them in the `actions` array. +2. **Providers**: Add new providers by defining them in the `providers` array. +3. **Evaluators**: Add new evaluators by defining them in the `evaluators` array. +4. **Services**: Add new services by defining them in the `services` array. +5. **Clients**: Add new clients by defining them in the `clients` array. + +For more detailed information on how to extend the plugin, refer to the documentation provided in the Eliza platform. diff --git a/packages/plugin-b2/eslint.config.mjs b/packages/plugin-b2/eslint.config.mjs new file mode 100644 index 00000000000..92fe5bbebef --- /dev/null +++ b/packages/plugin-b2/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-b2/package.json b/packages/plugin-b2/package.json new file mode 100644 index 00000000000..b31f26179c5 --- /dev/null +++ b/packages/plugin-b2/package.json @@ -0,0 +1,19 @@ +{ + "name": "@elizaos/plugin-sample", + "version": "0.1.5-alpha.5", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*" + }, + "devDependencies": { + "tsup": "8.3.5", + "@types/node": "^20.0.0" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache ." + } +} diff --git a/packages/plugin-b2/src/actions/sampleAction.ts b/packages/plugin-b2/src/actions/sampleAction.ts new file mode 100644 index 00000000000..7f835dac0a5 --- /dev/null +++ b/packages/plugin-b2/src/actions/sampleAction.ts @@ -0,0 +1,111 @@ +import { + Action, + IAgentRuntime, + Memory, + HandlerCallback, + State, + composeContext, + generateObject, + ModelClass, + elizaLogger, +} from "@elizaos/core"; + +import { CreateResourceSchema, isCreateResourceContent } from "../types"; + +import { createResourceTemplate } from "../templates"; + +export const createResourceAction: Action = { + name: "CREATE_RESOURCE", + description: "Create a new resource with the specified details", + validate: async (runtime: IAgentRuntime, _message: Memory) => { + return !!runtime.character.settings.secrets?.API_KEY; + }, + handler: async ( + runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: any, + callback: HandlerCallback + ) => { + try { + const context = composeContext({ + state, + template: createResourceTemplate, + }); + + const resourceDetails = await generateObject({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: CreateResourceSchema, + }); + + if (!isCreateResourceContent(resourceDetails.object)) { + callback({ text: "Invalid resource details provided." }, []); + return; + } + + // persist relevant data if needed to memory/knowledge + // const memory = { + // type: "resource", + // content: resourceDetails.object, + // timestamp: new Date().toISOString() + // }; + + // await runtime.storeMemory(memory); + + callback( + { + text: `Resource created successfully: +- Name: ${resourceDetails.object.name} +- Type: ${resourceDetails.object.type} +- Description: ${resourceDetails.object.description} +- Tags: ${resourceDetails.object.tags.join(", ")} + +Resource has been stored in memory.`, + }, + [] + ); + } catch (error) { + elizaLogger.error("Error creating resource:", error); + callback( + { text: "Failed to create resource. Please check the logs." }, + [] + ); + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Create a new resource with the name 'Resource1' and type 'TypeA'", + }, + }, + { + user: "{{agentName}}", + content: { + text: `Resource created successfully: +- Name: Resource1 +- Type: TypeA`, + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Create a new resource with the name 'Resource2' and type 'TypeB'", + }, + }, + { + user: "{{agentName}}", + content: { + text: `Resource created successfully: +- Name: Resource2 +- Type: TypeB`, + }, + }, + ], + ], +}; diff --git a/packages/plugin-b2/src/evaluators/sampleEvalutor.ts b/packages/plugin-b2/src/evaluators/sampleEvalutor.ts new file mode 100644 index 00000000000..c6d48b07153 --- /dev/null +++ b/packages/plugin-b2/src/evaluators/sampleEvalutor.ts @@ -0,0 +1,53 @@ +import { + Evaluator, + IAgentRuntime, + Memory, + State, + elizaLogger, +} from "@elizaos/core"; + +export const sampleEvaluator: Evaluator = { + alwaysRun: false, + description: "Sample evaluator for checking important content in memory", + similes: ["content checker", "memory evaluator"], + examples: [ + { + context: "Checking if memory contains important content", + messages: [ + { + action: "evaluate", + input: "This is an important message", + output: { + score: 1, + reason: "Memory contains important content.", + }, + }, + ], + outcome: "Memory should be evaluated as important", + }, + ], + handler: async (runtime: IAgentRuntime, memory: Memory, state: State) => { + // Evaluation logic for the evaluator + elizaLogger.log("Evaluating data in sampleEvaluator..."); + + // Example evaluation logic + if (memory.content && memory.content.includes("important")) { + elizaLogger.log("Important content found in memory."); + return { + score: 1, + reason: "Memory contains important content.", + }; + } else { + elizaLogger.log("No important content found in memory."); + return { + score: 0, + reason: "Memory does not contain important content.", + }; + } + }, + name: "sampleEvaluator", + validate: async (runtime: IAgentRuntime, memory: Memory, state: State) => { + // Validation logic for the evaluator + return true; + }, +}; diff --git a/packages/plugin-b2/src/index.ts b/packages/plugin-b2/src/index.ts new file mode 100644 index 00000000000..b16dd061427 --- /dev/null +++ b/packages/plugin-b2/src/index.ts @@ -0,0 +1,5 @@ +import { samplePlugin } from "./plugins/samplePlugin"; + +export * from "./plugins/samplePlugin"; + +export default samplePlugin; diff --git a/packages/plugin-b2/src/plugins/samplePlugin.ts b/packages/plugin-b2/src/plugins/samplePlugin.ts new file mode 100644 index 00000000000..90fd2898a16 --- /dev/null +++ b/packages/plugin-b2/src/plugins/samplePlugin.ts @@ -0,0 +1,15 @@ +import { Plugin } from "@elizaos/core"; +import { createResourceAction } from "../actions/sampleAction"; +import { sampleProvider } from "../providers/sampleProvider"; +import { sampleEvaluator } from "../evaluators/sampleEvalutor"; + +export const samplePlugin: Plugin = { + name: "sample", + description: "Enables creation and management of generic resources", + actions: [createResourceAction], + providers: [sampleProvider], + evaluators: [sampleEvaluator], + // separate examples will be added for services and clients + services: [], + clients: [], +}; diff --git a/packages/plugin-b2/src/providers/sampleProvider.ts b/packages/plugin-b2/src/providers/sampleProvider.ts new file mode 100644 index 00000000000..d16f3ba6ddf --- /dev/null +++ b/packages/plugin-b2/src/providers/sampleProvider.ts @@ -0,0 +1,14 @@ +import { + Provider, + IAgentRuntime, + Memory, + State, + elizaLogger, +} from "@elizaos/core"; + +export const sampleProvider: Provider = { + get: async (runtime: IAgentRuntime, message: Memory, state: State) => { + // Data retrieval logic for the provider + elizaLogger.log("Retrieving data in sampleProvider..."); + }, +}; diff --git a/packages/plugin-b2/src/templates.ts b/packages/plugin-b2/src/templates.ts new file mode 100644 index 00000000000..f9c0d965917 --- /dev/null +++ b/packages/plugin-b2/src/templates.ts @@ -0,0 +1,60 @@ +export const createResourceTemplate = ` +Extract the following details to create a new resource: +- **name** (string): Name of the resource +- **type** (string): Type of resource (document, image, video) +- **description** (string): Description of the resource +- **tags** (array): Array of tags to categorize the resource + +Provide the values in the following JSON format: + +\`\`\`json +{ + "name": "", + "type": "", + "description": "", + "tags": ["", ""] +} +\`\`\` + +Here are the recent user messages for context: +{{recentMessages}} +`; + +export const readResourceTemplate = ` +Extract the following details to read a resource: +- **id** (string): Unique identifier of the resource +- **fields** (array): Specific fields to retrieve (optional) + +Provide the values in the following JSON format: + +\`\`\`json +{ + "id": "", + "fields": ["", ""] +} +\`\`\` + +Here are the recent user messages for context: +{{recentMessages}} +`; + +export const updateResourceTemplate = ` +Extract the following details to update a resource: +- **id** (string): Unique identifier of the resource +- **updates** (object): Key-value pairs of fields to update + +Provide the values in the following JSON format: + +\`\`\`json +{ + "id": "", + "updates": { + "": "", + "": "" + } +} +\`\`\` + +Here are the recent user messages for context: +{{recentMessages}} +`; diff --git a/packages/plugin-b2/src/types.ts b/packages/plugin-b2/src/types.ts new file mode 100644 index 00000000000..ef7d4939631 --- /dev/null +++ b/packages/plugin-b2/src/types.ts @@ -0,0 +1,55 @@ +import { z } from "zod"; + +// Base resource schema +export const ResourceSchema = z.object({ + id: z.string().optional(), + name: z.string().min(1), + type: z.enum(["document", "image", "video"]), + description: z.string(), + tags: z.array(z.string()), +}); + +// Create resource schema +export const CreateResourceSchema = ResourceSchema.omit({ id: true }); + +// Read resource schema +export const ReadResourceSchema = z.object({ + id: z.string(), + fields: z.array(z.string()).optional(), +}); + +// Update resource schema +export const UpdateResourceSchema = z.object({ + id: z.string(), + updates: z.record(z.string(), z.any()), +}); + +// Type definitions +export type Resource = z.infer; +export type CreateResourceContent = z.infer; +export type ReadResourceContent = z.infer; +export type UpdateResourceContent = z.infer; + +// Type guards +export const isCreateResourceContent = ( + obj: any +): obj is CreateResourceContent => { + return CreateResourceSchema.safeParse(obj).success; +}; + +export const isReadResourceContent = (obj: any): obj is ReadResourceContent => { + return ReadResourceSchema.safeParse(obj).success; +}; + +export const isUpdateResourceContent = ( + obj: any +): obj is UpdateResourceContent => { + return UpdateResourceSchema.safeParse(obj).success; +}; + +// Plugin configuration type +export interface ExamplePluginConfig { + apiKey: string; + apiSecret: string; + endpoint?: string; +} diff --git a/packages/plugin-b2/tsconfig.json b/packages/plugin-b2/tsconfig.json new file mode 100644 index 00000000000..8c77b755f7b --- /dev/null +++ b/packages/plugin-b2/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/plugin-b2/tsup.config.ts b/packages/plugin-b2/tsup.config.ts new file mode 100644 index 00000000000..1a96f24afa1 --- /dev/null +++ b/packages/plugin-b2/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "safe-buffer", + // Add other modules you want to externalize + ], +}); From 92a6fa548496c18629b43de6a5dfbd540cfbcc27 Mon Sep 17 00:00:00 2001 From: threewebcode Date: Wed, 8 Jan 2025 10:20:55 +0800 Subject: [PATCH 02/16] docs:update readme and package description --- packages/plugin-b2/README.md | 8 ++++---- packages/plugin-b2/package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/plugin-b2/README.md b/packages/plugin-b2/README.md index 3d396d51a84..8ff0b3e41fd 100644 --- a/packages/plugin-b2/README.md +++ b/packages/plugin-b2/README.md @@ -1,10 +1,10 @@ -# Sample Plugin for Eliza +# B2 Network Plugin for Eliza -The Sample Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. +The B2 Network Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. ## Description -The Sample Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. +The B2 Network Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. ## Actions @@ -28,7 +28,7 @@ The Sample Plugin offers a set of features that can be integrated into the Eliza ## How to Extend -To extend the Sample Plugin, you can add new actions, providers, evaluators, services, and clients by following the structure provided in the plugin. Each component can be customized to fit your specific requirements. +To extend the B2 Network Plugin, you can add new actions, providers, evaluators, services, and clients by following the structure provided in the plugin. Each component can be customized to fit your specific requirements. 1. **Actions**: Add new actions by defining them in the `actions` array. 2. **Providers**: Add new providers by defining them in the `providers` array. diff --git a/packages/plugin-b2/package.json b/packages/plugin-b2/package.json index b31f26179c5..84727b1ac1d 100644 --- a/packages/plugin-b2/package.json +++ b/packages/plugin-b2/package.json @@ -1,6 +1,6 @@ { - "name": "@elizaos/plugin-sample", - "version": "0.1.5-alpha.5", + "name": "@elizaos/plugin-b2", + "version": "0.1.0", "main": "dist/index.js", "type": "module", "types": "dist/index.d.ts", From b72151cd95a7f17e528fc82a0a765d60104e5246 Mon Sep 17 00:00:00 2001 From: threewebcode Date: Wed, 8 Jan 2025 14:30:08 +0800 Subject: [PATCH 03/16] fix: solve build error --- packages/plugin-b2/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-b2/tsconfig.json b/packages/plugin-b2/tsconfig.json index 8c77b755f7b..e9c2e9f8527 100644 --- a/packages/plugin-b2/tsconfig.json +++ b/packages/plugin-b2/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../core/tsconfig.json", + "extends": "../core/tsconfig.json", "compilerOptions": { "outDir": "dist", "rootDir": "src", From cdb5b01fa5c9f4125f5804481e782d44fa1e590c Mon Sep 17 00:00:00 2001 From: goalongway Date: Wed, 8 Jan 2025 15:19:59 +0800 Subject: [PATCH 04/16] modify --- packages/plugin-b2/README.md | 254 ++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 20 deletions(-) diff --git a/packages/plugin-b2/README.md b/packages/plugin-b2/README.md index 3d396d51a84..732f3317da7 100644 --- a/packages/plugin-b2/README.md +++ b/packages/plugin-b2/README.md @@ -1,39 +1,253 @@ -# Sample Plugin for Eliza +# @elizaos/plugin-b2 -The Sample Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. +A plugin for interacting with the B2-Network within the ElizaOS ecosystem. ## Description -The Sample Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. +The Avalanche plugin enables comprehensive DeFi operations on the Avalanche network, including token transfers, YAK swaps, yield strategy management, and token creation via Token Mill. -## Actions +## Installation -- **createResourceAction**: This action enables the creation and management of generic resources. It can be customized to handle different types of resources and integrate with various data sources. +```bash +pnpm install @elizaos/plugin-avalanche +``` + +## Configuration + +The plugin requires the following environment variable: + +```typescript +AVALANCHE_PRIVATE_KEY= +``` + +## Features + +### 1. Token Transfers + +- Send native AVAX and ERC20 tokens +- Support for multiple token standards +- Built-in address validation + +### 2. YAK Swaps + +- Decentralized token swaps +- Automatic best path finding +- Slippage protection (default: 0.2%) +- Support for all major tokens + +### 3. Yield Strategies + +- Deposit tokens into yield-generating strategies +- Support for multiple strategies including: + - YAK staking + - USDC Benqi + - gmYAK Token Mill + - PRINCESS staking + - JOE staking + +### 4. Token Mill + +- Create new tokens +- Configure custom tokenomics +- Automatic market creation + +## Supported Tokens + +```typescript +const TOKENS = { + AVAX: "0x0000000000000000000000000000000000000000", + WAVAX: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + YAK: "0x59414b3089ce2AF0010e7523Dea7E2b35d776ec7", + gmYAK: "0x3A30784c1af928CdFce678eE49370220aA716DC3", + USDC: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + // ... and more +}; +``` + +## Usage Examples + +### Token Transfer + +```typescript +// Send AVAX +"Send 10 AVAX to 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; + +// Send ERC20 +"Transfer 100 USDC to [address]"; +``` + +### YAK Swap + +```typescript +// Swap tokens +"Swap 1 AVAX for USDC"; +"Swap 10 USDC for gmYAK"; +``` + +### Yield Strategy + +```typescript +// Deposit into strategies +"Deposit 1 USDC into the strategy"; +"Deposit 10 gmYAK to earn yield"; +``` + +### Token Creation + +```typescript +// Create new token +"Create a new memecoin called 'Test Token' with the symbol 'TEST'"; +``` ## Providers -- **sampleProvider**: This provider offers a mechanism to supply data or services to the plugin. It can be extended to include additional providers as needed. +### 1. Wallet Provider + +- Displays wallet balances +- Shows tokens in yield strategies +- Real-time balance updates + +### 2. Strategies Provider + +- Lists available yield strategies +- Shows deposit token requirements + +### 3. Tokens Provider + +- Lists supported tokens +- Shows token addresses + +## Development + +1. Clone the repository +2. Install dependencies: + +```bash +pnpm install +``` + +3. Build the plugin: + +```bash +pnpm run build +``` + +4. Run linting: + +```bash +pnpm run lint +``` + +## Dependencies + +- viem: ^2.21.49 +- @elizaos/core: workspace:\* + +## Future Enhancements + +1. **Advanced DeFi Operations** + + - Multi-hop yield strategies + - Auto-compounding features + - Yield optimization algorithms + - Risk assessment tools + - Portfolio rebalancing automation + - Cross-chain yield farming + +2. **Enhanced Token Management** + + - Batch token operations + - Advanced token creation templates + - Token migration tools + - Automated token listing + - Token analytics dashboard + - Custom tokenomics implementation + +3. **YAK Protocol Integration** + + - Advanced routing algorithms + - MEV protection features + - Gas optimization strategies + - Liquidity analysis tools + - Price impact predictions + - Custom trading strategies + +4. **Benqi Protocol Features** + + - Collateral optimization + - Liquidation protection + - Interest rate monitoring + - Position management tools + - Risk assessment dashboard + - Auto-repayment features + +5. **Token Mill Improvements** + + - Advanced token customization + - Automated market making + - Token distribution tools + - Vesting schedule management + - Governance token features + - Token upgrade mechanisms + +6. **Security Enhancements** + + - Transaction simulation + - Smart contract auditing tools + - Real-time monitoring + - Automated safety checks + - Emergency shutdown features + - Multi-signature support + +7. **Developer Tools** + + - Enhanced debugging capabilities + - Testing framework improvements + - Documentation generator + - CLI tools for common operations + - Integration templates + - Performance monitoring + +8. **Analytics and Reporting** + - Portfolio tracking + - Performance metrics + - Gas usage optimization + - Transaction history analysis + - Yield comparison tools + - Risk assessment reports + +We welcome community feedback and contributions to help prioritize these enhancements. -## Evaluators +## Contributing -- **sampleEvaluator**: This evaluator provides a way to assess or analyze data within the plugin. It can be extended to include additional evaluators as needed. +Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information. -## Services +## Credits -- **[ServiceName]**: Description of the service and its functionality. This can be extended to include additional services as needed. +This plugin integrates with and builds upon several key technologies: -## Clients +- [Avalanche](https://www.avax.network/): High-performance blockchain platform +- [avalanchejs](https://github.com/ava-labs/avalanchejs): Official Avalanche JavaScript library +- [YAK Protocol](https://yak.exchange/): Decentralized exchange aggregator +- [Benqi](https://benqi.fi/): Lending and borrowing protocol +- [Token Mill](https://tokenmill.xyz/): Token creation platform -- **[ClientName]**: Description of the client and its functionality. This can be extended to include additional clients as needed. +Special thanks to: -## How to Extend +- The Ava Labs team for developing Avalanche +- The YAK Protocol development team +- The Benqi protocol developers +- The Token Mill platform team +- The Avalanche Developer community +- The Eliza community for their contributions and feedback -To extend the Sample Plugin, you can add new actions, providers, evaluators, services, and clients by following the structure provided in the plugin. Each component can be customized to fit your specific requirements. +For more information about Avalanche capabilities: -1. **Actions**: Add new actions by defining them in the `actions` array. -2. **Providers**: Add new providers by defining them in the `providers` array. -3. **Evaluators**: Add new evaluators by defining them in the `evaluators` array. -4. **Services**: Add new services by defining them in the `services` array. -5. **Clients**: Add new clients by defining them in the `clients` array. +- [Avalanche Documentation](https://docs.avax.network/) +- [YAK Protocol Docs](https://yak.exchange/docs) +- [Benqi Documentation](https://docs.benqi.fi/) +- [Token Mill Guide](https://docs.tokenmill.xyz/) + +## License -For more detailed information on how to extend the plugin, refer to the documentation provided in the Eliza platform. +This plugin is part of the Eliza project. See the main project repository for license information. From 195c57b96f8b2985b4f21046de9263eb8fec45d3 Mon Sep 17 00:00:00 2001 From: goalongway Date: Wed, 8 Jan 2025 15:27:15 +0800 Subject: [PATCH 05/16] modify --- .env.example | 5 +- .../plugin-b2/src/actions/sampleAction.ts | 111 ------- packages/plugin-b2/src/actions/transfer.ts | 158 ++++++++++ packages/plugin-b2/src/environment.ts | 31 ++ .../src/evaluators/sampleEvalutor.ts | 53 ---- packages/plugin-b2/src/index.ts | 6 +- packages/plugin-b2/src/plugins/index.ts | 13 + .../plugin-b2/src/plugins/samplePlugin.ts | 15 - .../plugin-b2/src/providers/sampleProvider.ts | 14 - packages/plugin-b2/src/providers/wallet.ts | 41 +++ packages/plugin-b2/src/templates.ts | 60 ---- packages/plugin-b2/src/templates/index.ts | 44 +++ packages/plugin-b2/src/utils/chains.ts | 26 ++ packages/plugin-b2/src/utils/constants.ts | 12 + packages/plugin-b2/src/utils/index.ts | 291 ++++++++++++++++++ 15 files changed, 623 insertions(+), 257 deletions(-) delete mode 100644 packages/plugin-b2/src/actions/sampleAction.ts create mode 100644 packages/plugin-b2/src/actions/transfer.ts create mode 100644 packages/plugin-b2/src/environment.ts delete mode 100644 packages/plugin-b2/src/evaluators/sampleEvalutor.ts create mode 100644 packages/plugin-b2/src/plugins/index.ts delete mode 100644 packages/plugin-b2/src/plugins/samplePlugin.ts delete mode 100644 packages/plugin-b2/src/providers/sampleProvider.ts create mode 100644 packages/plugin-b2/src/providers/wallet.ts delete mode 100644 packages/plugin-b2/src/templates.ts create mode 100644 packages/plugin-b2/src/templates/index.ts create mode 100644 packages/plugin-b2/src/utils/chains.ts create mode 100644 packages/plugin-b2/src/utils/constants.ts create mode 100644 packages/plugin-b2/src/utils/index.ts diff --git a/.env.example b/.env.example index 3b18e9eedfd..7d996460a09 100644 --- a/.env.example +++ b/.env.example @@ -430,4 +430,7 @@ OPEN_WEATHER_API_KEY= # OpenWeather API key # Allora ALLORA_API_KEY=UP-f8db7d6558ab432ca0d92716 # Allora API key -ALLORA_CHAIN_SLUG=testnet # must be one of mainnet, testnet. If not specified, it will use testnet by default \ No newline at end of file +ALLORA_CHAIN_SLUG=testnet # must be one of mainnet, testnet. If not specified, it will use testnet by default + +# B2 Network +B2_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # Private key of the B2 Network account to use for the agent \ No newline at end of file diff --git a/packages/plugin-b2/src/actions/sampleAction.ts b/packages/plugin-b2/src/actions/sampleAction.ts deleted file mode 100644 index 7f835dac0a5..00000000000 --- a/packages/plugin-b2/src/actions/sampleAction.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { - Action, - IAgentRuntime, - Memory, - HandlerCallback, - State, - composeContext, - generateObject, - ModelClass, - elizaLogger, -} from "@elizaos/core"; - -import { CreateResourceSchema, isCreateResourceContent } from "../types"; - -import { createResourceTemplate } from "../templates"; - -export const createResourceAction: Action = { - name: "CREATE_RESOURCE", - description: "Create a new resource with the specified details", - validate: async (runtime: IAgentRuntime, _message: Memory) => { - return !!runtime.character.settings.secrets?.API_KEY; - }, - handler: async ( - runtime: IAgentRuntime, - _message: Memory, - state: State, - _options: any, - callback: HandlerCallback - ) => { - try { - const context = composeContext({ - state, - template: createResourceTemplate, - }); - - const resourceDetails = await generateObject({ - runtime, - context, - modelClass: ModelClass.SMALL, - schema: CreateResourceSchema, - }); - - if (!isCreateResourceContent(resourceDetails.object)) { - callback({ text: "Invalid resource details provided." }, []); - return; - } - - // persist relevant data if needed to memory/knowledge - // const memory = { - // type: "resource", - // content: resourceDetails.object, - // timestamp: new Date().toISOString() - // }; - - // await runtime.storeMemory(memory); - - callback( - { - text: `Resource created successfully: -- Name: ${resourceDetails.object.name} -- Type: ${resourceDetails.object.type} -- Description: ${resourceDetails.object.description} -- Tags: ${resourceDetails.object.tags.join(", ")} - -Resource has been stored in memory.`, - }, - [] - ); - } catch (error) { - elizaLogger.error("Error creating resource:", error); - callback( - { text: "Failed to create resource. Please check the logs." }, - [] - ); - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - text: "Create a new resource with the name 'Resource1' and type 'TypeA'", - }, - }, - { - user: "{{agentName}}", - content: { - text: `Resource created successfully: -- Name: Resource1 -- Type: TypeA`, - }, - }, - ], - [ - { - user: "{{user1}}", - content: { - text: "Create a new resource with the name 'Resource2' and type 'TypeB'", - }, - }, - { - user: "{{agentName}}", - content: { - text: `Resource created successfully: -- Name: Resource2 -- Type: TypeB`, - }, - }, - ], - ], -}; diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts new file mode 100644 index 00000000000..264b66c4ecf --- /dev/null +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -0,0 +1,158 @@ +import { + Action, + ActionExample, + IAgentRuntime, + Memory, + State, + HandlerCallback, + elizaLogger, + composeContext, + generateObject, + ModelClass, + Content, +} from "@elizaos/core"; +import { getTxReceipt, sendNativeAsset, sendToken } from "../utils"; +import { Address } from "viem"; +import { validateB2NetworkConfig } from "../environment"; +import { transferTemplate } from "../templates"; + + +export interface TransferContent extends Content { + tokenAddress: string; + recipient: string; + amount: string | number; +} + +function isTransferContent( + runtime: IAgentRuntime, + content: any +): content is TransferContent { + elizaLogger.debug("Content for transfer", content); + return ( + typeof content.tokenAddress === "string" && + typeof content.recipient === "string" && + (typeof content.amount === "string" || + typeof content.amount === "number") + ); +} + + +export default { + name: "SEND_TOKEN", + similes: [ + "TRANSFER_TOKEN_ON_B2", + "TRANSFER_TOKENS_ON_B2", + "SEND_TOKENS_ON_B2", + "SEND_AVAX_ON_B2", + "PAY_ON_B2", + ], + validate: async (runtime: IAgentRuntime, _message: Memory) => { + await validateB2NetworkConfig(runtime); + return true; + }, + description: + "MUST use this action if the user requests send a token or transfer a token, the request might be varied, but it will always be a token transfer.", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + elizaLogger.log("Starting SEND_TOKEN handler..."); + + // Validate transfer + if (message.content.source === "direct") { + // + } else { + callback?.({ + text: "i can't do that for you.", + content: { error: "Transfer not allowed" }, + }); + return false; + } + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose transfer context + const transferContext = composeContext({ + state, + template: transferTemplate, + }); + + // Generate transfer content + const content = await generateObject({ + runtime, + context: transferContext, + modelClass: ModelClass.SMALL, + }); + + elizaLogger.debug("Transfer content:", content); + + // Validate transfer content + if (!isTransferContent(runtime, content)) { + elizaLogger.error("Invalid content for TRANSFER_TOKEN action."); + callback?.({ + text: "Unable to process transfer request. Invalid content provided.", + content: { error: "Invalid transfer content" }, + }); + return false; + } + + let tx; + if ( + content.tokenAddress === + "0x0000000000000000000000000000000000000000" + ) { + tx = await sendNativeAsset( + runtime, + content.recipient as Address, + content.amount as number + ); + } else { + tx = await sendToken( + runtime, + content.tokenAddress as Address, + content.recipient as Address, + content.amount as number + ); + } + + if (tx) { + const receipt = await getTxReceipt(runtime, tx); + if (receipt.status === "success") { + callback?.({ + text: "transfer successful", + content: { success: true, txHash: tx }, + }); + } else { + callback?.({ + text: "transfer failed", + content: { error: "Transfer failed" }, + }); + } + } else { + callback?.({ + text: "transfer failed", + content: { error: "Transfer failed" }, + }); + } + + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Send 1 B2-BTC to 0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-b2/src/environment.ts b/packages/plugin-b2/src/environment.ts new file mode 100644 index 00000000000..637f5dacde9 --- /dev/null +++ b/packages/plugin-b2/src/environment.ts @@ -0,0 +1,31 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const b2NetworkEnvSchema = z.object({ + B2_PRIVATE_KEY: z + .string() + .min(1, "b2 network private key is required"), +}); + +export type b2NetworkConfig = z.infer; +export async function validateB2NetworkConfig( + runtime: IAgentRuntime +): Promise { + try { + const config = { + B2_PRIVATE_KEY: + runtime.getSetting("B2_PRIVATE_KEY") || + process.env.B2_PRIVATE_KEY, + }; + + return b2NetworkEnvSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error(errorMessages); + } + throw error; + } +} diff --git a/packages/plugin-b2/src/evaluators/sampleEvalutor.ts b/packages/plugin-b2/src/evaluators/sampleEvalutor.ts deleted file mode 100644 index c6d48b07153..00000000000 --- a/packages/plugin-b2/src/evaluators/sampleEvalutor.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - Evaluator, - IAgentRuntime, - Memory, - State, - elizaLogger, -} from "@elizaos/core"; - -export const sampleEvaluator: Evaluator = { - alwaysRun: false, - description: "Sample evaluator for checking important content in memory", - similes: ["content checker", "memory evaluator"], - examples: [ - { - context: "Checking if memory contains important content", - messages: [ - { - action: "evaluate", - input: "This is an important message", - output: { - score: 1, - reason: "Memory contains important content.", - }, - }, - ], - outcome: "Memory should be evaluated as important", - }, - ], - handler: async (runtime: IAgentRuntime, memory: Memory, state: State) => { - // Evaluation logic for the evaluator - elizaLogger.log("Evaluating data in sampleEvaluator..."); - - // Example evaluation logic - if (memory.content && memory.content.includes("important")) { - elizaLogger.log("Important content found in memory."); - return { - score: 1, - reason: "Memory contains important content.", - }; - } else { - elizaLogger.log("No important content found in memory."); - return { - score: 0, - reason: "Memory does not contain important content.", - }; - } - }, - name: "sampleEvaluator", - validate: async (runtime: IAgentRuntime, memory: Memory, state: State) => { - // Validation logic for the evaluator - return true; - }, -}; diff --git a/packages/plugin-b2/src/index.ts b/packages/plugin-b2/src/index.ts index b16dd061427..94f5fe6b8ba 100644 --- a/packages/plugin-b2/src/index.ts +++ b/packages/plugin-b2/src/index.ts @@ -1,5 +1,5 @@ -import { samplePlugin } from "./plugins/samplePlugin"; +import { b2Plugin } from "./plugins"; -export * from "./plugins/samplePlugin"; +export * from "./plugins"; -export default samplePlugin; +export default b2Plugin; diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts new file mode 100644 index 00000000000..ff0267131a4 --- /dev/null +++ b/packages/plugin-b2/src/plugins/index.ts @@ -0,0 +1,13 @@ +import { Plugin } from "@elizaos/core"; +import transfer from "../actions/transfer"; +import { b2WalletProvider } from "../providers/wallet"; + +export const b2Plugin: Plugin = { + name: "b2 plugin", + description: "B2 Network Plugin for Eliza", + actions: [transfer], + providers: [b2WalletProvider], + evaluators: [], + services: [], + clients: [], +}; diff --git a/packages/plugin-b2/src/plugins/samplePlugin.ts b/packages/plugin-b2/src/plugins/samplePlugin.ts deleted file mode 100644 index 90fd2898a16..00000000000 --- a/packages/plugin-b2/src/plugins/samplePlugin.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Plugin } from "@elizaos/core"; -import { createResourceAction } from "../actions/sampleAction"; -import { sampleProvider } from "../providers/sampleProvider"; -import { sampleEvaluator } from "../evaluators/sampleEvalutor"; - -export const samplePlugin: Plugin = { - name: "sample", - description: "Enables creation and management of generic resources", - actions: [createResourceAction], - providers: [sampleProvider], - evaluators: [sampleEvaluator], - // separate examples will be added for services and clients - services: [], - clients: [], -}; diff --git a/packages/plugin-b2/src/providers/sampleProvider.ts b/packages/plugin-b2/src/providers/sampleProvider.ts deleted file mode 100644 index d16f3ba6ddf..00000000000 --- a/packages/plugin-b2/src/providers/sampleProvider.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { - Provider, - IAgentRuntime, - Memory, - State, - elizaLogger, -} from "@elizaos/core"; - -export const sampleProvider: Provider = { - get: async (runtime: IAgentRuntime, message: Memory, state: State) => { - // Data retrieval logic for the provider - elizaLogger.log("Retrieving data in sampleProvider..."); - }, -}; diff --git a/packages/plugin-b2/src/providers/wallet.ts b/packages/plugin-b2/src/providers/wallet.ts new file mode 100644 index 00000000000..e1fdda8271f --- /dev/null +++ b/packages/plugin-b2/src/providers/wallet.ts @@ -0,0 +1,41 @@ +import { + IAgentRuntime, + Memory, + Provider, + State, + elizaLogger, +} from "@elizaos/core"; +import { formatUnits } from "viem"; +import { getAccount, getDecimals, getTokenBalance } from "../utils"; +import { TOKEN_ADDRESSES } from "../utils/constants"; + +const b2WalletProvider: Provider = { + get: async (runtime: IAgentRuntime, _message: Memory, _state?: State) => { + elizaLogger.debug("walletProvider::get"); + const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); + if (!privateKey) { + throw new Error( + "AVALANCHE_PRIVATE_KEY not found in environment variables" + ); + } + const account = getAccount(runtime); + let output = `# Wallet Balances\n\n`; + output += `## Wallet Address\n\n\`${account.address}\`\n\n`; + + output += `## Latest Token Balances\n\n`; + for (const [token, address] of Object.entries(TOKEN_ADDRESSES)) { + const decimals = await getDecimals(runtime, address); + const balance = await getTokenBalance( + runtime, + address, + account.address + ); + output += `${token}: ${formatUnits(balance, decimals)}\n`; + } + output += `Note: These balances can be used at any time.\n\n`; + elizaLogger.debug("walletProvider::get output:", output); + return output; + }, +}; + +export { b2WalletProvider }; diff --git a/packages/plugin-b2/src/templates.ts b/packages/plugin-b2/src/templates.ts deleted file mode 100644 index f9c0d965917..00000000000 --- a/packages/plugin-b2/src/templates.ts +++ /dev/null @@ -1,60 +0,0 @@ -export const createResourceTemplate = ` -Extract the following details to create a new resource: -- **name** (string): Name of the resource -- **type** (string): Type of resource (document, image, video) -- **description** (string): Description of the resource -- **tags** (array): Array of tags to categorize the resource - -Provide the values in the following JSON format: - -\`\`\`json -{ - "name": "", - "type": "", - "description": "", - "tags": ["", ""] -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; - -export const readResourceTemplate = ` -Extract the following details to read a resource: -- **id** (string): Unique identifier of the resource -- **fields** (array): Specific fields to retrieve (optional) - -Provide the values in the following JSON format: - -\`\`\`json -{ - "id": "", - "fields": ["", ""] -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; - -export const updateResourceTemplate = ` -Extract the following details to update a resource: -- **id** (string): Unique identifier of the resource -- **updates** (object): Key-value pairs of fields to update - -Provide the values in the following JSON format: - -\`\`\`json -{ - "id": "", - "updates": { - "": "", - "": "" - } -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; diff --git a/packages/plugin-b2/src/templates/index.ts b/packages/plugin-b2/src/templates/index.ts new file mode 100644 index 00000000000..09ab4471ee6 --- /dev/null +++ b/packages/plugin-b2/src/templates/index.ts @@ -0,0 +1,44 @@ +import { TOKEN_ADDRESSES } from "../utils/constants"; + +const transferTemplate = `Respond with a JSON markdown block containing only the extracted values +- Use null for any values that cannot be determined. +- Use address zero for native B2-BTC transfers. + +Example response for a 10 uBTC transfer: +\`\`\`json +{ + "tokenAddress": "0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2", + "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", + "amount": "10" +} +\`\`\` + +Example response for a 0.1 B2-BTC transfer: +\`\`\`json +{ + "tokenAddress": "0x0000000000000000000000000000000000000000", + "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", + "amount": "0.1" +} +\`\`\` + +## Token Addresses + +${Object.entries(TOKEN_ADDRESSES) + .map(([key, value]) => `- ${key}: ${value}`) + .join("\n")} + +## Recent Messages + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested token transfer: +- Token contract address +- Recipient wallet address +- Amount to transfer + +Respond with a JSON markdown block containing only the extracted values.`; + +export { + transferTemplate, +}; \ No newline at end of file diff --git a/packages/plugin-b2/src/utils/chains.ts b/packages/plugin-b2/src/utils/chains.ts new file mode 100644 index 00000000000..0eec893a835 --- /dev/null +++ b/packages/plugin-b2/src/utils/chains.ts @@ -0,0 +1,26 @@ +import { defineChain } from 'viem' + +export const b2Network = defineChain({ + id: 223, + name: 'B2Network', + network: 'B2Network', + nativeCurrency: { + decimals: 18, + name: 'Bitcoin', + symbol: 'BTC', + }, + blockExplorers: { + default: { + name: 'B2Network', + url: 'https://explorer.bsquared.network/' + } + }, + rpcUrls: { + default: { + http: ['https://rpc.bsquared.network/'], + }, + public: { + http: ['https://rpc.bsquared.network/'], + }, + }, +}) \ No newline at end of file diff --git a/packages/plugin-b2/src/utils/constants.ts b/packages/plugin-b2/src/utils/constants.ts new file mode 100644 index 00000000000..0ca874d1854 --- /dev/null +++ b/packages/plugin-b2/src/utils/constants.ts @@ -0,0 +1,12 @@ +import { Address } from "viem"; + +const TOKEN_ADDRESSES: Record = { + "B2-BTC": "0x0000000000000000000000000000000000000000", + uBTC: "0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2", + USDC: "0xE544e8a38aDD9B1ABF21922090445Ba93f74B9E5", + USDT: "0x681202351a488040Fa4FdCc24188AfB582c9DD62", +}; + +export { + TOKEN_ADDRESSES, +}; diff --git a/packages/plugin-b2/src/utils/index.ts b/packages/plugin-b2/src/utils/index.ts new file mode 100644 index 00000000000..be97fc65390 --- /dev/null +++ b/packages/plugin-b2/src/utils/index.ts @@ -0,0 +1,291 @@ +import { IAgentRuntime, elizaLogger } from "@elizaos/core"; +import { + createPublicClient, + createWalletClient, + Hash, + http, + Address, + parseUnits, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { b2Network } from "./chains"; + +export const getAccount = (runtime: IAgentRuntime) => { + const privateKey = + runtime.getSetting("B2_PRIVATE_KEY") || + process.env.B2_PRIVATE_KEY; + return privateKeyToAccount(`0x${privateKey.replace("0x", "")}`); +}; + +export const getPublicClient = (_runtime: IAgentRuntime) => { + return createPublicClient({ + chain: b2Network, + transport: http(), + }); +}; + +export const getWalletClient = (runtime: IAgentRuntime) => { + return createWalletClient({ + account: getAccount(runtime), + chain: b2Network, + transport: http(), + }); +}; + +export const getTxReceipt = async (runtime: IAgentRuntime, tx: Hash) => { + const publicClient = getPublicClient(runtime); + const receipt = await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + return receipt; +}; + +export const getDecimals = async ( + runtime: IAgentRuntime, + tokenAddress: Address +) => { + if (tokenAddress === "0x0000000000000000000000000000000000000000") { + return b2Network.nativeCurrency.decimals; + } + const publicClient = getPublicClient(runtime); + const decimals = await publicClient.readContract({ + address: tokenAddress, + abi: [ + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function", + }, + ], + functionName: "decimals", + }); + return decimals; +}; + +export const getNativeBalance = async ( + runtime: IAgentRuntime, + owner: Address +) => { + const publicClient = getPublicClient(runtime); + const balance = await publicClient.getBalance({ + address: owner, + }); + return balance; +}; + +export const getTokenBalance = async ( + runtime: IAgentRuntime, + tokenAddress: Address, + owner: Address +) => { + if (tokenAddress === "0x0000000000000000000000000000000000000000") { + return getNativeBalance(runtime, owner); + } + const publicClient = getPublicClient(runtime); + const balance = await publicClient.readContract({ + address: tokenAddress, + abi: [ + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { internalType: "uint256", name: "", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + ], + functionName: "balanceOf", + args: [owner], + }); + return balance; +}; + +export const sendNativeAsset = async ( + runtime: IAgentRuntime, + recipient: Address, + amount: number +) => { + const walletClient = getWalletClient(runtime); + const decimals = await getDecimals( + runtime, + "0x0000000000000000000000000000000000000000" + ); + const tx = await walletClient.sendTransaction({ + to: recipient, + value: parseUnits(amount.toString(), decimals), + }); + return tx as Hash; +}; + +export const sendToken = async ( + runtime: IAgentRuntime, + tokenAddress: Address, + recipient: Address, + amount: number +) => { + const decimals = await getDecimals(runtime, tokenAddress); + const publicClient = getPublicClient(runtime); + + try { + const { result, request } = await publicClient.simulateContract({ + account: getAccount(runtime), + address: tokenAddress, + abi: [ + { + inputs: [ + { + internalType: "address", + name: "dst", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "transfer", + args: [recipient, parseUnits(amount.toString(), decimals)], + }); + + if (!result) { + throw new Error("Transfer failed"); + } + + elizaLogger.debug("Request:", request); + + const walletClient = getWalletClient(runtime); + const tx = await walletClient.writeContract(request); + elizaLogger.log("Transaction:", tx); + return tx as Hash; + } catch (error) { + elizaLogger.error("Error simulating contract:", error); + return; + } +}; + +export const approve = async ( + runtime: IAgentRuntime, + tokenAddress: Address, + spender: Address, + amount: number +) => { + try { + const decimals = await getDecimals(runtime, tokenAddress); + const publicClient = getPublicClient(runtime); + const { result, request } = await publicClient.simulateContract({ + account: getAccount(runtime), + address: tokenAddress, + abi: [ + { + inputs: [ + { + internalType: "address", + name: "_spender", + type: "address", + }, + { + internalType: "uint256", + name: "_value", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "approve", + args: [spender, parseUnits(amount.toString(), decimals)], + }); + + if (!result) { + throw new Error("Approve failed"); + } + + elizaLogger.debug("Request:", request); + + const walletClient = getWalletClient(runtime); + const tx = await walletClient.writeContract(request); + elizaLogger.log("Transaction:", tx); + return tx; + } catch (error) { + elizaLogger.error("Error approving:", error); + return; + } +}; + +export const deposit = async ( + runtime: IAgentRuntime, + depositTokenAddress: Address, + strategyAddress: Address, + amount: number +) => { + try { + const decimals = await getDecimals(runtime, depositTokenAddress); + const publicClient = getPublicClient(runtime); + const { _result, request } = await publicClient.simulateContract({ + account: getAccount(runtime), + address: strategyAddress, + abi: [ + { + inputs: [ + { + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "deposit", + args: [parseUnits(amount.toString(), decimals)], + }); + + // if (!result) { + // throw new Error('Deposit failed') + // } + + elizaLogger.debug("Request:", request); + + const walletClient = getWalletClient(runtime); + const tx = await walletClient.writeContract(request); + elizaLogger.log("Transaction:", tx); + return tx; + } catch (error) { + elizaLogger.error("Error depositing:", error); + return; + } +}; From de31440485e0cf629f1393c14526ebe7381b60f4 Mon Sep 17 00:00:00 2001 From: Afanti Afanti Date: Wed, 8 Jan 2025 07:42:01 +0000 Subject: [PATCH 06/16] feat: transfer action --- packages/plugin-b2/package.json | 3 +- .../plugin-b2/src/actions/sampleAction.ts | 111 ------------------ packages/plugin-b2/src/actions/transfer.ts | 20 ++++ .../src/evaluators/sampleEvalutor.ts | 53 --------- packages/plugin-b2/src/index.ts | 16 ++- .../plugin-b2/src/plugins/samplePlugin.ts | 15 --- .../{sampleProvider.ts => walletProvider.ts} | 5 +- packages/plugin-b2/src/templates.ts | 60 ---------- packages/plugin-b2/src/types.ts | 55 --------- 9 files changed, 36 insertions(+), 302 deletions(-) delete mode 100644 packages/plugin-b2/src/actions/sampleAction.ts create mode 100644 packages/plugin-b2/src/actions/transfer.ts delete mode 100644 packages/plugin-b2/src/evaluators/sampleEvalutor.ts delete mode 100644 packages/plugin-b2/src/plugins/samplePlugin.ts rename packages/plugin-b2/src/providers/{sampleProvider.ts => walletProvider.ts} (55%) delete mode 100644 packages/plugin-b2/src/templates.ts delete mode 100644 packages/plugin-b2/src/types.ts diff --git a/packages/plugin-b2/package.json b/packages/plugin-b2/package.json index 84727b1ac1d..90c41a65080 100644 --- a/packages/plugin-b2/package.json +++ b/packages/plugin-b2/package.json @@ -5,7 +5,8 @@ "type": "module", "types": "dist/index.d.ts", "dependencies": { - "@elizaos/core": "workspace:*" + "@elizaos/core": "workspace:*", + "tsup": "8.3.5" }, "devDependencies": { "tsup": "8.3.5", diff --git a/packages/plugin-b2/src/actions/sampleAction.ts b/packages/plugin-b2/src/actions/sampleAction.ts deleted file mode 100644 index 7f835dac0a5..00000000000 --- a/packages/plugin-b2/src/actions/sampleAction.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { - Action, - IAgentRuntime, - Memory, - HandlerCallback, - State, - composeContext, - generateObject, - ModelClass, - elizaLogger, -} from "@elizaos/core"; - -import { CreateResourceSchema, isCreateResourceContent } from "../types"; - -import { createResourceTemplate } from "../templates"; - -export const createResourceAction: Action = { - name: "CREATE_RESOURCE", - description: "Create a new resource with the specified details", - validate: async (runtime: IAgentRuntime, _message: Memory) => { - return !!runtime.character.settings.secrets?.API_KEY; - }, - handler: async ( - runtime: IAgentRuntime, - _message: Memory, - state: State, - _options: any, - callback: HandlerCallback - ) => { - try { - const context = composeContext({ - state, - template: createResourceTemplate, - }); - - const resourceDetails = await generateObject({ - runtime, - context, - modelClass: ModelClass.SMALL, - schema: CreateResourceSchema, - }); - - if (!isCreateResourceContent(resourceDetails.object)) { - callback({ text: "Invalid resource details provided." }, []); - return; - } - - // persist relevant data if needed to memory/knowledge - // const memory = { - // type: "resource", - // content: resourceDetails.object, - // timestamp: new Date().toISOString() - // }; - - // await runtime.storeMemory(memory); - - callback( - { - text: `Resource created successfully: -- Name: ${resourceDetails.object.name} -- Type: ${resourceDetails.object.type} -- Description: ${resourceDetails.object.description} -- Tags: ${resourceDetails.object.tags.join(", ")} - -Resource has been stored in memory.`, - }, - [] - ); - } catch (error) { - elizaLogger.error("Error creating resource:", error); - callback( - { text: "Failed to create resource. Please check the logs." }, - [] - ); - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - text: "Create a new resource with the name 'Resource1' and type 'TypeA'", - }, - }, - { - user: "{{agentName}}", - content: { - text: `Resource created successfully: -- Name: Resource1 -- Type: TypeA`, - }, - }, - ], - [ - { - user: "{{user1}}", - content: { - text: "Create a new resource with the name 'Resource2' and type 'TypeB'", - }, - }, - { - user: "{{agentName}}", - content: { - text: `Resource created successfully: -- Name: Resource2 -- Type: TypeB`, - }, - }, - ], - ], -}; diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts new file mode 100644 index 00000000000..9d14bfde12f --- /dev/null +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -0,0 +1,20 @@ +import { Action , type IAgentRuntime, type Memory, type State, type HandlerCallback } from "@elizaos/core"; + +export const transferAction: Action = { + name: "B2_TRANSFER", + description: "Transfer B2 gas token between addresses on the B2 network", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state?: State, + option?: any, + callback?: HandlerCallback + ) => { + return true; + }, + validate: async (runtime: IAgentRuntime) => { + return true; + }, + examples: [], + similes: ["SEND_B2_TOKEN", "TRANSFER_B2_TOKEN", "MOVE_B2_TOKEN"], +}; \ No newline at end of file diff --git a/packages/plugin-b2/src/evaluators/sampleEvalutor.ts b/packages/plugin-b2/src/evaluators/sampleEvalutor.ts deleted file mode 100644 index c6d48b07153..00000000000 --- a/packages/plugin-b2/src/evaluators/sampleEvalutor.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - Evaluator, - IAgentRuntime, - Memory, - State, - elizaLogger, -} from "@elizaos/core"; - -export const sampleEvaluator: Evaluator = { - alwaysRun: false, - description: "Sample evaluator for checking important content in memory", - similes: ["content checker", "memory evaluator"], - examples: [ - { - context: "Checking if memory contains important content", - messages: [ - { - action: "evaluate", - input: "This is an important message", - output: { - score: 1, - reason: "Memory contains important content.", - }, - }, - ], - outcome: "Memory should be evaluated as important", - }, - ], - handler: async (runtime: IAgentRuntime, memory: Memory, state: State) => { - // Evaluation logic for the evaluator - elizaLogger.log("Evaluating data in sampleEvaluator..."); - - // Example evaluation logic - if (memory.content && memory.content.includes("important")) { - elizaLogger.log("Important content found in memory."); - return { - score: 1, - reason: "Memory contains important content.", - }; - } else { - elizaLogger.log("No important content found in memory."); - return { - score: 0, - reason: "Memory does not contain important content.", - }; - } - }, - name: "sampleEvaluator", - validate: async (runtime: IAgentRuntime, memory: Memory, state: State) => { - // Validation logic for the evaluator - return true; - }, -}; diff --git a/packages/plugin-b2/src/index.ts b/packages/plugin-b2/src/index.ts index b16dd061427..d23cc0d9533 100644 --- a/packages/plugin-b2/src/index.ts +++ b/packages/plugin-b2/src/index.ts @@ -1,5 +1,13 @@ -import { samplePlugin } from "./plugins/samplePlugin"; +import { Plugin } from "@elizaos/core"; +import { transferAction } from "./actions/transfer"; +import { walletProvider } from "./providers/walletProvider"; -export * from "./plugins/samplePlugin"; - -export default samplePlugin; +export const b2Plugin: Plugin = { + name: "b2", + description: "B2 network plugin for Eliza", + actions: [transferAction], + providers: [walletProvider], + evaluators: [], + services: [], + clients: [], +}; diff --git a/packages/plugin-b2/src/plugins/samplePlugin.ts b/packages/plugin-b2/src/plugins/samplePlugin.ts deleted file mode 100644 index 90fd2898a16..00000000000 --- a/packages/plugin-b2/src/plugins/samplePlugin.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Plugin } from "@elizaos/core"; -import { createResourceAction } from "../actions/sampleAction"; -import { sampleProvider } from "../providers/sampleProvider"; -import { sampleEvaluator } from "../evaluators/sampleEvalutor"; - -export const samplePlugin: Plugin = { - name: "sample", - description: "Enables creation and management of generic resources", - actions: [createResourceAction], - providers: [sampleProvider], - evaluators: [sampleEvaluator], - // separate examples will be added for services and clients - services: [], - clients: [], -}; diff --git a/packages/plugin-b2/src/providers/sampleProvider.ts b/packages/plugin-b2/src/providers/walletProvider.ts similarity index 55% rename from packages/plugin-b2/src/providers/sampleProvider.ts rename to packages/plugin-b2/src/providers/walletProvider.ts index d16f3ba6ddf..ee9ec035748 100644 --- a/packages/plugin-b2/src/providers/sampleProvider.ts +++ b/packages/plugin-b2/src/providers/walletProvider.ts @@ -6,9 +6,8 @@ import { elizaLogger, } from "@elizaos/core"; -export const sampleProvider: Provider = { +export const walletProvider: Provider = { get: async (runtime: IAgentRuntime, message: Memory, state: State) => { - // Data retrieval logic for the provider - elizaLogger.log("Retrieving data in sampleProvider..."); + elizaLogger.log("Retrieving data in walletProvider..."); }, }; diff --git a/packages/plugin-b2/src/templates.ts b/packages/plugin-b2/src/templates.ts deleted file mode 100644 index f9c0d965917..00000000000 --- a/packages/plugin-b2/src/templates.ts +++ /dev/null @@ -1,60 +0,0 @@ -export const createResourceTemplate = ` -Extract the following details to create a new resource: -- **name** (string): Name of the resource -- **type** (string): Type of resource (document, image, video) -- **description** (string): Description of the resource -- **tags** (array): Array of tags to categorize the resource - -Provide the values in the following JSON format: - -\`\`\`json -{ - "name": "", - "type": "", - "description": "", - "tags": ["", ""] -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; - -export const readResourceTemplate = ` -Extract the following details to read a resource: -- **id** (string): Unique identifier of the resource -- **fields** (array): Specific fields to retrieve (optional) - -Provide the values in the following JSON format: - -\`\`\`json -{ - "id": "", - "fields": ["", ""] -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; - -export const updateResourceTemplate = ` -Extract the following details to update a resource: -- **id** (string): Unique identifier of the resource -- **updates** (object): Key-value pairs of fields to update - -Provide the values in the following JSON format: - -\`\`\`json -{ - "id": "", - "updates": { - "": "", - "": "" - } -} -\`\`\` - -Here are the recent user messages for context: -{{recentMessages}} -`; diff --git a/packages/plugin-b2/src/types.ts b/packages/plugin-b2/src/types.ts deleted file mode 100644 index ef7d4939631..00000000000 --- a/packages/plugin-b2/src/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { z } from "zod"; - -// Base resource schema -export const ResourceSchema = z.object({ - id: z.string().optional(), - name: z.string().min(1), - type: z.enum(["document", "image", "video"]), - description: z.string(), - tags: z.array(z.string()), -}); - -// Create resource schema -export const CreateResourceSchema = ResourceSchema.omit({ id: true }); - -// Read resource schema -export const ReadResourceSchema = z.object({ - id: z.string(), - fields: z.array(z.string()).optional(), -}); - -// Update resource schema -export const UpdateResourceSchema = z.object({ - id: z.string(), - updates: z.record(z.string(), z.any()), -}); - -// Type definitions -export type Resource = z.infer; -export type CreateResourceContent = z.infer; -export type ReadResourceContent = z.infer; -export type UpdateResourceContent = z.infer; - -// Type guards -export const isCreateResourceContent = ( - obj: any -): obj is CreateResourceContent => { - return CreateResourceSchema.safeParse(obj).success; -}; - -export const isReadResourceContent = (obj: any): obj is ReadResourceContent => { - return ReadResourceSchema.safeParse(obj).success; -}; - -export const isUpdateResourceContent = ( - obj: any -): obj is UpdateResourceContent => { - return UpdateResourceSchema.safeParse(obj).success; -}; - -// Plugin configuration type -export interface ExamplePluginConfig { - apiKey: string; - apiSecret: string; - endpoint?: string; -} From f5b3b5603577ed3ba0047f2bfd0d4d4f692b2c6e Mon Sep 17 00:00:00 2001 From: goalongway Date: Wed, 8 Jan 2025 16:03:22 +0800 Subject: [PATCH 07/16] modify README.md --- packages/plugin-b2/README.md | 114 ++------------------- packages/plugin-b2/src/providers/wallet.ts | 2 +- 2 files changed, 11 insertions(+), 105 deletions(-) diff --git a/packages/plugin-b2/README.md b/packages/plugin-b2/README.md index 81908ac1713..9d6b21477a2 100644 --- a/packages/plugin-b2/README.md +++ b/packages/plugin-b2/README.md @@ -1,12 +1,7 @@ -<<<<<<< HEAD # @elizaos/plugin-b2 A plugin for interacting with the B2-Network within the ElizaOS ecosystem. -## Description - -The Avalanche plugin enables comprehensive DeFi operations on the Avalanche network, including token transfers, YAK swaps, yield strategy management, and token creation via Token Mill. -======= # B2 Network Plugin for Eliza The B2 Network Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. @@ -14,12 +9,11 @@ The B2 Network Plugin for Eliza extends the functionality of the Eliza platform ## Description The B2 Network Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. ->>>>>>> b72151cd95a7f17e528fc82a0a765d60104e5246 ## Installation ```bash -pnpm install @elizaos/plugin-avalanche +pnpm install @elizaos/plugin-b2 ``` ## Configuration @@ -27,49 +21,25 @@ pnpm install @elizaos/plugin-avalanche The plugin requires the following environment variable: ```typescript -AVALANCHE_PRIVATE_KEY= +B2_PRIVATE_KEY= ``` ## Features ### 1. Token Transfers -- Send native AVAX and ERC20 tokens +- Send native B2-BTC and ERC20 tokens - Support for multiple token standards - Built-in address validation -### 2. YAK Swaps - -- Decentralized token swaps -- Automatic best path finding -- Slippage protection (default: 0.2%) -- Support for all major tokens - -### 3. Yield Strategies - -- Deposit tokens into yield-generating strategies -- Support for multiple strategies including: - - YAK staking - - USDC Benqi - - gmYAK Token Mill - - PRINCESS staking - - JOE staking - -### 4. Token Mill - -- Create new tokens -- Configure custom tokenomics -- Automatic market creation - ## Supported Tokens ```typescript const TOKENS = { - AVAX: "0x0000000000000000000000000000000000000000", - WAVAX: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", - YAK: "0x59414b3089ce2AF0010e7523Dea7E2b35d776ec7", - gmYAK: "0x3A30784c1af928CdFce678eE49370220aA716DC3", - USDC: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + "B2-BTC": "0x0000000000000000000000000000000000000000", + uBTC: "0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2", + USDC: "0xE544e8a38aDD9B1ABF21922090445Ba93f74B9E5", + USDT: "0x681202351a488040Fa4FdCc24188AfB582c9DD62", // ... and more }; ``` @@ -79,50 +49,21 @@ const TOKENS = { ### Token Transfer ```typescript -// Send AVAX -"Send 10 AVAX to 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; +// Send B2-BTC +"Send 1 B2-BTC to 0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a"; // Send ERC20 "Transfer 100 USDC to [address]"; ``` -### YAK Swap - -```typescript -// Swap tokens -"Swap 1 AVAX for USDC"; -"Swap 10 USDC for gmYAK"; -``` - -### Yield Strategy - -```typescript -// Deposit into strategies -"Deposit 1 USDC into the strategy"; -"Deposit 10 gmYAK to earn yield"; -``` - -### Token Creation - -```typescript -// Create new token -"Create a new memecoin called 'Test Token' with the symbol 'TEST'"; -``` - ## Providers ### 1. Wallet Provider - Displays wallet balances -- Shows tokens in yield strategies - Real-time balance updates -### 2. Strategies Provider - -- Lists available yield strategies -- Shows deposit token requirements - -### 3. Tokens Provider +### 2. Tokens Provider - Lists supported tokens - Shows token addresses @@ -131,15 +72,6 @@ const TOKENS = { 1. Clone the repository 2. Install dependencies: - -<<<<<<< HEAD -```bash -pnpm install -``` -======= -To extend the B2 Network Plugin, you can add new actions, providers, evaluators, services, and clients by following the structure provided in the plugin. Each component can be customized to fit your specific requirements. ->>>>>>> b72151cd95a7f17e528fc82a0a765d60104e5246 - 3. Build the plugin: ```bash @@ -236,32 +168,6 @@ We welcome community feedback and contributions to help prioritize these enhance Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information. -## Credits - -This plugin integrates with and builds upon several key technologies: - -- [Avalanche](https://www.avax.network/): High-performance blockchain platform -- [avalanchejs](https://github.com/ava-labs/avalanchejs): Official Avalanche JavaScript library -- [YAK Protocol](https://yak.exchange/): Decentralized exchange aggregator -- [Benqi](https://benqi.fi/): Lending and borrowing protocol -- [Token Mill](https://tokenmill.xyz/): Token creation platform - -Special thanks to: - -- The Ava Labs team for developing Avalanche -- The YAK Protocol development team -- The Benqi protocol developers -- The Token Mill platform team -- The Avalanche Developer community -- The Eliza community for their contributions and feedback - -For more information about Avalanche capabilities: - -- [Avalanche Documentation](https://docs.avax.network/) -- [YAK Protocol Docs](https://yak.exchange/docs) -- [Benqi Documentation](https://docs.benqi.fi/) -- [Token Mill Guide](https://docs.tokenmill.xyz/) - ## License This plugin is part of the Eliza project. See the main project repository for license information. diff --git a/packages/plugin-b2/src/providers/wallet.ts b/packages/plugin-b2/src/providers/wallet.ts index e1fdda8271f..df906a5b778 100644 --- a/packages/plugin-b2/src/providers/wallet.ts +++ b/packages/plugin-b2/src/providers/wallet.ts @@ -15,7 +15,7 @@ const b2WalletProvider: Provider = { const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); if (!privateKey) { throw new Error( - "AVALANCHE_PRIVATE_KEY not found in environment variables" + "B2_PRIVATE_KEY not found in environment variables" ); } const account = getAccount(runtime); From b0282ca24f901f09c0a98f84ddd18ba84e66a5ec Mon Sep 17 00:00:00 2001 From: Afanti Afanti Date: Wed, 8 Jan 2025 08:15:28 +0000 Subject: [PATCH 08/16] docs: update readme --- packages/plugin-b2/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/plugin-b2/README.md b/packages/plugin-b2/README.md index 9d6b21477a2..1e5ba623cab 100644 --- a/packages/plugin-b2/README.md +++ b/packages/plugin-b2/README.md @@ -2,13 +2,9 @@ A plugin for interacting with the B2-Network within the ElizaOS ecosystem. -# B2 Network Plugin for Eliza - -The B2 Network Plugin for Eliza extends the functionality of the Eliza platform by providing additional actions, providers, evaluators, and more. This plugin is designed to be easily extendable and customizable to fit various use cases. - ## Description -The B2 Network Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. Below is a high-level overview of the different components available in this plugin. +The B2 Network Plugin offers a set of features that can be integrated into the Eliza platform to enhance its capabilities. This plugin enables seamless token transfers on the B2-Network. It provides functionality to transfer both native B2-BTC and ERC20 tokens using secure wallet operations. ## Installation From 615a299bcb64e64e370167e060986f0cb73d841e Mon Sep 17 00:00:00 2001 From: Afanti <127061691+threewebcode@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:55:26 +0800 Subject: [PATCH 09/16] chore: update env file --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 7d996460a09..1bd36d1460b 100644 --- a/.env.example +++ b/.env.example @@ -433,4 +433,4 @@ ALLORA_API_KEY=UP-f8db7d6558ab432ca0d92716 # Allora API key ALLORA_CHAIN_SLUG=testnet # must be one of mainnet, testnet. If not specified, it will use testnet by default # B2 Network -B2_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # Private key of the B2 Network account to use for the agent \ No newline at end of file +B2_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # Private key of the B2 Network account to use for the agent From 0d9cf53c5ad5e42ba04e0eb80644a8bd3d7e6aa1 Mon Sep 17 00:00:00 2001 From: goalongway Date: Wed, 8 Jan 2025 20:16:15 +0800 Subject: [PATCH 10/16] modify b2 plugin --- packages/plugin-b2/package.json | 4 +- packages/plugin-b2/src/actions/transfer.ts | 159 ++++++++++-------- packages/plugin-b2/src/plugins/index.ts | 6 +- packages/plugin-b2/src/providers/index.ts | 139 +++++++++++++++ packages/plugin-b2/src/providers/wallet.ts | 41 ----- packages/plugin-b2/src/tests/topics.test.ts | 29 ++++ packages/plugin-b2/src/tests/transfer.test.ts | 47 ++++++ packages/plugin-b2/src/tests/wallet.test.ts | 42 +++++ packages/plugin-b2/src/types/index.ts | 24 +++ packages/plugin-b2/src/utils/index.ts | 109 ++++++------ 10 files changed, 428 insertions(+), 172 deletions(-) create mode 100644 packages/plugin-b2/src/providers/index.ts delete mode 100644 packages/plugin-b2/src/providers/wallet.ts create mode 100644 packages/plugin-b2/src/tests/topics.test.ts create mode 100644 packages/plugin-b2/src/tests/transfer.test.ts create mode 100644 packages/plugin-b2/src/tests/wallet.test.ts create mode 100644 packages/plugin-b2/src/types/index.ts diff --git a/packages/plugin-b2/package.json b/packages/plugin-b2/package.json index 3d5ab7df4df..106f79e45e8 100644 --- a/packages/plugin-b2/package.json +++ b/packages/plugin-b2/package.json @@ -27,7 +27,9 @@ }, "scripts": { "build": "tsup src/index.ts --format esm --no-dts", - "lint": "eslint --fix --cache ." + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache .", + "test": "vitest run" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts index 264b66c4ecf..df60868dcf7 100644 --- a/packages/plugin-b2/src/actions/transfer.ts +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -2,6 +2,7 @@ import { Action, ActionExample, IAgentRuntime, + generateObjectDeprecated, Memory, State, HandlerCallback, @@ -12,10 +13,82 @@ import { Content, } from "@elizaos/core"; import { getTxReceipt, sendNativeAsset, sendToken } from "../utils"; -import { Address } from "viem"; +import { Address, Hash } from "viem"; import { validateB2NetworkConfig } from "../environment"; import { transferTemplate } from "../templates"; +import { B2WalletProvider } from "../providers"; +import { Transaction, TransferParams } from "../types"; +import { initWalletProvider } from "../providers"; +// Exported for tests +export class TransferAction { + + constructor(private walletProvider: B2WalletProvider) {} + + async transfer(params: TransferParams): Promise { + let txHash; + if (params.tokenAddress === "0x0000000000000000000000000000000000000000") { + txHash = await sendNativeAsset( + this.walletProvider, + params.recipient as Address, + params.amount as number + ); + } else { + txHash = await sendToken( + this.walletProvider, + params.tokenAddress as Address, + params.recipient as Address, + params.amount as number + ); + } + return { + hash: txHash, + from: this.walletProvider.getAddress(), + tokenAddress: params.tokenAddress, + recipient: params.recipient, + amount: params.amount, + }; + } + + async transferTxReceipt(tx: Hash) { + const receipt = await getTxReceipt(this.walletProvider, tx); + if (receipt.status === "success") { + return true; + } else { + return false; + } + } + + async isTransferContent(content: any): Promise { + elizaLogger.debug("Content for transfer", content); + return ( + typeof content.tokenAddress === "string" && + typeof content.recipient === "string" && + (typeof content.amount === "string" || + typeof content.amount === "number") + ); + } +} + + +export const buildTransferDetails = async ( + state: State, + runtime: IAgentRuntime, + wp: B2WalletProvider +): Promise => { + const context = composeContext({ + state, + template: transferTemplate, + }); + + const transferDetails = (await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + })) as TransferParams; + + return transferDetails; +}; export interface TransferContent extends Content { tokenAddress: string; @@ -23,21 +96,7 @@ export interface TransferContent extends Content { amount: string | number; } -function isTransferContent( - runtime: IAgentRuntime, - content: any -): content is TransferContent { - elizaLogger.debug("Content for transfer", content); - return ( - typeof content.tokenAddress === "string" && - typeof content.recipient === "string" && - (typeof content.amount === "string" || - typeof content.amount === "number") - ); -} - - -export default { +export const transferAction: Action = { name: "SEND_TOKEN", similes: [ "TRANSFER_TOKEN_ON_B2", @@ -61,17 +120,6 @@ export default { ) => { elizaLogger.log("Starting SEND_TOKEN handler..."); - // Validate transfer - if (message.content.source === "direct") { - // - } else { - callback?.({ - text: "i can't do that for you.", - content: { error: "Transfer not allowed" }, - }); - return false; - } - // Initialize or update state if (!state) { state = (await runtime.composeState(message)) as State; @@ -79,56 +127,26 @@ export default { state = await runtime.updateRecentMessageState(state); } + console.log("Transfer action handler called"); + const walletProvider = await initWalletProvider(runtime); + const action = new TransferAction(walletProvider); + // Compose transfer context - const transferContext = composeContext({ + const paramOptions = await buildTransferDetails( state, - template: transferTemplate, - }); - - // Generate transfer content - const content = await generateObject({ runtime, - context: transferContext, - modelClass: ModelClass.SMALL, - }); - - elizaLogger.debug("Transfer content:", content); + walletProvider + ); - // Validate transfer content - if (!isTransferContent(runtime, content)) { - elizaLogger.error("Invalid content for TRANSFER_TOKEN action."); - callback?.({ - text: "Unable to process transfer request. Invalid content provided.", - content: { error: "Invalid transfer content" }, - }); - return false; - } - - let tx; - if ( - content.tokenAddress === - "0x0000000000000000000000000000000000000000" - ) { - tx = await sendNativeAsset( - runtime, - content.recipient as Address, - content.amount as number - ); - } else { - tx = await sendToken( - runtime, - content.tokenAddress as Address, - content.recipient as Address, - content.amount as number - ); - } + elizaLogger.debug("Transfer paramOptions:", paramOptions); + let tx = await action.transfer(paramOptions); if (tx) { - const receipt = await getTxReceipt(runtime, tx); - if (receipt.status === "success") { + let result = await action.transferTxReceipt(tx.hash); + if (result) { callback?.({ text: "transfer successful", - content: { success: true, txHash: tx }, + content: { success: true, txHash: tx.hash }, }); } else { callback?.({ @@ -142,7 +160,6 @@ export default { content: { error: "Transfer failed" }, }); } - return true; }, examples: [ @@ -155,4 +172,4 @@ export default { }, ], ] as ActionExample[][], -} as Action; +}; diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts index a9f339bca74..98f350cb2e9 100644 --- a/packages/plugin-b2/src/plugins/index.ts +++ b/packages/plugin-b2/src/plugins/index.ts @@ -1,11 +1,11 @@ import { Plugin } from "@elizaos/core"; -import transfer from "../actions/transfer"; -import { b2WalletProvider } from "../providers/wallet"; +import { transferAction } from "../actions/transfer"; +import { b2WalletProvider } from "../providers"; export const b2Plugin: Plugin = { name: "b2 plugin", description: "B2 Network Plugin for Eliza", - actions: [transfer], + actions: [transferAction], providers: [b2WalletProvider], evaluators: [], services: [], diff --git a/packages/plugin-b2/src/providers/index.ts b/packages/plugin-b2/src/providers/index.ts new file mode 100644 index 00000000000..ef2e24e0908 --- /dev/null +++ b/packages/plugin-b2/src/providers/index.ts @@ -0,0 +1,139 @@ +import { + IAgentRuntime, + Memory, + Provider, + State, + elizaLogger, +} from "@elizaos/core"; +import { privateKeyToAccount } from "viem/accounts"; + +import { + formatUnits, + Address, + Chain, + Account, + WalletClient, + PrivateKeyAccount, + http, + createPublicClient, + createWalletClient, + PublicClient +} from "viem"; +import { getTokenBalance } from "../utils"; +import { TOKEN_ADDRESSES } from "../utils/constants"; +import { b2Network } from "../utils/chains"; + +export class B2WalletProvider implements Provider { + private account: PrivateKeyAccount; + + constructor(accountOrPrivateKey: PrivateKeyAccount | `0x${string}`) { + this.setAccount(accountOrPrivateKey); + } + + private setAccount = ( + accountOrPrivateKey: PrivateKeyAccount | `0x${string}` + ) => { + if (typeof accountOrPrivateKey === "string") { + this.account = privateKeyToAccount(accountOrPrivateKey); + } else { + this.account = accountOrPrivateKey; + } + }; + + getAccount(): Account { + return this.account; + } + + getAddress(): Address { + return this.account.address; + } + + getPublicClient( + ): PublicClient { + return createPublicClient({ + chain: b2Network, + transport: http(), + }); + } + + getWalletClient(): WalletClient { + const transport = http(b2Network.rpcUrls.default.http[0]); + const walletClient = createWalletClient({ + chain: b2Network, + transport, + account: this.account, + }); + return walletClient; + } + + async getDecimals(tokenAddress: Address) { + if (tokenAddress === "0x0000000000000000000000000000000000000000") { + return b2Network.nativeCurrency.decimals; + } + const publicClient = this.getPublicClient(); + const decimals = await publicClient.readContract({ + address: tokenAddress, + abi: [ + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function", + }, + ], + functionName: "decimals", + }); + return decimals; + } + +}; + +export const initWalletProvider = async (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); + if (!privateKey) { + throw new Error( + "B2_PRIVATE_KEY not found in environment variables" + ); + } + return new B2WalletProvider(privateKey); +}; + +export const b2WalletProvider: Provider = { + async get( + runtime: IAgentRuntime, + _message: Memory, + _state?: State + ): Promise { + elizaLogger.debug("walletProvider::get"); + const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); + if (!privateKey) { + throw new Error( + "B2_PRIVATE_KEY not found in environment variables" + ); + } + try { + const walletProvider = await initWalletProvider(runtime); + const account = walletProvider.getAccount(); + let output = `# Wallet Balances\n\n`; + output += `## Wallet Address\n\n\`${account.address}\`\n\n`; + + output += `## Latest Token Balances\n\n`; + for (const [token, address] of Object.entries(TOKEN_ADDRESSES)) { + const decimals = await walletProvider.getDecimals(address); + const balance = await getTokenBalance( + runtime, + address, + account.address + ); + output += `${token}: ${formatUnits(balance, decimals)}\n`; + } + output += `Note: These balances can be used at any time.\n\n`; + elizaLogger.debug("walletProvider::get output:", output); + return output; + } catch (error) { + console.error("Error in b2 wallet provider:", error); + return null; + } + } +}; \ No newline at end of file diff --git a/packages/plugin-b2/src/providers/wallet.ts b/packages/plugin-b2/src/providers/wallet.ts deleted file mode 100644 index df906a5b778..00000000000 --- a/packages/plugin-b2/src/providers/wallet.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - IAgentRuntime, - Memory, - Provider, - State, - elizaLogger, -} from "@elizaos/core"; -import { formatUnits } from "viem"; -import { getAccount, getDecimals, getTokenBalance } from "../utils"; -import { TOKEN_ADDRESSES } from "../utils/constants"; - -const b2WalletProvider: Provider = { - get: async (runtime: IAgentRuntime, _message: Memory, _state?: State) => { - elizaLogger.debug("walletProvider::get"); - const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); - if (!privateKey) { - throw new Error( - "B2_PRIVATE_KEY not found in environment variables" - ); - } - const account = getAccount(runtime); - let output = `# Wallet Balances\n\n`; - output += `## Wallet Address\n\n\`${account.address}\`\n\n`; - - output += `## Latest Token Balances\n\n`; - for (const [token, address] of Object.entries(TOKEN_ADDRESSES)) { - const decimals = await getDecimals(runtime, address); - const balance = await getTokenBalance( - runtime, - address, - account.address - ); - output += `${token}: ${formatUnits(balance, decimals)}\n`; - } - output += `Note: These balances can be used at any time.\n\n`; - elizaLogger.debug("walletProvider::get output:", output); - return output; - }, -}; - -export { b2WalletProvider }; diff --git a/packages/plugin-b2/src/tests/topics.test.ts b/packages/plugin-b2/src/tests/topics.test.ts new file mode 100644 index 00000000000..010fba4f640 --- /dev/null +++ b/packages/plugin-b2/src/tests/topics.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +// import { TopicsProvider } from "../../src/providers/topics"; +import { B2WalletProvider } from "../providers"; +import { Memory, State } from "@elizaos/core"; + +describe("TopicsProvider", () => { + let walletProvider: B2WalletProvider; + let mockRuntime; + + beforeEach(() => { + walletProvider = new B2WalletProvider(); + mockRuntime = { + getSetting: vi.fn(), + }; + + mockRuntime.getSetting.mockImplementation((key: string) => { + const settings = { + B2_PRIVATE_KEY: "test-private-key", + }; + return settings[key]; + }); + }); + + describe("Topics data integration", () => { + it("should format topics into expected string format", async () => { + + }); + }); +}); diff --git a/packages/plugin-b2/src/tests/transfer.test.ts b/packages/plugin-b2/src/tests/transfer.test.ts new file mode 100644 index 00000000000..1086a2872fd --- /dev/null +++ b/packages/plugin-b2/src/tests/transfer.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { initWalletProvider, B2WalletProvider } from "../providers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { Memory, State } from "@elizaos/core"; +import { TransferAction } from "../actions/transfer"; + +describe("Transfer Action", () => { + let mockRuntime; + beforeEach(() => { + vi.clearAllMocks(); + const pk = generatePrivateKey(); + mockRuntime = { + getSetting: vi.fn(), + }; + mockRuntime.getSetting.mockImplementation((key: string) => { + const settings = { + B2_PRIVATE_KEY: pk, + }; + return settings[key]; + }); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + describe("Constructor", () => { + it("should initialize with wallet provider",async () => { + const wp = await initWalletProvider(mockRuntime); + expect(wp).toBeDefined(); + }); + }); + describe("Transfer", () => { + let ta: TransferAction; + beforeEach(() => { + ta = new TransferAction(mockRuntime); + expect(ta).toBeDefined(); + // if (wp1) { + // ta1 = new TransferAction(wp1); + // receiverAddress = wp1.getAddress(); + // } + // else { + // receiverAddress = wp.getAddress(); + // } + }); + + }); +}); diff --git a/packages/plugin-b2/src/tests/wallet.test.ts b/packages/plugin-b2/src/tests/wallet.test.ts new file mode 100644 index 00000000000..cc9c3c6db05 --- /dev/null +++ b/packages/plugin-b2/src/tests/wallet.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { initWalletProvider, B2WalletProvider } from "../providers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { Memory, State } from "@elizaos/core"; + +describe("B2 Network Wallet Provider", () => { + let walletProvider: B2WalletProvider; + let mockRuntime; + + beforeEach(() => { + vi.clearAllMocks(); + const pk = generatePrivateKey(); + walletProvider = new B2WalletProvider(pk); + mockRuntime = { + getSetting: vi.fn(), + }; + mockRuntime.getSetting.mockImplementation((key: string) => { + const settings = { + B2_PRIVATE_KEY: pk, + }; + return settings[key]; + }); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + + describe("Constructor", () => { + it("new wallet provider", () => { + const pk = generatePrivateKey(); + const ta = new B2WalletProvider(pk); + expect(ta).toBeDefined(); + console.log(`wallet.address {}`, ta.getAddress()); + }); + it("init wallet provider",async () => { + const ta = await initWalletProvider(mockRuntime); + expect(ta).toBeDefined(); + console.log(`wallet.address {}`, ta.getAddress()); + }); + }); +}); diff --git a/packages/plugin-b2/src/types/index.ts b/packages/plugin-b2/src/types/index.ts new file mode 100644 index 00000000000..6c7a6d3386e --- /dev/null +++ b/packages/plugin-b2/src/types/index.ts @@ -0,0 +1,24 @@ +import type { Token } from "@lifi/types"; +import type { + Account, + Address, + Chain, + Hash, + HttpTransport, + PublicClient, + WalletClient, +} from "viem"; + +export interface Transaction { + hash: Hash; + from: Address; + tokenAddress: string; + recipient: string; + amount: string | number; +} + +export interface TransferParams { + tokenAddress: string; + recipient: string; + amount: string | number; +} \ No newline at end of file diff --git a/packages/plugin-b2/src/utils/index.ts b/packages/plugin-b2/src/utils/index.ts index be97fc65390..4a4dab90827 100644 --- a/packages/plugin-b2/src/utils/index.ts +++ b/packages/plugin-b2/src/utils/index.ts @@ -6,63 +6,64 @@ import { http, Address, parseUnits, + WalletClient, } from "viem"; -import { privateKeyToAccount } from "viem/accounts"; import { b2Network } from "./chains"; +import { B2WalletProvider } from "../providers"; -export const getAccount = (runtime: IAgentRuntime) => { - const privateKey = - runtime.getSetting("B2_PRIVATE_KEY") || - process.env.B2_PRIVATE_KEY; - return privateKeyToAccount(`0x${privateKey.replace("0x", "")}`); -}; +// export const getAccount = (runtime: IAgentRuntime) => { +// const privateKey = +// runtime.getSetting("B2_PRIVATE_KEY") || +// process.env.B2_PRIVATE_KEY; +// return privateKeyToAccount(`0x${privateKey.replace("0x", "")}`); +// }; -export const getPublicClient = (_runtime: IAgentRuntime) => { - return createPublicClient({ - chain: b2Network, - transport: http(), - }); -}; +// export const getPublicClient = (_runtime: IAgentRuntime) => { +// return createPublicClient({ +// chain: b2Network, +// transport: http(), +// }); +// }; -export const getWalletClient = (runtime: IAgentRuntime) => { - return createWalletClient({ - account: getAccount(runtime), - chain: b2Network, - transport: http(), - }); -}; +// export const getWalletClient = (runtime: IAgentRuntime) => { +// return createWalletClient({ +// account: getAccount(runtime), +// chain: b2Network, +// transport: http(), +// }); +// }; -export const getTxReceipt = async (runtime: IAgentRuntime, tx: Hash) => { - const publicClient = getPublicClient(runtime); +export const getTxReceipt = async (walletProvider: B2WalletProvider, tx: Hash) => { + const publicClient = walletProvider.getPublicClient(); const receipt = await publicClient.waitForTransactionReceipt({ hash: tx, }); return receipt; }; -export const getDecimals = async ( - runtime: IAgentRuntime, - tokenAddress: Address -) => { - if (tokenAddress === "0x0000000000000000000000000000000000000000") { - return b2Network.nativeCurrency.decimals; - } - const publicClient = getPublicClient(runtime); - const decimals = await publicClient.readContract({ - address: tokenAddress, - abi: [ - { - inputs: [], - name: "decimals", - outputs: [{ internalType: "uint8", name: "", type: "uint8" }], - stateMutability: "view", - type: "function", - }, - ], - functionName: "decimals", - }); - return decimals; -}; +// export const getDecimals = async ( +// runtime: IAgentRuntime, +// tokenAddress: Address +// ) => { +// if (tokenAddress === "0x0000000000000000000000000000000000000000") { +// return b2Network.nativeCurrency.decimals; +// } +// const publicClient = getPublicClient(runtime); +// const decimals = await publicClient.readContract({ +// address: tokenAddress, +// abi: [ +// { +// inputs: [], +// name: "decimals", +// outputs: [{ internalType: "uint8", name: "", type: "uint8" }], +// stateMutability: "view", +// type: "function", +// }, +// ], +// functionName: "decimals", +// }); +// return decimals; +// }; export const getNativeBalance = async ( runtime: IAgentRuntime, @@ -110,15 +111,12 @@ export const getTokenBalance = async ( }; export const sendNativeAsset = async ( - runtime: IAgentRuntime, + walletProvider: B2WalletProvider, recipient: Address, amount: number ) => { - const walletClient = getWalletClient(runtime); - const decimals = await getDecimals( - runtime, - "0x0000000000000000000000000000000000000000" - ); + const decimals = await walletProvider.getDecimals("0x0000000000000000000000000000000000000000"); + const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.sendTransaction({ to: recipient, value: parseUnits(amount.toString(), decimals), @@ -127,17 +125,16 @@ export const sendNativeAsset = async ( }; export const sendToken = async ( - runtime: IAgentRuntime, + walletProvider: B2WalletProvider, tokenAddress: Address, recipient: Address, amount: number ) => { - const decimals = await getDecimals(runtime, tokenAddress); - const publicClient = getPublicClient(runtime); - + const decimals = await walletProvider.getDecimals(tokenAddress); + const publicClient = walletProvider.getPublicClient(); try { const { result, request } = await publicClient.simulateContract({ - account: getAccount(runtime), + account: walletProvider.getAccount(), address: tokenAddress, abi: [ { From 972cc5df690d77b754a78f705c9f59687062c655 Mon Sep 17 00:00:00 2001 From: goalongway Date: Wed, 8 Jan 2025 21:41:06 +0800 Subject: [PATCH 11/16] modify b2 plugin --- packages/plugin-b2/src/actions/transfer.ts | 103 ++++++-------- packages/plugin-b2/src/plugins/index.ts | 4 +- packages/plugin-b2/src/providers/index.ts | 98 ++++++++++++-- packages/plugin-b2/src/tests/topics.test.ts | 29 ---- packages/plugin-b2/src/tests/transfer.test.ts | 91 ++++++++----- packages/plugin-b2/src/tests/wallet.test.ts | 8 +- packages/plugin-b2/src/types/index.ts | 5 - packages/plugin-b2/src/utils/index.ts | 126 +++--------------- 8 files changed, 214 insertions(+), 250 deletions(-) delete mode 100644 packages/plugin-b2/src/tests/topics.test.ts diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts index df60868dcf7..1c8a7bc1111 100644 --- a/packages/plugin-b2/src/actions/transfer.ts +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -8,46 +8,48 @@ import { HandlerCallback, elizaLogger, composeContext, - generateObject, ModelClass, - Content, } from "@elizaos/core"; import { getTxReceipt, sendNativeAsset, sendToken } from "../utils"; import { Address, Hash } from "viem"; import { validateB2NetworkConfig } from "../environment"; import { transferTemplate } from "../templates"; -import { B2WalletProvider } from "../providers"; +import { WalletProvider } from "../providers"; import { Transaction, TransferParams } from "../types"; import { initWalletProvider } from "../providers"; // Exported for tests export class TransferAction { - constructor(private walletProvider: B2WalletProvider) {} + constructor(private walletProvider: WalletProvider) {} async transfer(params: TransferParams): Promise { - let txHash; - if (params.tokenAddress === "0x0000000000000000000000000000000000000000") { - txHash = await sendNativeAsset( - this.walletProvider, - params.recipient as Address, - params.amount as number - ); - } else { - txHash = await sendToken( - this.walletProvider, - params.tokenAddress as Address, - params.recipient as Address, - params.amount as number - ); + try { + let txHash; + if (params.tokenAddress === "0x0000000000000000000000000000000000000000") { + txHash = await sendNativeAsset( + this.walletProvider, + params.recipient as Address, + params.amount as number + ); + } else { + txHash = await sendToken( + this.walletProvider, + params.tokenAddress as Address, + params.recipient as Address, + params.amount as number + ); + } + return { + hash: txHash, + from: this.walletProvider.getAddress(), + tokenAddress: params.tokenAddress, + recipient: params.recipient, + amount: params.amount, + }; + } catch(error) { + throw new Error(`Transfer failed: ${error.message}`); } - return { - hash: txHash, - from: this.walletProvider.getAddress(), - tokenAddress: params.tokenAddress, - recipient: params.recipient, - amount: params.amount, - }; } async transferTxReceipt(tx: Hash) { @@ -59,41 +61,24 @@ export class TransferAction { } } - async isTransferContent(content: any): Promise { - elizaLogger.debug("Content for transfer", content); - return ( - typeof content.tokenAddress === "string" && - typeof content.recipient === "string" && - (typeof content.amount === "string" || - typeof content.amount === "number") - ); - } -} - - -export const buildTransferDetails = async ( - state: State, - runtime: IAgentRuntime, - wp: B2WalletProvider -): Promise => { - const context = composeContext({ - state, - template: transferTemplate, - }); - - const transferDetails = (await generateObjectDeprecated({ - runtime, - context, - modelClass: ModelClass.SMALL, - })) as TransferParams; + async buildTransferDetails( + state: State, + runtime: IAgentRuntime, + wp: WalletProvider + ): Promise { + const context = composeContext({ + state, + template: transferTemplate, + }); - return transferDetails; -}; + const transferDetails = (await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + })) as TransferParams; -export interface TransferContent extends Content { - tokenAddress: string; - recipient: string; - amount: string | number; + return transferDetails; + } } export const transferAction: Action = { @@ -132,7 +117,7 @@ export const transferAction: Action = { const action = new TransferAction(walletProvider); // Compose transfer context - const paramOptions = await buildTransferDetails( + const paramOptions = await action.buildTransferDetails( state, runtime, walletProvider diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts index 98f350cb2e9..508e3c518f7 100644 --- a/packages/plugin-b2/src/plugins/index.ts +++ b/packages/plugin-b2/src/plugins/index.ts @@ -1,12 +1,12 @@ import { Plugin } from "@elizaos/core"; import { transferAction } from "../actions/transfer"; -import { b2WalletProvider } from "../providers"; +import { walletProvider } from "../providers"; export const b2Plugin: Plugin = { name: "b2 plugin", description: "B2 Network Plugin for Eliza", actions: [transferAction], - providers: [b2WalletProvider], + providers: [walletProvider], evaluators: [], services: [], clients: [], diff --git a/packages/plugin-b2/src/providers/index.ts b/packages/plugin-b2/src/providers/index.ts index ef2e24e0908..5ee6cf48587 100644 --- a/packages/plugin-b2/src/providers/index.ts +++ b/packages/plugin-b2/src/providers/index.ts @@ -19,11 +19,10 @@ import { createWalletClient, PublicClient } from "viem"; -import { getTokenBalance } from "../utils"; import { TOKEN_ADDRESSES } from "../utils/constants"; import { b2Network } from "../utils/chains"; -export class B2WalletProvider implements Provider { +export class WalletProvider implements Provider { private account: PrivateKeyAccount; constructor(accountOrPrivateKey: PrivateKeyAccount | `0x${string}`) { @@ -40,6 +39,49 @@ export class B2WalletProvider implements Provider { } }; + async getNativeBalance ( + owner: Address + ) { + const publicClient = this.getPublicClient(); + const balance = await publicClient.getBalance({ + address: owner, + }); + return balance; + }; + + async getTokenBalance ( + tokenAddress: Address, + owner: Address + ) { + if (tokenAddress === "0x0000000000000000000000000000000000000000") { + return this.getNativeBalance(owner); + } + const publicClient = this.getPublicClient(); + const balance = await publicClient.readContract({ + address: tokenAddress, + abi: [ + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { internalType: "uint256", name: "", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + ], + functionName: "balanceOf", + args: [owner], + }); + return balance; + }; + getAccount(): Account { return this.account; } @@ -48,8 +90,7 @@ export class B2WalletProvider implements Provider { return this.account.address; } - getPublicClient( - ): PublicClient { + getPublicClient(): PublicClient { return createPublicClient({ chain: b2Network, transport: http(), @@ -87,6 +128,48 @@ export class B2WalletProvider implements Provider { return decimals; } + async get( + runtime: IAgentRuntime, + _message: Memory, + _state?: State + ): Promise { + elizaLogger.debug("walletProvider::get"); + try { + const privateKey = runtime.getSetting("B2_PRIVATE_KEY"); + if (!privateKey) { + throw new Error( + "B2_PRIVATE_KEY not found in environment variables" + ); + } + let accountAddress; + if (this.account) { + accountAddress = this.getAddress(); + } else { + const walletProvider = await initWalletProvider(runtime); + accountAddress = walletProvider.getAddress(); + } + + let output = `# Wallet Balances\n\n`; + output += `## Wallet Address\n\n\`${accountAddress}\`\n\n`; + + output += `## Latest Token Balances\n\n`; + for (const [token, address] of Object.entries(TOKEN_ADDRESSES)) { + const decimals = await this.getDecimals(address); + const balance = await this.getTokenBalance( + address, + accountAddress, + ); + output += `${token}: ${formatUnits(balance, decimals)}\n`; + } + output += `Note: These balances can be used at any time.\n\n`; + elizaLogger.debug("walletProvider::get output:", output); + return output; + } catch (error) { + console.error("Error in b2 wallet provider:", error); + return null; + } + } + }; export const initWalletProvider = async (runtime: IAgentRuntime) => { @@ -96,10 +179,10 @@ export const initWalletProvider = async (runtime: IAgentRuntime) => { "B2_PRIVATE_KEY not found in environment variables" ); } - return new B2WalletProvider(privateKey); + return new WalletProvider(privateKey); }; -export const b2WalletProvider: Provider = { +export const walletProvider: Provider = { async get( runtime: IAgentRuntime, _message: Memory, @@ -121,8 +204,7 @@ export const b2WalletProvider: Provider = { output += `## Latest Token Balances\n\n`; for (const [token, address] of Object.entries(TOKEN_ADDRESSES)) { const decimals = await walletProvider.getDecimals(address); - const balance = await getTokenBalance( - runtime, + const balance = await walletProvider.getTokenBalance( address, account.address ); diff --git a/packages/plugin-b2/src/tests/topics.test.ts b/packages/plugin-b2/src/tests/topics.test.ts deleted file mode 100644 index 010fba4f640..00000000000 --- a/packages/plugin-b2/src/tests/topics.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; -// import { TopicsProvider } from "../../src/providers/topics"; -import { B2WalletProvider } from "../providers"; -import { Memory, State } from "@elizaos/core"; - -describe("TopicsProvider", () => { - let walletProvider: B2WalletProvider; - let mockRuntime; - - beforeEach(() => { - walletProvider = new B2WalletProvider(); - mockRuntime = { - getSetting: vi.fn(), - }; - - mockRuntime.getSetting.mockImplementation((key: string) => { - const settings = { - B2_PRIVATE_KEY: "test-private-key", - }; - return settings[key]; - }); - }); - - describe("Topics data integration", () => { - it("should format topics into expected string format", async () => { - - }); - }); -}); diff --git a/packages/plugin-b2/src/tests/transfer.test.ts b/packages/plugin-b2/src/tests/transfer.test.ts index 1086a2872fd..b29d166fe20 100644 --- a/packages/plugin-b2/src/tests/transfer.test.ts +++ b/packages/plugin-b2/src/tests/transfer.test.ts @@ -1,47 +1,74 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { initWalletProvider, B2WalletProvider } from "../providers"; -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; -import { Memory, State } from "@elizaos/core"; +import { describe, it, expect, beforeEach } from "vitest"; +import { generatePrivateKey } from "viem/accounts"; +import { getEnvVariable } from "@elizaos/core"; + import { TransferAction } from "../actions/transfer"; +import { WalletProvider } from "../providers"; +import { TransferParams } from "../types"; describe("Transfer Action", () => { - let mockRuntime; - beforeEach(() => { - vi.clearAllMocks(); - const pk = generatePrivateKey(); - mockRuntime = { - getSetting: vi.fn(), - }; - mockRuntime.getSetting.mockImplementation((key: string) => { - const settings = { - B2_PRIVATE_KEY: pk, - }; - return settings[key]; - }); - }); + let wp: WalletProvider; + let wp1: WalletProvider; - afterEach(() => { - vi.clearAllTimers(); + beforeEach(async () => { + const pk = generatePrivateKey(); + const pk1 = getEnvVariable("ARTHERA_PRIVATE_KEY") as `0x${string}`; + wp = new WalletProvider(pk); + console.log(wp.getAddress()); + if (pk1) { + wp1 = new WalletProvider(pk1); + } }); describe("Constructor", () => { - it("should initialize with wallet provider",async () => { - const wp = await initWalletProvider(mockRuntime); - expect(wp).toBeDefined(); + it("should initialize with wallet provider", () => { + const ta = new TransferAction(wp); + + expect(ta).toBeDefined(); }); }); describe("Transfer", () => { let ta: TransferAction; + let ta1: TransferAction; + let receiverAddress: `0x${string}`; + beforeEach(() => { - ta = new TransferAction(mockRuntime); - expect(ta).toBeDefined(); - // if (wp1) { - // ta1 = new TransferAction(wp1); - // receiverAddress = wp1.getAddress(); - // } - // else { - // receiverAddress = wp.getAddress(); - // } + ta = new TransferAction(wp); + if (wp1) { + ta1 = new TransferAction(wp1); + receiverAddress = wp1.getAddress(); + } + else { + receiverAddress = wp.getAddress(); + } + }); + + it("throws if not enough gas", async () => { + const params = { + tokenAddress: "0x0000000000000000000000000000000000000000", + recipient: receiverAddress, + amount: "1", + } as TransferParams; + await expect( + ta.transfer(params) + ).rejects.toThrow( + "Transfer failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account." + ); }); + if (wp1) { + console.log("----------------------------------------------"); + it("transfers tokens", async () => { + const params = { + tokenAddress: "0x0000000000000000000000000000000000000000", + recipient: receiverAddress, + amount: "0.001", + } as TransferParams; + const tx = await ta1.transfer(params); + expect(tx).toBeDefined(); + expect(tx.from).toEqual(wp1.getAddress()); + expect(tx.recipient).toEqual(receiverAddress); + expect(tx.amount).toEqual(1000000000000000n); + }); + } }); }); diff --git a/packages/plugin-b2/src/tests/wallet.test.ts b/packages/plugin-b2/src/tests/wallet.test.ts index cc9c3c6db05..8b28d9b0470 100644 --- a/packages/plugin-b2/src/tests/wallet.test.ts +++ b/packages/plugin-b2/src/tests/wallet.test.ts @@ -1,16 +1,16 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { initWalletProvider, B2WalletProvider } from "../providers"; +import { initWalletProvider, WalletProvider } from "../providers"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { Memory, State } from "@elizaos/core"; describe("B2 Network Wallet Provider", () => { - let walletProvider: B2WalletProvider; + let walletProvider: WalletProvider; let mockRuntime; beforeEach(() => { vi.clearAllMocks(); const pk = generatePrivateKey(); - walletProvider = new B2WalletProvider(pk); + walletProvider = new WalletProvider(pk); mockRuntime = { getSetting: vi.fn(), }; @@ -29,7 +29,7 @@ describe("B2 Network Wallet Provider", () => { describe("Constructor", () => { it("new wallet provider", () => { const pk = generatePrivateKey(); - const ta = new B2WalletProvider(pk); + const ta = new WalletProvider(pk); expect(ta).toBeDefined(); console.log(`wallet.address {}`, ta.getAddress()); }); diff --git a/packages/plugin-b2/src/types/index.ts b/packages/plugin-b2/src/types/index.ts index 6c7a6d3386e..a89fe2ddaa3 100644 --- a/packages/plugin-b2/src/types/index.ts +++ b/packages/plugin-b2/src/types/index.ts @@ -1,12 +1,7 @@ import type { Token } from "@lifi/types"; import type { - Account, Address, - Chain, Hash, - HttpTransport, - PublicClient, - WalletClient, } from "viem"; export interface Transaction { diff --git a/packages/plugin-b2/src/utils/index.ts b/packages/plugin-b2/src/utils/index.ts index 4a4dab90827..6b4b48a6da6 100644 --- a/packages/plugin-b2/src/utils/index.ts +++ b/packages/plugin-b2/src/utils/index.ts @@ -1,39 +1,13 @@ import { IAgentRuntime, elizaLogger } from "@elizaos/core"; import { - createPublicClient, - createWalletClient, Hash, - http, Address, parseUnits, - WalletClient, } from "viem"; import { b2Network } from "./chains"; -import { B2WalletProvider } from "../providers"; +import { WalletProvider } from "../providers"; -// export const getAccount = (runtime: IAgentRuntime) => { -// const privateKey = -// runtime.getSetting("B2_PRIVATE_KEY") || -// process.env.B2_PRIVATE_KEY; -// return privateKeyToAccount(`0x${privateKey.replace("0x", "")}`); -// }; - -// export const getPublicClient = (_runtime: IAgentRuntime) => { -// return createPublicClient({ -// chain: b2Network, -// transport: http(), -// }); -// }; - -// export const getWalletClient = (runtime: IAgentRuntime) => { -// return createWalletClient({ -// account: getAccount(runtime), -// chain: b2Network, -// transport: http(), -// }); -// }; - -export const getTxReceipt = async (walletProvider: B2WalletProvider, tx: Hash) => { +export const getTxReceipt = async (walletProvider: WalletProvider, tx: Hash) => { const publicClient = walletProvider.getPublicClient(); const receipt = await publicClient.waitForTransactionReceipt({ hash: tx, @@ -41,77 +15,8 @@ export const getTxReceipt = async (walletProvider: B2WalletProvider, tx: Hash) = return receipt; }; -// export const getDecimals = async ( -// runtime: IAgentRuntime, -// tokenAddress: Address -// ) => { -// if (tokenAddress === "0x0000000000000000000000000000000000000000") { -// return b2Network.nativeCurrency.decimals; -// } -// const publicClient = getPublicClient(runtime); -// const decimals = await publicClient.readContract({ -// address: tokenAddress, -// abi: [ -// { -// inputs: [], -// name: "decimals", -// outputs: [{ internalType: "uint8", name: "", type: "uint8" }], -// stateMutability: "view", -// type: "function", -// }, -// ], -// functionName: "decimals", -// }); -// return decimals; -// }; - -export const getNativeBalance = async ( - runtime: IAgentRuntime, - owner: Address -) => { - const publicClient = getPublicClient(runtime); - const balance = await publicClient.getBalance({ - address: owner, - }); - return balance; -}; - -export const getTokenBalance = async ( - runtime: IAgentRuntime, - tokenAddress: Address, - owner: Address -) => { - if (tokenAddress === "0x0000000000000000000000000000000000000000") { - return getNativeBalance(runtime, owner); - } - const publicClient = getPublicClient(runtime); - const balance = await publicClient.readContract({ - address: tokenAddress, - abi: [ - { - inputs: [ - { - internalType: "address", - name: "account", - type: "address", - }, - ], - name: "balanceOf", - outputs: [ - { internalType: "uint256", name: "", type: "uint256" }, - ], - stateMutability: "view", - type: "function", - }, - ], - functionName: "balanceOf", - args: [owner], - }); - return balance; -}; - export const sendNativeAsset = async ( - walletProvider: B2WalletProvider, + walletProvider: WalletProvider, recipient: Address, amount: number ) => { @@ -125,7 +30,7 @@ export const sendNativeAsset = async ( }; export const sendToken = async ( - walletProvider: B2WalletProvider, + walletProvider: WalletProvider, tokenAddress: Address, recipient: Address, amount: number @@ -171,8 +76,7 @@ export const sendToken = async ( } elizaLogger.debug("Request:", request); - - const walletClient = getWalletClient(runtime); + const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); elizaLogger.log("Transaction:", tx); return tx as Hash; @@ -183,16 +87,16 @@ export const sendToken = async ( }; export const approve = async ( - runtime: IAgentRuntime, + walletProvider: WalletProvider, tokenAddress: Address, spender: Address, amount: number ) => { try { - const decimals = await getDecimals(runtime, tokenAddress); - const publicClient = getPublicClient(runtime); + const decimals = await walletProvider.getDecimals(tokenAddress); + const publicClient = walletProvider.getPublicClient(); const { result, request } = await publicClient.simulateContract({ - account: getAccount(runtime), + account: walletProvider.getAccount(), address: tokenAddress, abi: [ { @@ -230,7 +134,7 @@ export const approve = async ( elizaLogger.debug("Request:", request); - const walletClient = getWalletClient(runtime); + const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); elizaLogger.log("Transaction:", tx); return tx; @@ -241,16 +145,16 @@ export const approve = async ( }; export const deposit = async ( - runtime: IAgentRuntime, + walletProvider: WalletProvider, depositTokenAddress: Address, strategyAddress: Address, amount: number ) => { try { - const decimals = await getDecimals(runtime, depositTokenAddress); - const publicClient = getPublicClient(runtime); + const decimals = await walletProvider.getDecimals(depositTokenAddress); + const publicClient = walletProvider.getPublicClient(); const { _result, request } = await publicClient.simulateContract({ - account: getAccount(runtime), + account: walletProvider.getAccount(), address: strategyAddress, abi: [ { @@ -277,7 +181,7 @@ export const deposit = async ( elizaLogger.debug("Request:", request); - const walletClient = getWalletClient(runtime); + const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); elizaLogger.log("Transaction:", tx); return tx; From 5be7eb4489193ea5a6c95975fe59f61bcb71168a Mon Sep 17 00:00:00 2001 From: goalongway Date: Thu, 9 Jan 2025 07:42:06 +0800 Subject: [PATCH 12/16] modify b2 plugin --- packages/plugin-b2/src/plugins/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts index 508e3c518f7..074691bb68f 100644 --- a/packages/plugin-b2/src/plugins/index.ts +++ b/packages/plugin-b2/src/plugins/index.ts @@ -3,7 +3,7 @@ import { transferAction } from "../actions/transfer"; import { walletProvider } from "../providers"; export const b2Plugin: Plugin = { - name: "b2 plugin", + name: "b2", description: "B2 Network Plugin for Eliza", actions: [transferAction], providers: [walletProvider], From fc0dc5cde5628b0fa61163ab254469316a3fbea9 Mon Sep 17 00:00:00 2001 From: goalongway Date: Sat, 11 Jan 2025 17:27:37 +0800 Subject: [PATCH 13/16] modify b2 plugin --- packages/plugin-b2/src/actions/stake.ts | 150 ++++++++++++++++++ packages/plugin-b2/src/actions/transfer.ts | 6 +- packages/plugin-b2/src/actions/unstake.ts | 150 ++++++++++++++++++ packages/plugin-b2/src/actions/withdraw.ts | 150 ++++++++++++++++++ packages/plugin-b2/src/plugins/index.ts | 5 +- packages/plugin-b2/src/templates/index.ts | 47 +----- packages/plugin-b2/src/templates/stake.ts | 17 ++ packages/plugin-b2/src/templates/transfer.ts | 40 +++++ packages/plugin-b2/src/templates/unstake.ts | 18 +++ packages/plugin-b2/src/templates/withdraw.ts | 15 ++ packages/plugin-b2/src/tests/stake.test.ts | 45 ++++++ packages/plugin-b2/src/tests/transfer.test.ts | 2 +- packages/plugin-b2/src/tests/unstake.test.ts | 45 ++++++ packages/plugin-b2/src/tests/wallet.test.ts | 2 - packages/plugin-b2/src/tests/withdraw.test.ts | 41 +++++ packages/plugin-b2/src/types/index.ts | 13 +- packages/plugin-b2/src/utils/constants.ts | 3 + packages/plugin-b2/src/utils/index.ts | 136 ++++++++++++++++ 18 files changed, 837 insertions(+), 48 deletions(-) create mode 100644 packages/plugin-b2/src/actions/stake.ts create mode 100644 packages/plugin-b2/src/actions/unstake.ts create mode 100644 packages/plugin-b2/src/actions/withdraw.ts create mode 100644 packages/plugin-b2/src/templates/stake.ts create mode 100644 packages/plugin-b2/src/templates/transfer.ts create mode 100644 packages/plugin-b2/src/templates/unstake.ts create mode 100644 packages/plugin-b2/src/templates/withdraw.ts create mode 100644 packages/plugin-b2/src/tests/stake.test.ts create mode 100644 packages/plugin-b2/src/tests/unstake.test.ts create mode 100644 packages/plugin-b2/src/tests/withdraw.test.ts diff --git a/packages/plugin-b2/src/actions/stake.ts b/packages/plugin-b2/src/actions/stake.ts new file mode 100644 index 00000000000..83ccddf80b5 --- /dev/null +++ b/packages/plugin-b2/src/actions/stake.ts @@ -0,0 +1,150 @@ +import { + Action, + ActionExample, + IAgentRuntime, + generateObjectDeprecated, + Memory, + State, + HandlerCallback, + elizaLogger, + composeContext, + ModelClass, +} from "@elizaos/core"; +import { getTxReceipt, sendNativeAsset, sendToken, depositBTC } from "../utils"; +import { Address, Hash } from "viem"; +import { validateB2NetworkConfig } from "../environment"; +import { stakeTemplate } from "../templates"; +import { WalletProvider } from "../providers"; +import { StakeParams } from "../types"; +import { initWalletProvider } from "../providers"; +import { FARM_ADDRESS } from "../utils/constants"; + +// Exported for tests +export class StakeAction { + + constructor(private walletProvider: WalletProvider) {} + + async stake(params: StakeParams): Promise { + try { + let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == 0 ) { + throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); + } + let txHash = await depositBTC( + this.walletProvider, + FARM_ADDRESS, + params.amount, + ); + return txHash; + } catch(error) { + console.log(`Stake failed: ${error.message}`); + throw new Error(`Stake failed: ${error.message}`); + // throw new Error(`Stake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) + } + } + + async txReceipt(tx: Hash) { + const receipt = await getTxReceipt(this.walletProvider, tx); + if (receipt.status === "success") { + return true; + } else { + return false; + } + } + + async buildStakeDetails( + state: State, + runtime: IAgentRuntime, + wp: WalletProvider + ): Promise { + const context = composeContext({ + state, + template: stakeTemplate, + }); + + const stakeDetails = (await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + })) as StakeParams; + + return stakeDetails; + } +} + +export const stakeAction: Action = { + name: "STAKE", + similes: [ + "STAKE_BTC_ON_B2", + "STAKE_NATIVE_BTC_ON_B2", + "DEPOSIT_BTC_ON_B2", + "DEPOSIT_NATIVE_BTC_ON_B2", + ], + validate: async (runtime: IAgentRuntime, _message: Memory) => { + await validateB2NetworkConfig(runtime); + return true; + }, + description: + "stake native btc.", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + elizaLogger.log("Starting STAKE handler..."); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + console.log("stake action handler called"); + const walletProvider = await initWalletProvider(runtime); + const action = new StakeAction(walletProvider); + + // Compose stake context + const paramOptions = await action.buildStakeDetails( + state, + runtime, + walletProvider + ); + + elizaLogger.debug("Stake paramOptions:", paramOptions); + + let txHash = await action.stake(paramOptions); + if (txHash) { + let result = await action.txReceipt(txHash); + if (result) { + callback?.({ + text: "stake successful", + content: { success: true, txHash: txHash }, + }); + } else { + callback?.({ + text: "stake failed", + content: { error: "Stake failed" }, + }); + } + } else { + callback?.({ + text: "stake failed", + content: { error: "Stake failed" }, + }); + } + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Stake 1 B2-BTC", + }, + }, + ], + ] as ActionExample[][], +}; diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts index 1c8a7bc1111..10532b7231e 100644 --- a/packages/plugin-b2/src/actions/transfer.ts +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -52,7 +52,7 @@ export class TransferAction { } } - async transferTxReceipt(tx: Hash) { + async txReceipt(tx: Hash) { const receipt = await getTxReceipt(this.walletProvider, tx); if (receipt.status === "success") { return true; @@ -87,7 +87,7 @@ export const transferAction: Action = { "TRANSFER_TOKEN_ON_B2", "TRANSFER_TOKENS_ON_B2", "SEND_TOKENS_ON_B2", - "SEND_AVAX_ON_B2", + "SEND_B2BTC_ON_B2", "PAY_ON_B2", ], validate: async (runtime: IAgentRuntime, _message: Memory) => { @@ -127,7 +127,7 @@ export const transferAction: Action = { let tx = await action.transfer(paramOptions); if (tx) { - let result = await action.transferTxReceipt(tx.hash); + let result = await action.txReceipt(tx.hash); if (result) { callback?.({ text: "transfer successful", diff --git a/packages/plugin-b2/src/actions/unstake.ts b/packages/plugin-b2/src/actions/unstake.ts new file mode 100644 index 00000000000..27ae8898432 --- /dev/null +++ b/packages/plugin-b2/src/actions/unstake.ts @@ -0,0 +1,150 @@ +import { + Action, + ActionExample, + IAgentRuntime, + generateObjectDeprecated, + Memory, + State, + HandlerCallback, + elizaLogger, + composeContext, + ModelClass, +} from "@elizaos/core"; +import { getTxReceipt, unstake } from "../utils"; +import { Hash } from "viem"; +import { validateB2NetworkConfig } from "../environment"; +import { unstakeTemplate } from "../templates"; +import { WalletProvider } from "../providers"; +import { UnstakeParams } from "../types"; +import { initWalletProvider } from "../providers"; +import { FARM_ADDRESS } from "../utils/constants"; + +// Exported for tests +export class UnstakeAction { + + constructor(private walletProvider: WalletProvider) {} + + async unstake(params: UnstakeParams): Promise { + try { + let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == 0 ) { + throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); + } + let txHash = await unstake( + this.walletProvider, + FARM_ADDRESS, + params.amount, + ); + return txHash; + } catch(error) { + console.log(`Unstake failed: ${error.message}`); + throw new Error(`Unstake failed: ${error.message}`); + // throw new Error(`Unstake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) + } + } + + async txReceipt(tx: Hash) { + const receipt = await getTxReceipt(this.walletProvider, tx); + if (receipt.status === "success") { + return true; + } else { + return false; + } + } + + async buildUnstakeDetails( + state: State, + runtime: IAgentRuntime, + wp: WalletProvider + ): Promise { + const context = composeContext({ + state, + template: unstakeTemplate, + }); + + const unstakeDetails = (await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + })) as UnstakeParams; + + return unstakeDetails; + } +} + +export const unstakeAction: Action = { + name: "UNSTAKE", + similes: [ + "UNSTAKE_BTC_ON_B2", + "UNSTAKE_NATIVE_BTC_ON_B2", + "UNSTAKE_BTC_ON_B2", + "UNSTAKE_NATIVE_BTC_ON_B2", + ], + validate: async (runtime: IAgentRuntime, _message: Memory) => { + await validateB2NetworkConfig(runtime); + return true; + }, + description: + "unstake native btc.", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + elizaLogger.log("Starting UNSTAKE handler..."); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + console.log("unstake action handler called"); + const walletProvider = await initWalletProvider(runtime); + const action = new UnstakeAction(walletProvider); + + // Compose unstake context + const paramOptions = await action.buildUnstakeDetails( + state, + runtime, + walletProvider + ); + + elizaLogger.debug("Unstake paramOptions:", paramOptions); + + let txHash = await action.unstake(paramOptions); + if (txHash) { + let result = await action.txReceipt(txHash); + if (result) { + callback?.({ + text: "unstake successful", + content: { success: true, txHash: txHash }, + }); + } else { + callback?.({ + text: "unstake failed", + content: { error: "Unstake failed" }, + }); + } + } else { + callback?.({ + text: "unstake failed", + content: { error: "Unstake failed" }, + }); + } + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Unstake 1 B2-BTC", + }, + }, + ], + ] as ActionExample[][], +}; diff --git a/packages/plugin-b2/src/actions/withdraw.ts b/packages/plugin-b2/src/actions/withdraw.ts new file mode 100644 index 00000000000..e02e2d86051 --- /dev/null +++ b/packages/plugin-b2/src/actions/withdraw.ts @@ -0,0 +1,150 @@ +import { + Action, + ActionExample, + IAgentRuntime, + generateObjectDeprecated, + Memory, + State, + HandlerCallback, + elizaLogger, + composeContext, + ModelClass, +} from "@elizaos/core"; +import { getTxReceipt, withdraw } from "../utils"; +import { Hash } from "viem"; +import { validateB2NetworkConfig } from "../environment"; +import { withdrawTemplate } from "../templates"; +import { WalletProvider } from "../providers"; +import { WithdrawParams } from "../types"; +import { initWalletProvider } from "../providers"; +import { FARM_ADDRESS } from "../utils/constants"; + +// Exported for tests +export class WithdrawAction { + + constructor(private walletProvider: WalletProvider) {} + + async withdraw(_params: WithdrawParams): Promise { + try { + let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == 0 ) { + throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); + } + let txHash = await withdraw( + this.walletProvider, + FARM_ADDRESS, + ); + return txHash; + } catch(error) { + console.log(`Withdraw failed: ${error.message}`); + throw new Error(`Withdraw failed: ${error.message}`); + // throw new Error(`Withdraw failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) + } + } + + async txReceipt(tx: Hash) { + const receipt = await getTxReceipt(this.walletProvider, tx); + if (receipt.status === "success") { + return true; + } else { + return false; + } + } + + async buildWithdrawDetails( + state: State, + runtime: IAgentRuntime, + wp: WalletProvider + ): Promise { + const context = composeContext({ + state, + template: withdrawTemplate, + }); + + const withdrawDetails = (await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + })) as WithdrawParams; + + return withdrawDetails; + } +} + +export const withdrawAction: Action = { + name: "WITHDRAW", + similes: [ + "WITHDRAW_BTC_ON_B2", + "WITHDRAW_NATIVE_BTC_ON_B2", + "WITHDRAW_BTC_ON_B2", + "WITHDRAW_NATIVE_BTC_ON_B2", + ], + validate: async (runtime: IAgentRuntime, _message: Memory) => { + await validateB2NetworkConfig(runtime); + return true; + }, + description: + "withdraw native btc.", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + elizaLogger.log("Starting WITHDRAW handler..."); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + console.log("withdraw action handler called"); + const walletProvider = await initWalletProvider(runtime); + const action = new WithdrawAction(walletProvider); + + // TODO check 是否需要Withdraw + // Compose unstake context + const paramOptions = await action.buildWithdrawDetails( + state, + runtime, + walletProvider + ); + + elizaLogger.debug("Unstake paramOptions:", paramOptions); + + let txHash = await action.withdraw(paramOptions); + if (txHash) { + let result = await action.txReceipt(txHash); + if (result) { + callback?.({ + text: "withdraw successful", + content: { success: true, txHash: txHash }, + }); + } else { + callback?.({ + text: "withdraw failed", + content: { error: "Withdraw failed" }, + }); + } + } else { + callback?.({ + text: "withdraw failed", + content: { error: "Withdraw failed" }, + }); + } + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Withdraw B2-BTC", + }, + }, + ], + ] as ActionExample[][], +}; diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts index 074691bb68f..ae20fc532e4 100644 --- a/packages/plugin-b2/src/plugins/index.ts +++ b/packages/plugin-b2/src/plugins/index.ts @@ -1,11 +1,14 @@ import { Plugin } from "@elizaos/core"; import { transferAction } from "../actions/transfer"; +import { stakeAction } from "../actions/stake"; +import { unstakeAction } from "../actions/unstake"; +import { withdrawAction } from "../actions/withdraw"; import { walletProvider } from "../providers"; export const b2Plugin: Plugin = { name: "b2", description: "B2 Network Plugin for Eliza", - actions: [transferAction], + actions: [transferAction, stakeAction, unstakeAction, withdrawAction], providers: [walletProvider], evaluators: [], services: [], diff --git a/packages/plugin-b2/src/templates/index.ts b/packages/plugin-b2/src/templates/index.ts index 09ab4471ee6..238a506a122 100644 --- a/packages/plugin-b2/src/templates/index.ts +++ b/packages/plugin-b2/src/templates/index.ts @@ -1,44 +1,11 @@ -import { TOKEN_ADDRESSES } from "../utils/constants"; - -const transferTemplate = `Respond with a JSON markdown block containing only the extracted values -- Use null for any values that cannot be determined. -- Use address zero for native B2-BTC transfers. - -Example response for a 10 uBTC transfer: -\`\`\`json -{ - "tokenAddress": "0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2", - "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", - "amount": "10" -} -\`\`\` - -Example response for a 0.1 B2-BTC transfer: -\`\`\`json -{ - "tokenAddress": "0x0000000000000000000000000000000000000000", - "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", - "amount": "0.1" -} -\`\`\` - -## Token Addresses - -${Object.entries(TOKEN_ADDRESSES) - .map(([key, value]) => `- ${key}: ${value}`) - .join("\n")} - -## Recent Messages - -{{recentMessages}} - -Given the recent messages, extract the following information about the requested token transfer: -- Token contract address -- Recipient wallet address -- Amount to transfer - -Respond with a JSON markdown block containing only the extracted values.`; +import { transferTemplate } from "./transfer"; +import { stakeTemplate } from "./stake"; +import { unstakeTemplate } from "./unstake"; +import {withdrawTemplate} from "./withdraw" export { transferTemplate, + stakeTemplate, + unstakeTemplate, + withdrawTemplate }; \ No newline at end of file diff --git a/packages/plugin-b2/src/templates/stake.ts b/packages/plugin-b2/src/templates/stake.ts new file mode 100644 index 00000000000..1bb0fb9bbad --- /dev/null +++ b/packages/plugin-b2/src/templates/stake.ts @@ -0,0 +1,17 @@ +export const stakeTemplate = `Respond with a JSON markdown block containing only the extracted values + +Example response for a 10 B2-BTC stake: +\`\`\`json +{ + "amount": "10" +} +\`\`\` + +## Recent Messages + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested stake: +- Amount to stake + +Respond with a JSON markdown block containing only the extracted values.`; \ No newline at end of file diff --git a/packages/plugin-b2/src/templates/transfer.ts b/packages/plugin-b2/src/templates/transfer.ts new file mode 100644 index 00000000000..d9e23698940 --- /dev/null +++ b/packages/plugin-b2/src/templates/transfer.ts @@ -0,0 +1,40 @@ +import { TOKEN_ADDRESSES } from "../utils/constants"; + +export const transferTemplate = `Respond with a JSON markdown block containing only the extracted values +- Use null for any values that cannot be determined. +- Use address zero for native B2-BTC transfers. + +Example response for a 10 uBTC transfer: +\`\`\`json +{ + "tokenAddress": "0x796e4D53067FF374B89b2Ac101ce0c1f72ccaAc2", + "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", + "amount": "10" +} +\`\`\` + +Example response for a 0.1 B2-BTC transfer: +\`\`\`json +{ + "tokenAddress": "0x0000000000000000000000000000000000000000", + "recipient": "0x4f9e2dc50B4Cd632CC2D24edaBa3Da2a9338832a", + "amount": "0.1" +} +\`\`\` + +## Token Addresses + +${Object.entries(TOKEN_ADDRESSES) + .map(([key, value]) => `- ${key}: ${value}`) + .join("\n")} + +## Recent Messages + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested token transfer: +- Token contract address +- Recipient wallet address +- Amount to transfer + +Respond with a JSON markdown block containing only the extracted values.`; \ No newline at end of file diff --git a/packages/plugin-b2/src/templates/unstake.ts b/packages/plugin-b2/src/templates/unstake.ts new file mode 100644 index 00000000000..35f2ee0418e --- /dev/null +++ b/packages/plugin-b2/src/templates/unstake.ts @@ -0,0 +1,18 @@ +export const unstakeTemplate = `Respond with a JSON markdown block containing only the extracted values +- Use null for any values that cannot be determined. + +Example response for a 5 B2-BTC unstake: +\`\`\`json +{ + "amount": "5" +} +\`\`\` + +## Recent Messages + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested unstake: +- Amount to unstake + +Respond with a JSON markdown block containing only the extracted values.`; diff --git a/packages/plugin-b2/src/templates/withdraw.ts b/packages/plugin-b2/src/templates/withdraw.ts new file mode 100644 index 00000000000..751df636ef4 --- /dev/null +++ b/packages/plugin-b2/src/templates/withdraw.ts @@ -0,0 +1,15 @@ +export const withdrawTemplate = `Respond with a JSON markdown block containing only the extracted values +- This action does not require any parameters. + +Example response for a withdraw request: +\`\`\`json +{} +\`\`\` + +## Recent Messages + +{{recentMessages}} + +Given the recent messages, confirm the request for withdrawal. + +Respond with a JSON markdown block containing only an empty object.`; diff --git a/packages/plugin-b2/src/tests/stake.test.ts b/packages/plugin-b2/src/tests/stake.test.ts new file mode 100644 index 00000000000..e71eaa01caf --- /dev/null +++ b/packages/plugin-b2/src/tests/stake.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { generatePrivateKey } from "viem/accounts"; +import { getEnvVariable } from "@elizaos/core"; + +import { StakeAction } from "../actions/stake"; +import { WalletProvider } from "../providers"; +import { StakeParams } from "../types"; + +describe("Stake Action", () => { + let wp: WalletProvider; + + beforeEach(async () => { + const pk = generatePrivateKey(); + wp = new WalletProvider(pk); + }); + describe("Constructor", () => { + it("should initialize with stake action", () => { + const sa = new StakeAction(wp); + expect(sa).toBeDefined(); + }); + }); + describe("Stake", () => { + let sa: StakeAction; + beforeEach(() => { + sa = new StakeAction(wp); + expect(sa).toBeDefined(); + }); + it("should initialize with stake action", () => { + const sa = new StakeAction(wp); + expect(sa).toBeDefined(); + }); + + it("throws if not enough gas", async () => { + const params = { + amount: "1", + } as StakeParams; + await expect( + sa.stake(params) + ).rejects.toThrow( + "Stake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account." + ); + }); + + }); +}); diff --git a/packages/plugin-b2/src/tests/transfer.test.ts b/packages/plugin-b2/src/tests/transfer.test.ts index b29d166fe20..72ecd9e0725 100644 --- a/packages/plugin-b2/src/tests/transfer.test.ts +++ b/packages/plugin-b2/src/tests/transfer.test.ts @@ -20,7 +20,7 @@ describe("Transfer Action", () => { } }); describe("Constructor", () => { - it("should initialize with wallet provider", () => { + it("should initialize with transfer action", () => { const ta = new TransferAction(wp); expect(ta).toBeDefined(); diff --git a/packages/plugin-b2/src/tests/unstake.test.ts b/packages/plugin-b2/src/tests/unstake.test.ts new file mode 100644 index 00000000000..d1aa5e21364 --- /dev/null +++ b/packages/plugin-b2/src/tests/unstake.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { generatePrivateKey } from "viem/accounts"; +import { getEnvVariable } from "@elizaos/core"; + +import { UnstakeAction } from "../actions/unstake"; +import { WalletProvider } from "../providers"; +import { TransferParams, UnstakeParams } from "../types"; + +describe("Unstake Action", () => { + let wp: WalletProvider; + + beforeEach(async () => { + const pk = generatePrivateKey(); + wp = new WalletProvider(pk); + }); + describe("Constructor", () => { + it("should initialize with unstake action", () => { + const ua = new UnstakeAction(wp); + expect(ua).toBeDefined(); + }); + }); + describe("Unstake", () => { + let ua: UnstakeAction; + + beforeEach(() => { + ua = new UnstakeAction(wp); + expect(ua).toBeDefined(); + }); + it("should initialize with unstake action", () => { + const ua = new UnstakeAction(wp); + expect(ua).toBeDefined(); + }); + + it("throws if not enough gas", async () => { + const params = { + amount: "1", + } as UnstakeParams; + await expect( + ua.unstake(params) + ).rejects.toThrow( + "Unstake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account." + ); + }); + }); +}); diff --git a/packages/plugin-b2/src/tests/wallet.test.ts b/packages/plugin-b2/src/tests/wallet.test.ts index 8b28d9b0470..c1429545e4f 100644 --- a/packages/plugin-b2/src/tests/wallet.test.ts +++ b/packages/plugin-b2/src/tests/wallet.test.ts @@ -31,12 +31,10 @@ describe("B2 Network Wallet Provider", () => { const pk = generatePrivateKey(); const ta = new WalletProvider(pk); expect(ta).toBeDefined(); - console.log(`wallet.address {}`, ta.getAddress()); }); it("init wallet provider",async () => { const ta = await initWalletProvider(mockRuntime); expect(ta).toBeDefined(); - console.log(`wallet.address {}`, ta.getAddress()); }); }); }); diff --git a/packages/plugin-b2/src/tests/withdraw.test.ts b/packages/plugin-b2/src/tests/withdraw.test.ts new file mode 100644 index 00000000000..69404237479 --- /dev/null +++ b/packages/plugin-b2/src/tests/withdraw.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { generatePrivateKey } from "viem/accounts"; +import { WithdrawAction } from "../actions/withdraw"; +import { WalletProvider } from "../providers"; +import { WithdrawParams } from "../types"; + +describe("Withdraw Action", () => { + let wp: WalletProvider; + + beforeEach(async () => { + const pk = generatePrivateKey(); + wp = new WalletProvider(pk); + }); + describe("Constructor", () => { + it("should initialize with withdraw action", () => { + const wa = new WithdrawAction(wp); + expect(wa).toBeDefined(); + }); + }); + describe("Withdraw", () => { + let wa: WithdrawAction; + beforeEach(() => { + wa = new WithdrawAction(wp); + expect(wa).toBeDefined(); + }); + it("should initialize with withdraw action", () => { + wa = new WithdrawAction(wp); + expect(wa).toBeDefined(); + }); + it("throws if not enough gas", async () => { + const params = {} as WithdrawParams; + wa = new WithdrawAction(wp); + await expect( + wa.withdraw(params) + ).rejects.toThrow( + "Withdraw failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account." + ); + }); + + }); +}); diff --git a/packages/plugin-b2/src/types/index.ts b/packages/plugin-b2/src/types/index.ts index a89fe2ddaa3..97a6ac06590 100644 --- a/packages/plugin-b2/src/types/index.ts +++ b/packages/plugin-b2/src/types/index.ts @@ -16,4 +16,15 @@ export interface TransferParams { tokenAddress: string; recipient: string; amount: string | number; -} \ No newline at end of file +} + +export interface StakeParams { + amount: string | number; +} + +export interface UnstakeParams { + amount: string | number; +} + +export interface WithdrawParams { +} diff --git a/packages/plugin-b2/src/utils/constants.ts b/packages/plugin-b2/src/utils/constants.ts index 0ca874d1854..23fec41ee6e 100644 --- a/packages/plugin-b2/src/utils/constants.ts +++ b/packages/plugin-b2/src/utils/constants.ts @@ -7,6 +7,9 @@ const TOKEN_ADDRESSES: Record = { USDT: "0x681202351a488040Fa4FdCc24188AfB582c9DD62", }; +const FARM_ADDRESS: Address = "0xd5B5f1CA0fa5636ac54b0a0007BA374A1513346e"; + export { TOKEN_ADDRESSES, + FARM_ADDRESS, }; diff --git a/packages/plugin-b2/src/utils/index.ts b/packages/plugin-b2/src/utils/index.ts index 6b4b48a6da6..96b776d0ca3 100644 --- a/packages/plugin-b2/src/utils/index.ts +++ b/packages/plugin-b2/src/utils/index.ts @@ -3,6 +3,7 @@ import { Hash, Address, parseUnits, + encodeFunctionData, } from "viem"; import { b2Network } from "./chains"; import { WalletProvider } from "../providers"; @@ -144,6 +145,141 @@ export const approve = async ( } }; +export const depositBTC = async ( + walletProvider: WalletProvider, + farmAddress: Address, + amount: string | number +) => { + try { + const decimals = b2Network.nativeCurrency.decimals; + // const publicClient = walletProvider.getPublicClient(); + + const walletClient = walletProvider.getWalletClient(); + const data = encodeFunctionData({ + abi: [ + { + "inputs": [ + + ], + "name": "depositBTC", + "outputs": [ + + ], + "stateMutability": "payable", + "type": "function" + }, + ], + functionName: 'depositBTC', + args: [], + }); + const txHash = await walletClient.sendTransaction({ + account: walletProvider.getAddress(), + to: farmAddress, + data, + value: parseUnits(amount.toString(), decimals), + }); + + elizaLogger.log("Transaction hash:", txHash); + return txHash; + } catch (error) { + elizaLogger.error("Error depositBTC:", error); + return; + } +}; + +// function unstake(uint256 _pid, uint256 _amount) public {} +export const unstake = async ( + walletProvider: WalletProvider, + farmAddress: Address, + amount: string | number +) => { + try { + const BTC_PID = 0; + const decimals = b2Network.nativeCurrency.decimals; + const publicClient = walletProvider.getPublicClient(); + const { _result, request } = await publicClient.simulateContract({ + account: walletProvider.getAccount(), + address: farmAddress, + abi: [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "unstake", + "outputs": [ + + ], + "stateMutability": "nonpayable", + "type": "function" + }, + ], + functionName: "unstake", + args: [BigInt(BTC_PID), parseUnits(amount.toString(), decimals)], + }); + elizaLogger.debug("Request:", request); + + const walletClient = walletProvider.getWalletClient(); + const tx = await walletClient.writeContract(request); + elizaLogger.log("Transaction:", tx); + return tx; + } catch (error) { + elizaLogger.error("Error unstake:", error); + return; + } +}; + +// function withdraw(uint256 _pid) public {} +export const withdraw = async ( + walletProvider: WalletProvider, + farmAddress: Address, +) => { + try { + const BTC_PID = 0; + const publicClient = walletProvider.getPublicClient(); + const { _result, request } = await publicClient.simulateContract({ + account: walletProvider.getAccount(), + address: farmAddress, + abi: [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [ + + ], + "stateMutability": "nonpayable", + "type": "function" + }, + ], + functionName: "withdraw", + args: [BigInt(BTC_PID)], + }); + elizaLogger.debug("Request:", request); + + const walletClient = walletProvider.getWalletClient(); + const tx = await walletClient.writeContract(request); + elizaLogger.log("Transaction:", tx); + return tx; + } catch (error) { + elizaLogger.error("Error withdraw:", error); + return; + } +}; + export const deposit = async ( walletProvider: WalletProvider, depositTokenAddress: Address, From 46eb7f1ed521e27fb3612d9aab8bf30f5b03a180 Mon Sep 17 00:00:00 2001 From: goalongway Date: Mon, 13 Jan 2025 11:38:56 +0800 Subject: [PATCH 14/16] modify b2 plugin --- agent/src/index.ts | 2 + packages/plugin-b2/src/actions/stake.ts | 21 +++--- packages/plugin-b2/src/actions/transfer.ts | 17 +++-- packages/plugin-b2/src/actions/unstake.ts | 21 +++--- packages/plugin-b2/src/actions/withdraw.ts | 27 ++++---- packages/plugin-b2/src/providers/index.ts | 4 +- packages/plugin-b2/src/utils/index.ts | 77 +++++----------------- 7 files changed, 58 insertions(+), 111 deletions(-) diff --git a/agent/src/index.ts b/agent/src/index.ts index f529786934b..df057708906 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -46,6 +46,7 @@ import { aptosPlugin } from "@elizaos/plugin-aptos"; import { artheraPlugin } from "@elizaos/plugin-arthera"; import { availPlugin } from "@elizaos/plugin-avail"; import { avalanchePlugin } from "@elizaos/plugin-avalanche"; +import { b2Plugin } from "@elizaos/plugin-b2"; import { binancePlugin } from "@elizaos/plugin-binance"; import { advancedTradePlugin, @@ -818,6 +819,7 @@ export async function createAgent( getSecret(character, "ABSTRACT_PRIVATE_KEY") ? abstractPlugin : null, + getSecret(character, "B2_PRIVATE_KEY") ? b2Plugin: null, getSecret(character, "BINANCE_API_KEY") && getSecret(character, "BINANCE_SECRET_KEY") ? binancePlugin diff --git a/packages/plugin-b2/src/actions/stake.ts b/packages/plugin-b2/src/actions/stake.ts index 83ccddf80b5..abc47000722 100644 --- a/packages/plugin-b2/src/actions/stake.ts +++ b/packages/plugin-b2/src/actions/stake.ts @@ -26,20 +26,19 @@ export class StakeAction { async stake(params: StakeParams): Promise { try { - let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); - if ( balance == 0 ) { + const balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == BigInt(0) ) { throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); } - let txHash = await depositBTC( + const txHash = await depositBTC( this.walletProvider, FARM_ADDRESS, params.amount, ); return txHash; } catch(error) { - console.log(`Stake failed: ${error.message}`); + elizaLogger.error(`Stake failed: ${error.message}`); throw new Error(`Stake failed: ${error.message}`); - // throw new Error(`Stake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) } } @@ -55,7 +54,6 @@ export class StakeAction { async buildStakeDetails( state: State, runtime: IAgentRuntime, - wp: WalletProvider ): Promise { const context = composeContext({ state, @@ -85,7 +83,7 @@ export const stakeAction: Action = { return true; }, description: - "stake native btc.", + "stake B2-BTC.", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -93,7 +91,7 @@ export const stakeAction: Action = { _options: { [key: string]: unknown }, callback?: HandlerCallback ) => { - elizaLogger.log("Starting STAKE handler..."); + elizaLogger.debug("Starting STAKE handler..."); // Initialize or update state if (!state) { @@ -102,7 +100,7 @@ export const stakeAction: Action = { state = await runtime.updateRecentMessageState(state); } - console.log("stake action handler called"); + elizaLogger.debug("stake action handler called"); const walletProvider = await initWalletProvider(runtime); const action = new StakeAction(walletProvider); @@ -110,14 +108,13 @@ export const stakeAction: Action = { const paramOptions = await action.buildStakeDetails( state, runtime, - walletProvider ); elizaLogger.debug("Stake paramOptions:", paramOptions); - let txHash = await action.stake(paramOptions); + const txHash = await action.stake(paramOptions); if (txHash) { - let result = await action.txReceipt(txHash); + const result = await action.txReceipt(txHash); if (result) { callback?.({ text: "stake successful", diff --git a/packages/plugin-b2/src/actions/transfer.ts b/packages/plugin-b2/src/actions/transfer.ts index 10532b7231e..52106bc8cc9 100644 --- a/packages/plugin-b2/src/actions/transfer.ts +++ b/packages/plugin-b2/src/actions/transfer.ts @@ -17,7 +17,7 @@ import { transferTemplate } from "../templates"; import { WalletProvider } from "../providers"; import { Transaction, TransferParams } from "../types"; import { initWalletProvider } from "../providers"; - +import { TOKEN_ADDRESSES } from "../utils/constants" // Exported for tests export class TransferAction { @@ -25,8 +25,8 @@ export class TransferAction { async transfer(params: TransferParams): Promise { try { - let txHash; - if (params.tokenAddress === "0x0000000000000000000000000000000000000000") { + let txHash: Hash; + if (params.tokenAddress === TOKEN_ADDRESSES["B2-BTC"]) { txHash = await sendNativeAsset( this.walletProvider, params.recipient as Address, @@ -48,6 +48,7 @@ export class TransferAction { amount: params.amount, }; } catch(error) { + elizaLogger.error(`Transfer failed: ${error.message}`); throw new Error(`Transfer failed: ${error.message}`); } } @@ -64,7 +65,6 @@ export class TransferAction { async buildTransferDetails( state: State, runtime: IAgentRuntime, - wp: WalletProvider ): Promise { const context = composeContext({ state, @@ -103,7 +103,7 @@ export const transferAction: Action = { _options: { [key: string]: unknown }, callback?: HandlerCallback ) => { - elizaLogger.log("Starting SEND_TOKEN handler..."); + elizaLogger.debug("Starting SEND_TOKEN handler..."); // Initialize or update state if (!state) { @@ -112,7 +112,7 @@ export const transferAction: Action = { state = await runtime.updateRecentMessageState(state); } - console.log("Transfer action handler called"); + elizaLogger.debug("Transfer action handler called"); const walletProvider = await initWalletProvider(runtime); const action = new TransferAction(walletProvider); @@ -120,14 +120,13 @@ export const transferAction: Action = { const paramOptions = await action.buildTransferDetails( state, runtime, - walletProvider ); elizaLogger.debug("Transfer paramOptions:", paramOptions); - let tx = await action.transfer(paramOptions); + const tx = await action.transfer(paramOptions); if (tx) { - let result = await action.txReceipt(tx.hash); + const result = await action.txReceipt(tx.hash); if (result) { callback?.({ text: "transfer successful", diff --git a/packages/plugin-b2/src/actions/unstake.ts b/packages/plugin-b2/src/actions/unstake.ts index 27ae8898432..7e6f6fd58a0 100644 --- a/packages/plugin-b2/src/actions/unstake.ts +++ b/packages/plugin-b2/src/actions/unstake.ts @@ -26,20 +26,19 @@ export class UnstakeAction { async unstake(params: UnstakeParams): Promise { try { - let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); - if ( balance == 0 ) { + const balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == BigInt(0) ) { throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); } - let txHash = await unstake( + const txHash = await unstake( this.walletProvider, FARM_ADDRESS, params.amount, ); return txHash; } catch(error) { - console.log(`Unstake failed: ${error.message}`); + elizaLogger.error(`Unstake failed: ${error.message}`); throw new Error(`Unstake failed: ${error.message}`); - // throw new Error(`Unstake failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) } } @@ -55,7 +54,6 @@ export class UnstakeAction { async buildUnstakeDetails( state: State, runtime: IAgentRuntime, - wp: WalletProvider ): Promise { const context = composeContext({ state, @@ -85,7 +83,7 @@ export const unstakeAction: Action = { return true; }, description: - "unstake native btc.", + "unstake B2-BTC.", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -93,7 +91,7 @@ export const unstakeAction: Action = { _options: { [key: string]: unknown }, callback?: HandlerCallback ) => { - elizaLogger.log("Starting UNSTAKE handler..."); + elizaLogger.debug("Starting UNSTAKE handler..."); // Initialize or update state if (!state) { @@ -102,7 +100,7 @@ export const unstakeAction: Action = { state = await runtime.updateRecentMessageState(state); } - console.log("unstake action handler called"); + elizaLogger.debug("unstake action handler called"); const walletProvider = await initWalletProvider(runtime); const action = new UnstakeAction(walletProvider); @@ -110,14 +108,13 @@ export const unstakeAction: Action = { const paramOptions = await action.buildUnstakeDetails( state, runtime, - walletProvider ); elizaLogger.debug("Unstake paramOptions:", paramOptions); - let txHash = await action.unstake(paramOptions); + const txHash = await action.unstake(paramOptions); if (txHash) { - let result = await action.txReceipt(txHash); + const result = await action.txReceipt(txHash); if (result) { callback?.({ text: "unstake successful", diff --git a/packages/plugin-b2/src/actions/withdraw.ts b/packages/plugin-b2/src/actions/withdraw.ts index e02e2d86051..d1e2ecb9193 100644 --- a/packages/plugin-b2/src/actions/withdraw.ts +++ b/packages/plugin-b2/src/actions/withdraw.ts @@ -26,19 +26,18 @@ export class WithdrawAction { async withdraw(_params: WithdrawParams): Promise { try { - let balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); - if ( balance == 0 ) { + const balance = await this.walletProvider.getNativeBalance(this.walletProvider.getAddress()); + if ( balance == BigInt(0) ) { throw new Error(`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`); } - let txHash = await withdraw( + const txHash = await withdraw( this.walletProvider, FARM_ADDRESS, ); return txHash; } catch(error) { - console.log(`Withdraw failed: ${error.message}`); + elizaLogger.log(`Withdraw failed: ${error.message}`); throw new Error(`Withdraw failed: ${error.message}`); - // throw new Error(`Withdraw failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`) } } @@ -54,7 +53,6 @@ export class WithdrawAction { async buildWithdrawDetails( state: State, runtime: IAgentRuntime, - wp: WalletProvider ): Promise { const context = composeContext({ state, @@ -84,7 +82,7 @@ export const withdrawAction: Action = { return true; }, description: - "withdraw native btc.", + "withdraw B2-BTC.", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -92,7 +90,7 @@ export const withdrawAction: Action = { _options: { [key: string]: unknown }, callback?: HandlerCallback ) => { - elizaLogger.log("Starting WITHDRAW handler..."); + elizaLogger.debug("Starting WITHDRAW handler..."); // Initialize or update state if (!state) { @@ -101,23 +99,20 @@ export const withdrawAction: Action = { state = await runtime.updateRecentMessageState(state); } - console.log("withdraw action handler called"); + elizaLogger.debug("withdraw action handler called"); const walletProvider = await initWalletProvider(runtime); const action = new WithdrawAction(walletProvider); - // TODO check 是否需要Withdraw - // Compose unstake context - const paramOptions = await action.buildWithdrawDetails( + // Compose unstake context + const paramOptions = await action.buildWithdrawDetails( state, runtime, - walletProvider ); - elizaLogger.debug("Unstake paramOptions:", paramOptions); - let txHash = await action.withdraw(paramOptions); + const txHash = await action.withdraw(paramOptions); if (txHash) { - let result = await action.txReceipt(txHash); + const result = await action.txReceipt(txHash); if (result) { callback?.({ text: "withdraw successful", diff --git a/packages/plugin-b2/src/providers/index.ts b/packages/plugin-b2/src/providers/index.ts index 5ee6cf48587..92fd6a7cfc2 100644 --- a/packages/plugin-b2/src/providers/index.ts +++ b/packages/plugin-b2/src/providers/index.ts @@ -165,7 +165,7 @@ export class WalletProvider implements Provider { elizaLogger.debug("walletProvider::get output:", output); return output; } catch (error) { - console.error("Error in b2 wallet provider:", error); + elizaLogger.error("Error in b2 wallet provider:", error); return null; } } @@ -214,7 +214,7 @@ export const walletProvider: Provider = { elizaLogger.debug("walletProvider::get output:", output); return output; } catch (error) { - console.error("Error in b2 wallet provider:", error); + elizaLogger.error("Error in b2 wallet provider:", error); return null; } } diff --git a/packages/plugin-b2/src/utils/index.ts b/packages/plugin-b2/src/utils/index.ts index 96b776d0ca3..e4a1666c3cf 100644 --- a/packages/plugin-b2/src/utils/index.ts +++ b/packages/plugin-b2/src/utils/index.ts @@ -4,9 +4,11 @@ import { Address, parseUnits, encodeFunctionData, + SendTransactionParameters, } from "viem"; import { b2Network } from "./chains"; import { WalletProvider } from "../providers"; +import { TOKEN_ADDRESSES } from "./constants"; export const getTxReceipt = async (walletProvider: WalletProvider, tx: Hash) => { const publicClient = walletProvider.getPublicClient(); @@ -21,12 +23,14 @@ export const sendNativeAsset = async ( recipient: Address, amount: number ) => { - const decimals = await walletProvider.getDecimals("0x0000000000000000000000000000000000000000"); + const decimals = await walletProvider.getDecimals(TOKEN_ADDRESSES["B2-BTC"]); const walletClient = walletProvider.getWalletClient(); - const tx = await walletClient.sendTransaction({ + + const args = { to: recipient, value: parseUnits(amount.toString(), decimals), - }); + }; + const tx = await walletClient.sendTransaction(args); return tx as Hash; }; @@ -79,7 +83,7 @@ export const sendToken = async ( elizaLogger.debug("Request:", request); const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); - elizaLogger.log("Transaction:", tx); + elizaLogger.debug("Transaction:", tx); return tx as Hash; } catch (error) { elizaLogger.error("Error simulating contract:", error); @@ -132,12 +136,10 @@ export const approve = async ( if (!result) { throw new Error("Approve failed"); } - elizaLogger.debug("Request:", request); - const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); - elizaLogger.log("Transaction:", tx); + elizaLogger.debug("Transaction:", tx); return tx; } catch (error) { elizaLogger.error("Error approving:", error); @@ -172,14 +174,16 @@ export const depositBTC = async ( functionName: 'depositBTC', args: [], }); - const txHash = await walletClient.sendTransaction({ + + const args = { account: walletProvider.getAddress(), to: farmAddress, data, value: parseUnits(amount.toString(), decimals), - }); + }; + const txHash = await walletClient.sendTransaction(args); - elizaLogger.log("Transaction hash:", txHash); + elizaLogger.debug("Transaction hash:", txHash); return txHash; } catch (error) { elizaLogger.error("Error depositBTC:", error); @@ -229,7 +233,7 @@ export const unstake = async ( const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); - elizaLogger.log("Transaction:", tx); + elizaLogger.debug("Transaction:", tx); return tx; } catch (error) { elizaLogger.error("Error unstake:", error); @@ -272,57 +276,10 @@ export const withdraw = async ( const walletClient = walletProvider.getWalletClient(); const tx = await walletClient.writeContract(request); - elizaLogger.log("Transaction:", tx); + elizaLogger.debug("Transaction:", tx); return tx; } catch (error) { elizaLogger.error("Error withdraw:", error); return; } -}; - -export const deposit = async ( - walletProvider: WalletProvider, - depositTokenAddress: Address, - strategyAddress: Address, - amount: number -) => { - try { - const decimals = await walletProvider.getDecimals(depositTokenAddress); - const publicClient = walletProvider.getPublicClient(); - const { _result, request } = await publicClient.simulateContract({ - account: walletProvider.getAccount(), - address: strategyAddress, - abi: [ - { - inputs: [ - { - internalType: "uint256", - name: "_amount", - type: "uint256", - }, - ], - name: "deposit", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - ], - functionName: "deposit", - args: [parseUnits(amount.toString(), decimals)], - }); - - // if (!result) { - // throw new Error('Deposit failed') - // } - - elizaLogger.debug("Request:", request); - - const walletClient = walletProvider.getWalletClient(); - const tx = await walletClient.writeContract(request); - elizaLogger.log("Transaction:", tx); - return tx; - } catch (error) { - elizaLogger.error("Error depositing:", error); - return; - } -}; +}; \ No newline at end of file From 396bb8c52b008a94264d5fca5b98d3f8f09fc46f Mon Sep 17 00:00:00 2001 From: goalongway Date: Mon, 13 Jan 2025 14:25:02 +0800 Subject: [PATCH 15/16] modify b2 plugin --- packages/plugin-b2/src/providers/index.ts | 4 ++-- packages/plugin-b2/src/tests/transfer.test.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/plugin-b2/src/providers/index.ts b/packages/plugin-b2/src/providers/index.ts index 92fd6a7cfc2..5080ac925b9 100644 --- a/packages/plugin-b2/src/providers/index.ts +++ b/packages/plugin-b2/src/providers/index.ts @@ -53,7 +53,7 @@ export class WalletProvider implements Provider { tokenAddress: Address, owner: Address ) { - if (tokenAddress === "0x0000000000000000000000000000000000000000") { + if (tokenAddress === TOKEN_ADDRESSES["B2-BTC"]) { return this.getNativeBalance(owner); } const publicClient = this.getPublicClient(); @@ -108,7 +108,7 @@ export class WalletProvider implements Provider { } async getDecimals(tokenAddress: Address) { - if (tokenAddress === "0x0000000000000000000000000000000000000000") { + if (tokenAddress === TOKEN_ADDRESSES["B2-BTC"]) { return b2Network.nativeCurrency.decimals; } const publicClient = this.getPublicClient(); diff --git a/packages/plugin-b2/src/tests/transfer.test.ts b/packages/plugin-b2/src/tests/transfer.test.ts index 72ecd9e0725..a48a59ffeaf 100644 --- a/packages/plugin-b2/src/tests/transfer.test.ts +++ b/packages/plugin-b2/src/tests/transfer.test.ts @@ -5,6 +5,7 @@ import { getEnvVariable } from "@elizaos/core"; import { TransferAction } from "../actions/transfer"; import { WalletProvider } from "../providers"; import { TransferParams } from "../types"; +import { TOKEN_ADDRESSES } from "../utils/constants"; describe("Transfer Action", () => { let wp: WalletProvider; @@ -44,7 +45,7 @@ describe("Transfer Action", () => { it("throws if not enough gas", async () => { const params = { - tokenAddress: "0x0000000000000000000000000000000000000000", + tokenAddress: TOKEN_ADDRESSES["B2-BTC"], recipient: receiverAddress, amount: "1", } as TransferParams; @@ -59,7 +60,7 @@ describe("Transfer Action", () => { console.log("----------------------------------------------"); it("transfers tokens", async () => { const params = { - tokenAddress: "0x0000000000000000000000000000000000000000", + tokenAddress: TOKEN_ADDRESSES["B2-BTC"], recipient: receiverAddress, amount: "0.001", } as TransferParams; From daf7ac9d0e7819b6d8072a43c1867325a80a09db Mon Sep 17 00:00:00 2001 From: Afanti Afanti Date: Tue, 14 Jan 2025 00:02:34 +0000 Subject: [PATCH 16/16] fix: default empty value and resolve type error --- .env.example | 2 +- packages/plugin-b2/src/index.ts | 17 ++++++- packages/plugin-b2/src/plugins/index.ts | 16 ------- packages/plugin-b2/src/providers/index.ts | 9 ++-- pnpm-lock.yaml | 55 ++--------------------- 5 files changed, 25 insertions(+), 74 deletions(-) delete mode 100644 packages/plugin-b2/src/plugins/index.ts diff --git a/.env.example b/.env.example index e0950acfa3b..a668b9b2d5a 100644 --- a/.env.example +++ b/.env.example @@ -513,7 +513,7 @@ ALLORA_API_KEY= # Allora API key, format: UP-f8db7d6558ab432ca0d ALLORA_CHAIN_SLUG= # must be one of mainnet, testnet. If not specified, it will use testnet by default # B2 Network -B2_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # Private key of the B2 Network account to use for the agent +B2_PRIVATE_KEY= # Private key of the B2 Network account to use for the agent # Opacity zkTLS OPACITY_TEAM_ID=f309ac8ae8a9a14a7e62cd1a521b1c5f diff --git a/packages/plugin-b2/src/index.ts b/packages/plugin-b2/src/index.ts index 94f5fe6b8ba..7cc4d86f4d2 100644 --- a/packages/plugin-b2/src/index.ts +++ b/packages/plugin-b2/src/index.ts @@ -1,5 +1,18 @@ -import { b2Plugin } from "./plugins"; +import { Plugin } from "@elizaos/core"; +import { transferAction } from "./actions/transfer"; +import { stakeAction } from "./actions/stake"; +import { unstakeAction } from "./actions/unstake"; +import { withdrawAction } from "./actions/withdraw"; +import { walletProvider } from "./providers"; -export * from "./plugins"; +const b2Plugin: Plugin = { + name: "b2", + description: "B2 Network Plugin for Eliza", + actions: [transferAction, stakeAction, unstakeAction, withdrawAction], + providers: [walletProvider], + evaluators: [], + services: [], + clients: [], +}; export default b2Plugin; diff --git a/packages/plugin-b2/src/plugins/index.ts b/packages/plugin-b2/src/plugins/index.ts deleted file mode 100644 index ae20fc532e4..00000000000 --- a/packages/plugin-b2/src/plugins/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Plugin } from "@elizaos/core"; -import { transferAction } from "../actions/transfer"; -import { stakeAction } from "../actions/stake"; -import { unstakeAction } from "../actions/unstake"; -import { withdrawAction } from "../actions/withdraw"; -import { walletProvider } from "../providers"; - -export const b2Plugin: Plugin = { - name: "b2", - description: "B2 Network Plugin for Eliza", - actions: [transferAction, stakeAction, unstakeAction, withdrawAction], - providers: [walletProvider], - evaluators: [], - services: [], - clients: [], -}; \ No newline at end of file diff --git a/packages/plugin-b2/src/providers/index.ts b/packages/plugin-b2/src/providers/index.ts index 5080ac925b9..34149ee555c 100644 --- a/packages/plugin-b2/src/providers/index.ts +++ b/packages/plugin-b2/src/providers/index.ts @@ -6,7 +6,6 @@ import { elizaLogger, } from "@elizaos/core"; import { privateKeyToAccount } from "viem/accounts"; - import { formatUnits, Address, @@ -17,7 +16,9 @@ import { http, createPublicClient, createWalletClient, - PublicClient + PublicClient, + Transport, + RpcSchema, } from "viem"; import { TOKEN_ADDRESSES } from "../utils/constants"; import { b2Network } from "../utils/chains"; @@ -90,7 +91,7 @@ export class WalletProvider implements Provider { return this.account.address; } - getPublicClient(): PublicClient { + getPublicClient(): PublicClient { return createPublicClient({ chain: b2Network, transport: http(), @@ -179,7 +180,7 @@ export const initWalletProvider = async (runtime: IAgentRuntime) => { "B2_PRIVATE_KEY not found in environment variables" ); } - return new WalletProvider(privateKey); + return new WalletProvider(privateKey as `0x${string}`); }; export const walletProvider: Provider = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39d16e530ee..39593d21ab4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1496,13 +1496,12 @@ importers: '@elizaos/core': specifier: workspace:* version: link:../core - devDependencies: - '@types/node': - specifier: ^20.0.0 - version: 20.17.9 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 packages/plugin-binance: dependencies: @@ -3378,7 +3377,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@anush008/tokenizers-win32-x64-msvc@0.0.0': resolution: {integrity: sha512-/5kP0G96+Cr6947F0ZetXnmL31YCaN15dbNbh2NHg7TXXRwfqk95+JtPP5Q7v4jbR2xxAmuseBqB4H/V7zKWuw==} @@ -6278,79 +6276,67 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -7123,35 +7109,30 @@ packages: engines: {node: '>=18.0.0'} cpu: [arm64, x64] os: [linux] - libc: [glibc] '@node-llama-cpp/linux-armv7l@3.1.1': resolution: {integrity: sha512-fM5dr/wmL4R3rADUOa0SnFRYYpyzsxG0akhg+qBgh0/b1jGwGM6jzBQ9AuhsgfW9tjKdpvpM2GyUDh4tHGHN5w==} engines: {node: '>=18.0.0'} cpu: [arm, x64] os: [linux] - libc: [glibc] '@node-llama-cpp/linux-x64-cuda@3.1.1': resolution: {integrity: sha512-2435gpEI1M0gs8R0/EcpsXwkEtz1hu0waFJjQjck2KNE/Pz+DTw4T7JgWSkAS8uPS7XzzDGBXDuuK1er0ACq3w==} engines: {node: '>=18.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@node-llama-cpp/linux-x64-vulkan@3.1.1': resolution: {integrity: sha512-iSuaLDsmypv/eASW5DD09FMCCFRKgumpxdB9DHiG8oOd9CLFZle+fxql1TJx3zwtYRrsR7YkfWinjhILYfSIZw==} engines: {node: '>=18.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@node-llama-cpp/linux-x64@3.1.1': resolution: {integrity: sha512-s3VsBTrVWJgBfV5HruhfkTrnh5ykbuaCXvm1xRMpmMpnkL2tMMOrJJFJJIvrTurtGTxEvbO45O+wLU4wrVlQOw==} engines: {node: '>=18.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@node-llama-cpp/mac-arm64-metal@3.1.1': resolution: {integrity: sha512-VBVVZhF5zQ31BmmIN/dWG0k4VIWZGar8nDn0/64eLjufkdYGns6hAIssu6IDQ2HBfnq3ENgSgJTpXp7jq9Z2Ig==} @@ -7309,28 +7290,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@nx/nx-linux-arm64-musl@19.8.14': resolution: {integrity: sha512-ltty/PDWqkYgu/6Ye65d7v5nh3D6e0n3SacoKRs2Vtfz5oHYRUkSKizKIhEVfRNuHn3d9j8ve1fdcCN4SDPUBQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@nx/nx-linux-x64-gnu@19.8.14': resolution: {integrity: sha512-JzE3BuO9RCBVdgai18CCze6KUzG0AozE0TtYFxRokfSC05NU3nUhd/o62UsOl7s6Bqt/9nwrW7JC8pNDiCi9OQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@nx/nx-linux-x64-musl@19.8.14': resolution: {integrity: sha512-2rPvDOQLb7Wd6YiU88FMBiLtYco0dVXF99IJBRGAWv+WTI7MNr47OyK2ze+JOsbYY1d8aOGUvckUvCCZvZKEfg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@nx/nx-win32-arm64-msvc@19.8.14': resolution: {integrity: sha512-JxW+YPS+EjhUsLw9C6wtk9pQTG3psyFwxhab8y/dgk2s4AOTLyIm0XxgcCJVvB6i4uv+s1g0QXRwp6+q3IR6hg==} @@ -7694,42 +7671,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.0': resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.0': resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.0': resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.0': resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.0': resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.0': resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==} @@ -8482,28 +8453,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@reflink/reflink-linux-arm64-musl@0.1.19': resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@reflink/reflink-linux-x64-gnu@0.1.19': resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@reflink/reflink-linux-x64-musl@0.1.19': resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@reflink/reflink-win32-arm64-msvc@0.1.19': resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==} @@ -8681,61 +8648,51 @@ packages: resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.30.1': resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.30.1': resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.30.1': resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.30.1': resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.30.1': resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.30.1': resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.30.1': resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.30.1': resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.30.1': resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} @@ -9553,28 +9510,24 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [glibc] '@swc/core-linux-arm64-musl@1.10.7': resolution: {integrity: sha512-gp5Un3EbeSThBIh6oac5ZArV/CsSmTKj5jNuuUAuEsML3VF9vqPO+25VuxCvsRf/z3py+xOWRaN2HY/rjMeZog==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [musl] '@swc/core-linux-x64-gnu@1.10.7': resolution: {integrity: sha512-k/OxLLMl/edYqbZyUNg6/bqEHTXJT15l9WGqsl/2QaIGwWGvles8YjruQYQ9d4h/thSXLT9gd8bExU2D0N+bUA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [glibc] '@swc/core-linux-x64-musl@1.10.7': resolution: {integrity: sha512-XeDoURdWt/ybYmXLCEE8aSiTOzEn0o3Dx5l9hgt0IZEmTts7HgHHVeRgzGXbR4yDo0MfRuX5nE1dYpTmCz0uyA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [musl] '@swc/core-win32-arm64-msvc@1.10.7': resolution: {integrity: sha512-nYAbi/uLS+CU0wFtBx8TquJw2uIMKBnl04LBmiVoFrsIhqSl+0MklaA9FVMGA35NcxSJfcm92Prl2W2LfSnTqQ==}