Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat:add plugin-lightning #2429

Merged
merged 19 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"@elizaos/plugin-hyperliquid": "workspace:*",
"@elizaos/plugin-akash": "workspace:*",
"@elizaos/plugin-quai": "workspace:*",
"@elizaos/plugin-lightning": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
7 changes: 6 additions & 1 deletion agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { TwitterClientInterface } from "@elizaos/client-twitter";
// import { ReclaimAdapter } from "@elizaos/plugin-reclaim";
import { DirectClient } from "@elizaos/client-direct";
import { PrimusAdapter } from "@elizaos/plugin-primus";

import { lightningPlugin } from "@elizaos/plugin-lightning";
import {
AgentRuntime,
CacheManager,
Expand Down Expand Up @@ -854,6 +854,11 @@ export async function createAgent(
getSecret(character, "QUAI_PRIVATE_KEY")
? quaiPlugin
: null,
getSecret(character, "LND_TLS_CERT") &&
getSecret(character, "LND_MACAROON") &&
getSecret(character, "LND_SOCKET")
? lightningPlugin
: null
].filter(Boolean),
providers: [],
actions: [],
Expand Down
72 changes: 72 additions & 0 deletions packages/plugin-lightning/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# @elizaos/plugin-lightning

This plugin enables interaction with the Hyperliquid DEX through Eliza, providing spot trading capabilities.

## Features

- 💱 Make a new off-chain invoice.
- 📊 Make an off-chain payment.

## Installation

Add the plugin to your Eliza configuration:

```json
{
"plugins": ["@elizaos/plugin-lightning"]
}
```

## Configuration

Set the following environment variables:

```env
LND_TLS_CERT=your_lnnode_tls_cert #Base64 of LND certificate
LND_MACAROON=020..... #Base64 encoded admin.macaroon file
LND_SOCKET='x.x.x.x:10009'
```

## Available Actions

### 1. CREATE_INVOICE

Make a new off-chain invoice.

Examples:

```

"Help me create an invoice for 1000sats"
"Create an invoice for 1000sats"

```

Returns: Current price, 24h change, and volume.

### 2. PAY_INVOICE

Make an off-chain payment.

Examples:

```

"Pay invoice lnbcrt10u1pncndjvpp58y77adkngcz3ypx6t39j245ydvk2vu67c8ugvegee3gt5wgs7yjqdxvdec82c33wdmnq73s0qcxwurrxp4nquncxe4h56m9xu6xwetyd3mrq6ehdguxkd35wuurgarex4u8gefkdsekgdtnddehxurrxecxvhmwwp6kyvfexekhxwtv8paryvnpwsuhxdryvachwangw3kn2atddq6kzvrvwfcxzanewce8ja34d43k56rkweu8jdtcwv68zmrsvdescqzzsxqrrsssp5q3hv38wfprvaazzwf8c4t33tzjcac5xz94sk8muehmn5szqaw6ks9qxpqysgqt5pjhna4922s8ayzgu5rh8clx7psp2culdr5r6cxxxqzs3e5ep345p45vggg0qegt6fu3prdrqgpd8v70l9wdhekt8gex5e8pqvxg2sp97fkmd"


```

## Security Notes

- Store your LND_TLS_CERT and LND_MACAROON securely using environment variables
- Test with small amounts first
- Use testnet for initial testing

## License

MIT

```

```
3 changes: 3 additions & 0 deletions packages/plugin-lightning/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
35 changes: 35 additions & 0 deletions packages/plugin-lightning/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@elizaos/plugin-lightning",
"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"
],
"dependencies": {
"@elizaos/core": "workspace:*",
"@elizaos/plugin-tee": "workspace:*",
"astra-lightning": "^1.1.0",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint --fix --cache ."
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
105 changes: 105 additions & 0 deletions packages/plugin-lightning/src/actions/createInvoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type { IAgentRuntime, Memory, State } from "@elizaos/core";
import {
composeContext,
generateObjectDeprecated,
ModelClass,
} from "@elizaos/core";

import {
initLightningProvider,
LightningProvider,
} from "../providers/lightning";
import { CreateInvoiceResult } from "astra-lightning";
import { CreateInvoiceArgs } from "../types";
import { createInvoiceTemplate } from "../templates";

export { createInvoiceTemplate };

export class CreateInvoiceAction {
constructor(private lightningProvider: LightningProvider) {
this.lightningProvider = lightningProvider;
}

async createInvoice(
params: CreateInvoiceArgs
): Promise<CreateInvoiceResult> {
if (!params.tokens) {
throw new Error("tokens is required.");
}
const retCreateInvoice =
await this.lightningProvider.createInvoice(params);
return retCreateInvoice;
}
}

export const createInvoiceAction = {
name: "CREATE_INVOICE",
description: "Create a Lightning invoice.",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback?: any
) => {
console.log("CreateInvoice action handler called");
const lightningProvider = await initLightningProvider(runtime);
const action = new CreateInvoiceAction(lightningProvider);

// Compose bridge context
const createInvoiceContext = composeContext({
state,
template: createInvoiceTemplate,
});
const content = await generateObjectDeprecated({
runtime,
context: createInvoiceContext,
modelClass: ModelClass.LARGE,
});

const createInvoiceOptions: CreateInvoiceArgs = {
tokens: content.tokens,
};

try {
const createInvoiceResp =
await action.createInvoice(createInvoiceOptions);

if (callback) {
callback({
text: `Successfully createInvoice ${createInvoiceResp.tokens}\r\nInvoice:${createInvoiceResp.request}`,
content: {
success: true,
invoice: createInvoiceResp.request,
},
});
}
return true;
} catch (error) {
console.error("Error in createInvoice handler:", error.message);
if (callback) {
callback({ text: `Error: ${error.message}` });
}
return false;
}
},
template: createInvoiceTemplate,
validate: async (runtime: IAgentRuntime) => {
const cert = runtime.getSetting("LND_TLS_CERT");
const macaroon = runtime.getSetting("LND_MACAROON");
const socket = runtime.getSetting("LND_SOCKET");
return !!cert && !!macaroon && !!socket;
},
examples: [
[
{
user: "user",
content: {
text: "Create an invoice for 1000 sats",
action: "CREATE_INVOICE",
},
},
],
],
similes: ["CREATE_INVOICE"],
};
132 changes: 132 additions & 0 deletions packages/plugin-lightning/src/actions/payInvoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type { IAgentRuntime, Memory, State } from "@elizaos/core";
import {
composeContext,
generateObjectDeprecated,
ModelClass,
} from "@elizaos/core";

import {
initLightningProvider,
LightningProvider,
} from "../providers/lightning";
import { PayResult } from "astra-lightning";
import { PayArgs } from "../types";
import { payInvoiceTemplate } from "../templates";

export { payInvoiceTemplate };

type ExtendedPayResult = PayResult & { outgoing_channel: string };
export class PayInvoiceAction {
constructor(private lightningProvider: LightningProvider) {
this.lightningProvider = lightningProvider;
}

async getAvalibleChannelId(): Promise<string> {
const { channels } = await this.lightningProvider.getLndChannel();
const filteredActiveChannels = channels.filter(
(channel) => channel.is_active === true
);
const sortedChannels = filteredActiveChannels.sort(
(a, b) => b.local_balance - a.local_balance
);
if (sortedChannels.length > 0) {
return sortedChannels[0].id;
}
return "";
}
async payInvoice(params: PayArgs): Promise<ExtendedPayResult> {
const outgoing_channel = await this.getAvalibleChannelId();
if (!outgoing_channel) {
throw new Error("no avalible channel");
}
const requestArgs = {
outgoing_channel: outgoing_channel,
...params,
};
const retPayInvoice =
await this.lightningProvider.payInvoice(requestArgs);
return {
...retPayInvoice,
outgoing_channel: outgoing_channel,
};
}
}

export const payInvoiceAction = {
name: "PAY_INVOICE",
description: "Make a payment.",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback?: any
) => {
console.log("payInvoice action handler called");
const lightningProvider = await initLightningProvider(runtime);
const action = new PayInvoiceAction(lightningProvider);

// Compose bridge context
const payInvoiceContext = composeContext({
state,
template: payInvoiceTemplate,
});
const content = await generateObjectDeprecated({
runtime,
context: payInvoiceContext,
modelClass: ModelClass.LARGE,
});

const payInvoiceOptions: PayArgs = {
request: content.request,
outgoing_channel: content.outgoing_channel,
};

try {
const payInvoiceResp = await action.payInvoice(payInvoiceOptions);
console.log("🚀 ~ payInvoiceResp:", payInvoiceResp);

if (callback) {
let text = "";
if (payInvoiceResp.is_confirmed) {
text = `Successfully payInvoice ${content.request} from ${payInvoiceResp.outgoing_channel};\r\n Amount: ${payInvoiceResp.tokens};\r\n Fee: ${payInvoiceResp.fee};\r\n Payment Hash: ${payInvoiceResp.id};`;
} else {
text = `Failed to payInvoice ${content.request} from ${content.outgoing_channel};\r\n Amount: ${payInvoiceResp.tokens};`;
}
callback({
text: text,
content: {
success: true,
},
});
}
return true;
} catch (error) {
const err = error?.[2]?.err;
console.error("Error in payInvoice handler:", err.details);
if (callback) {
callback({ text: `Error: ${err.details}` });
}
return false;
}
},
template: payInvoiceTemplate,
validate: async (runtime: IAgentRuntime) => {
const cert = runtime.getSetting("LND_TLS_CERT");
const macaroon = runtime.getSetting("LND_MACAROON");
const socket = runtime.getSetting("LND_SOCKET");
return !!cert && !!macaroon && !!socket;
},
examples: [
[
{
user: "user",
content: {
text: "Pay invoice for lnbrc...",
action: "PAY_INVOICE",
},
},
],
],
similes: ["PAY_INVOICE", "MAKE_PAYMENT"],
};
Loading
Loading