Skip to content

Commit

Permalink
init: initia plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
boohyunsik committed Jan 14, 2025
1 parent 0404e29 commit 2d2f922
Show file tree
Hide file tree
Showing 13 changed files with 547 additions and 4 deletions.
3 changes: 2 additions & 1 deletion agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@elizaos/plugin-goat": "workspace:*",
"@elizaos/plugin-lensNetwork": "workspace:*",
"@elizaos/plugin-icp": "workspace:*",
"@elizaos/plugin-initia": "workspace:*",
"@elizaos/plugin-image-generation": "workspace:*",
"@elizaos/plugin-movement": "workspace:*",
"@elizaos/plugin-massa": "workspace:*",
Expand Down Expand Up @@ -108,4 +109,4 @@
"ts-node": "10.9.2",
"tsup": "8.3.5"
}
}
}
2 changes: 2 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { fuelPlugin } from "@elizaos/plugin-fuel";
import { genLayerPlugin } from "@elizaos/plugin-genlayer";
import { giphyPlugin } from "@elizaos/plugin-giphy";
import { hyperliquidPlugin } from "@elizaos/plugin-hyperliquid";
import { initiaPlugin } from "@elizaos/plugin-initia";
import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
import { lensPlugin } from "@elizaos/plugin-lensNetwork";
import { letzAIPlugin } from "@elizaos/plugin-letzai";
Expand Down Expand Up @@ -897,6 +898,7 @@ export async function createAgent(
getSecret(character, "RESERVOIR_API_KEY")
? createNFTCollectionsPlugin()
: null,
getSecret(characeter, "INITIA_PRIVATE_KEY") ? initiaPlugin : null,
].filter(Boolean),
providers: [],
actions: [],
Expand Down
65 changes: 65 additions & 0 deletions packages/plugin-initia/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# @elizaos/plugin-initia

Initia plugin for Eliza OS.

## Overview

This plugin provides functionality to:

- Transfer INIT token

## Installation

```bash
pnpm install @elizaos/plugin-initia
```

## Configuration

```bash
INITIA_PRIVATE_KEY=0x1234...abcd
INITIA_NODE_URL=https://...
INITIA_CHAIN_ID=initiaion-2
```

## Features


### Token transfer

Send token to recipient:
```typescript
User: "Send 1 INIT to init14l3c2vxrdvu6y0sqykppey930s4kufsvt97aeu";
Assistant: "Sure! I am going to send 1 INIT to init14l3c2vxrdvu6y0sqykppey930s4kufsvt97aeu";
```

## Development

### Building

```bash
pnpm run build
```

### Testing

```bash
pnpm run test
```

## Dependencies

- `@initia/initia.js`: Official initia js SDK

## Future Enhancements

1. Execute other messages like delegate, undelegate.
2. Interacting with minitia.

## Contributing

Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information.

## License

This plugin is part of the Eliza project. See the main project repository for license information.
38 changes: 38 additions & 0 deletions packages/plugin-initia/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@elizaos/plugin-initia",
"version": "0.1.8+build.1",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"@elizaos/source": "./src/index.ts",
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"test": "vitest run"
},
"author": "boosik",
"license": "ISC",
"description": "",
"dependencies": {
"@elizaos/core": "workspace:*",
"@elizaos/plugin-trustdb": "workspace:*",
"@elizaos/plugin-tee": "workspace:*",
"@initia/initia.js": "^0.2.26"
},
"devDependencies": {
"@types/node": "^22.10.1"
}
}
144 changes: 144 additions & 0 deletions packages/plugin-initia/src/actions/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {
Action,
ActionExample,
composeContext,
Content, elizaLogger, generateObjectDeprecated,
HandlerCallback,
IAgentRuntime,
Memory, ModelClass,
State
} from "@elizaos/core";
import { WalletProvider } from "../providers/wallet";
import * as initia from '@initia/initia.js';
const { MsgSend } = initia;
import { KEY_PRIVATE_KEY } from "../types/const";

export interface TransferContent extends Content {
sender: string;
recipient: string;
amount: string;
}

function isTransferContent(runtime: IAgentRuntime, content: any): content is TransferContent {
return (
typeof content.sender === "string" && typeof content.recipient === "string" && typeof content.amount === "number"
);
}

const transferTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannt be determined.
Example response:
\`\`\`json
{
"sender": "init18sj3x80fdjc6gzfvwl7lf8sxcvuvqjpvcmp6np",
"recipient": "init1kdwzpz3wzvpdj90gtga4fw5zm9tk4cyrgnjauu",
"amount": "1000uinit",
}
\`\`\`
{{recnetMessages}}
Given the recent messages, extract the following information about the requested token transfer:
- Sender wallet address
- Recipient wallet address
- Amount to transfer
Respond with a JSON markdown block containing only the extracted values.`;

export default {
name: "SEND_TOKEN",
similes: [
"TRANSFER_TOKEN_ON_INITIA",
"TRANSFER_TOKENS_ON_INITIA",
"SEND_TOKEN_ON_INITIA",
"SEND_TOKENS_ON_INITIA",
"PAY_ON_INITIA"
],
description: "",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const privateKey = runtime.getSetting(KEY_PRIVATE_KEY);
return typeof privateKey === "string" && privateKey.startsWith("0x");
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: { [key: string]: unknown },
callback?: HandlerCallback
): Promise<boolean> => {
if (!state) {
state = (await runtime.composeState(message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
}

const transferContext = composeContext({
state,
template: transferTemplate,
});

const content = await generateObjectDeprecated({
runtime,
context: transferContext,
modelClass: ModelClass.LARGE,
});

if (!isTransferContent(runtime, content)) {
if (callback) {
callback({
text: "Unable to process transfer request. Invalid content provided.",
content: { error: "Invalid transfer content" }
});
}
return false;
}

try {
const walletProvider = new WalletProvider(runtime);
const msgSend = new MsgSend(
content.sender,
content.recipient,
content.amount,
);
const signedTx = await walletProvider.getWallet().createAndSignTx({
msgs: [msgSend],
memo: 'This transaction is made in ElizaOS',
});
const txResult = await walletProvider.sendTransaction(signedTx);
if (callback) {
callback({
text: `Successfully transferred INITIA.\n` +
`Transaction Hash: ${txResult.txhash}\n` +
`Sender: ${content.sender}\n` +
`Recipient: ${content.recipient}\n` +
`Amount: ${content.amount}`
});
}
return true;
} catch (e) {
elizaLogger.error("Failed to transfer INITIA:", e.message);
if (callback) {
callback({
text: `Failed to transfer INITIA: ${e.message}`
});
}
return false;
}
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "Hey send 1 INIT to init18sj3x80fdjc6gzfvwl7lf8sxcvuvqjpvcmp6np."
},
},
{
user: "{{user2}}",
content: {
text: "Sure! I am going to send 1 INIT to init18sj3x80fdjc6gzfvwl7lf8sxcvuvqjpvcmp6np."
}
}
]
] as ActionExample[][],
} as Action;
13 changes: 13 additions & 0 deletions packages/plugin-initia/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Plugin } from "@elizaos/core";
import transfer from "./actions/transfer.ts";
import { initiaWalletProvider } from "./providers/wallet.ts";

export const initiaPlugin: Plugin = {
name: "initiaPlugin",
description: "Initia Plugin for Eliza",
actions: [
transfer,
],
evaluators: [],
providers: [initiaWalletProvider],
};
88 changes: 88 additions & 0 deletions packages/plugin-initia/src/providers/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { IAgentRuntime, Provider, Memory, State } from "@elizaos/core";
import * as initia from '@initia/initia.js';
const { Wallet, RESTClient, RawKey, Tx, WaitTxBroadcastResult } = initia;
import { KEY_CHAIN_ID, KEY_NODE_URL, KEY_PRIVATE_KEY } from "../types/const";

interface WalletProviderOptions {
chainId: string;
nodeUrl: string;
}

const DEFAULT_INITIA_TESTNET_CONFIGS: WalletProviderOptions = {
chainId: 'initiation-2',
nodeUrl: 'https://rest.testnet.initia.xyz',
}

export class WalletProvider {
private readonly wallet: Wallet | null = null;
private readonly restClient: RESTClient | null = null;
private runtime: IAgentRuntime;

constructor(runtime: IAgentRuntime, options: WalletProviderOptions = DEFAULT_INITIA_TESTNET_CONFIGS) {
const privateKey = runtime.getSetting(KEY_PRIVATE_KEY);
if (!privateKey) throw new Error("INITIA_PRIVATE_KEY is not configured");

this.runtime = runtime;
this.restClient = new RESTClient(
options.nodeUrl, {
chainId: options.chainId,
gasPrices: '0.15uinit',
gasAdjustment: '1.75'
});
this.wallet = new Wallet(this.restClient, RawKey.fromHex(privateKey));
}

getWallet() {
if (this.wallet == null) {
throw new Error("Initia wallet is not configured.");
}

return this.wallet;
}

getAddress() {
if (this.wallet == null) {
throw new Error("Initia wallet is not configured.");
}

return this.wallet.key.accAddress;
}

async getBalance() {
if (this.wallet == null) {
throw new Error("Initia wallet is not configured.");
}

return this.wallet.rest.bank.balance(this.getAddress());
}

async sendTransaction(signedTx: Tx): Promise<WaitTxBroadcastResult> {
return await this.restClient.tx.broadcast(signedTx);
}
}

export const initiaWalletProvider: Provider = {
async get(runtime: IAgentRuntime, message: Memory, state?: State): Promise<string | null> {
if (!runtime.getSetting(KEY_PRIVATE_KEY)) {
return null;
}

try {
const nodeUrl: string | null = runtime.getSetting(KEY_NODE_URL);
const chainId: string | null = runtime.getSetting(KEY_CHAIN_ID);
let walletProvider: WalletProvider;
if (nodeUrl === null || chainId === null) {
walletProvider = new WalletProvider(runtime);
} else {
walletProvider = new WalletProvider(runtime, { nodeUrl: nodeUrl, chainId: chainId } as WalletProviderOptions);
}

const address = walletProvider.getAddress();
const balance = await walletProvider.getBalance();
return `Initia Wallet Address: ${address}\nBalance: ${balance} INIT`;
} catch (e) {
console.error("Error during configuring initia wallet provider", e);
return null;
}
}
}
Empty file.
3 changes: 3 additions & 0 deletions packages/plugin-initia/src/types/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const KEY_PRIVATE_KEY = "INITIA_PRIVATE_KEY";
export const KEY_NODE_URL = "INITIA_NODE_URL";
export const KEY_CHAIN_ID = "INITIA_CHAIN_ID";
10 changes: 10 additions & 0 deletions packages/plugin-initia/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": [
"src/**/*.ts"
]
}
Loading

0 comments on commit 2d2f922

Please sign in to comment.