Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: multiversx plugin #860

Merged
merged 15 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,7 @@ AWS_S3_UPLOAD_PATH=

# Deepgram
DEEPGRAM_API_KEY=

# MultiversX
MVX_PRIVATE_KEY= # Multiversx private key
MVX_NETWORK= # must be one of mainnet, devnet, testnet
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@ai16z/plugin-solana": "workspace:*",
"@ai16z/plugin-starknet": "workspace:*",
"@ai16z/plugin-tee": "workspace:*",
"@ai16z/plugin-multiversx": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
2 changes: 2 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { solanaPlugin } from "@ai16z/plugin-solana";
import { teePlugin, TEEMode } from "@ai16z/plugin-tee";
import { aptosPlugin, TransferAptosToken } from "@ai16z/plugin-aptos";
import { flowPlugin } from "@ai16z/plugin-flow";
import { multiversxPlugin } from '@ai16z/plugin-multiversx';
import Database from "better-sqlite3";
import fs from "fs";
import path from "path";
Expand Down Expand Up @@ -490,6 +491,7 @@ export async function createAgent(
? flowPlugin
: null,
getSecret(character, "APTOS_PRIVATE_KEY") ? aptosPlugin : null,
getSecret(character, 'MVX_PRIVATE_KEY') ? multiversxPlugin : null
].filter(Boolean),
providers: [],
actions: [],
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-multiversx/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
3 changes: 3 additions & 0 deletions packages/plugin-multiversx/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.global.mjs";

export default [...eslintGlobalConfig];
32 changes: 32 additions & 0 deletions packages/plugin-multiversx/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@ai16z/plugin-multiversx",
"version": "0.1.5-alpha.0",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@ai16z/eliza": "workspace:*",
"@multiversx/sdk-core": "13.15.0",
"bignumber.js": "9.1.2",
"browserify": "^17.0.1",
"esbuild-plugin-polyfill-node": "^0.3.0",
"esmify": "^2.1.1",
"tsup": "8.3.5",
"vitest": "2.1.5"
},
"devDependencies": {
"eslint": "9.13.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-vitest": "0.5.4"
},
"scripts": {
"build": "tsup --format esm --dts",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint . --fix"
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
12 changes: 12 additions & 0 deletions packages/plugin-multiversx/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# MultiversX Plugin

## Overview

This plugin aims to be the basis of all interactions with the MultiversX ecosystem.

## Adding a new action

Reuse providers and utilities from the existing actions where possible. Add more utilities if you think they will be useful for other actions.

1. Add the action to the `actions` directory. Try to follow the naming convention of the other actions.
2. Export the action in the `index.ts` file.
161 changes: 161 additions & 0 deletions packages/plugin-multiversx/src/actions/createToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import {
elizaLogger,
ActionExample,
Content,
HandlerCallback,
IAgentRuntime,
Memory,
ModelClass,
State,
generateObject,
composeContext,
type Action,
} from "@ai16z/eliza";
import { WalletProvider } from "../providers/wallet";
import { validateMultiversxConfig } from "../enviroment";

export interface CreateTokenContent extends Content {
tokenName: string;
tokenTicker: string;
decimals: string;
amount: string;
}

function isCreateTokenContent(
runtime: IAgentRuntime,
content: any
): content is CreateTokenContent {
console.log("Content for create token", content);
return content.tokenName && content.tokenTicker && content.amount;
}

const createTokenTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.

Example response:
\`\`\`json
{
"tokenName": "TEST",
"tokenTicker": "TST",
"amount: 100,
"decimals": 18
}
\`\`\`

{{recentMessages}}

Given the recent messages, extract the following information about the requested token creation:
- Token name
- Token ticker
- Amount
- Decimals

Respond with a JSON markdown block containing only the extracted values.`;

export default {
name: "CREATE_TOKEN",
similes: ["DEPLOY_TOKEN"],
validate: async (runtime: IAgentRuntime, message: Memory) => {
console.log("Validating config for user:", message.userId);
await validateMultiversxConfig(runtime);
return true;
},
description: "Create a new token.",
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: { [key: string]: unknown },
callback?: HandlerCallback
) => {
elizaLogger.log("Starting CREATE_TOKEN handler...");

// 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: createTokenTemplate,
});

// Generate transfer content
const content = await generateObject({
runtime,
context: transferContext,
modelClass: ModelClass.SMALL,
});

// Validate transfer content
if (!isCreateTokenContent(runtime, content)) {
console.error("Invalid content for TRANSFER_TOKEN action.");
if (callback) {
callback({
text: "Unable to process transfer request. Invalid content provided.",
content: { error: "Invalid transfer content" },
});
}
return false;
}

try {
const privateKey = runtime.getSetting("MVX_PRIVATE_KEY");
const network = runtime.getSetting("MVX_NETWORK");

const walletProvider = new WalletProvider(privateKey, network);

await walletProvider.createESDT({
tokenName: content.tokenName,
amount: content.amount,
decimals: Number(content.decimals) || 18,
tokenTicker: content.tokenTicker,
});
return true;
} catch (error) {
console.error("Error during creating token:", error);
if (callback) {
callback({
text: `Error creating token: ${error.message}`,
content: { error: error.message },
});
}
return false;
}
},

examples: [
[
{
user: "{{user1}}",
content: {
text: "Create a token XTREME with ticker XTR and supply of 10000",
action: "CREATE_TOKEN",
},
},
{
user: "{{user2}}",
content: {
text: "Succesfully created token.",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Create a token TEST with ticker TST, 18 decimals and su of 10000",
action: "CREATE_TOKEN",
},
},
{
user: "{{user2}}",
content: {
text: "Succesfully created token.",
},
},
],
] as ActionExample[][],
} as Action;
Loading
Loading