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 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
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"@elizaos/plugin-hyperliquid": "workspace:*",
"@elizaos/plugin-akash": "workspace:*",
"@elizaos/plugin-quai": "workspace:*",
"@elizaos/plugin-lightning": "workspace:*",
"@elizaos/plugin-b2": "workspace:*",
"@elizaos/plugin-nft-collections": "workspace:*",
"@elizaos/plugin-pyth-data": "workspace:*",
Expand Down
6 changes: 6 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DirectClient } from "@elizaos/client-direct";
import { agentKitPlugin } from "@elizaos/plugin-agentkit";
// import { ReclaimAdapter } from "@elizaos/plugin-reclaim";
import { PrimusAdapter } from "@elizaos/plugin-primus";
import { lightningPlugin } from "@elizaos/plugin-lightning";
import { elizaCodeinPlugin, onchainJson } from "@elizaos/plugin-iq6900";

import {
Expand Down Expand Up @@ -1049,6 +1050,11 @@ export async function createAgent(
getSecret(character, "PYTH_MAINNET_PROGRAM_KEY")
? pythDataPlugin
: null,
getSecret(character, "LND_TLS_CERT") &&
getSecret(character, "LND_MACAROON") &&
getSecret(character, "LND_SOCKET")
? lightningPlugin
: null,
getSecret(character, "OPENAI_API_KEY") && getSecret(character, "ENABLE_OPEN_AI_COMMUNITY_PLUGIN")
? openaiPlugin
: null,
Expand Down
64 changes: 64 additions & 0 deletions packages/plugin-lightning/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# @elizaos/plugin-lightning

This plugin enables create lightning invoice or payInvoice.

## 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:

```text

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

```

Returns: lnbcrt....

### 2. PAY_INVOICE

Make an off-chain payment.

Examples:

```text

"Pay invoice lnbcrt10u1pncndjvpp58y77adkngcz3ypx6t39j245ydvk2vu67c8ugvegee3gt5wgs7yjqdxvdec82c33wdmnq73s0qcxwurrxp4nquncxe4h56m9xu6xwetyd3mrq6ehdguxkd35wuurgarex4u8gefkdsekgdtnddehxurrxecxvhmwwp6kyvfexekhxwtv8paryvnpwsuhxdryvachwangw3kn2atddq6kzvrvwfcxzanewce8ja34d43k56rkweu8jdtcwv68zmrsvdescqzzsxqrrsssp5q3hv38wfprvaazzwf8c4t33tzjcac5xz94sk8muehmn5szqaw6ks9qxpqysgqt5pjhna4922s8ayzgu5rh8clx7psp2culdr5r6cxxxqzs3e5ep345p45vggg0qegt6fu3prdrqgpd8v70l9wdhekt8gex5e8pqvxg2sp97fkmd"


```

## Security Notes

- Store your LND_TLS_CERT and LND_MACAROON securely using environment variables
- Test with small amounts first
- Use regtest for initial testing
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];
37 changes: 37 additions & 0 deletions packages/plugin-lightning/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"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"
},
"devDependencies": {
"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"
}
}
108 changes: 108 additions & 0 deletions packages/plugin-lightning/src/actions/createInvoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import type { IAgentRuntime, Memory, State } from "@elizaos/core";
import {
composeContext,
generateObjectDeprecated,
ModelClass,
elizaLogger,
} from "@elizaos/core";

import {
initLightningProvider,
LightningProvider,
} from "../providers/lightning";

import { createInvoiceTemplate } from "../templates";
import { CreateInvoiceResult } from "astra-lightning";
import { CreateInvoiceArgs } from "../types";
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?: (response: {
text: string;
content?: { success: boolean; invoice?: string };
}) => void,
) => {
elizaLogger.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 = {
tokens: content.tokens,
};

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

if (callback) {
callback({
text: `Successfully created invoice for ${createInvoiceResp.tokens.toLocaleString()} sats\r\nInvoice: ${createInvoiceResp.request}`,
content: {
success: true,
invoice: createInvoiceResp.request,
},
});
}
return true;
} catch (error) {
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"],
};
136 changes: 136 additions & 0 deletions packages/plugin-lightning/src/actions/payInvoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type { IAgentRuntime, Memory, State } from "@elizaos/core";
import {
composeContext,
generateObjectV2,
ModelClass,
elizaLogger,
} 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,
) => {
elizaLogger.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 generateObjectV2({
runtime,
context: payInvoiceContext,
modelClass: ModelClass.LARGE,
});

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

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

if (callback) {
const text = "";
if (payInvoiceResp.is_confirmed) {
callback({
text: `Successfully paid invoice ${content.request} from ${payInvoiceResp.outgoing_channel};\nAmount: ${payInvoiceResp.tokens};\nFee: ${payInvoiceResp.fee};\nPayment Hash: ${payInvoiceResp.id};`,
content: { success: true },
});
} else {
callback({
text: `Failed to payInvoice ${content.request} from ${content.outgoing_channel};\r\n Amount: ${payInvoiceResp.tokens};`,
content: {
success: false,
},
});
}
}
return true;
} catch (error) {
elizaLogger.error("Error in payInvoice handler:", error);
if (callback) {
callback({
text: `Error: ${error.message || "An error occurred"}`,
});
}
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"],
};
15 changes: 15 additions & 0 deletions packages/plugin-lightning/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export * from "./actions/createInvoice";
export * from "./providers/lightning";
export * from "./types";

import type { Plugin } from "@elizaos/core";
import { createInvoiceAction } from "./actions/createInvoice";
import { payInvoiceAction } from "./actions/payInvoice";

export const lightningPlugin: Plugin = {
name: "lightning",
description: "lightning integration plugin",
actions: [createInvoiceAction, payInvoiceAction],
};

export default lightningPlugin;
Loading
Loading