Skip to content

Commit

Permalink
feat:add plugin-lightning (#2429)
Browse files Browse the repository at this point in the history
* feat:add plugin-lightning

* fix:readme

* Update packages/plugin-lightning/package.json

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/plugin-lightning/tsup.config.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/plugin-lightning/src/providers/lightning.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/plugin-lightning/src/providers/lightning.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update payInvoice.ts

* Apply suggestions from code review

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/plugin-lightning/src/actions/payInvoice.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update packages/plugin-lightning/src/actions/createInvoice.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update payInvoice.ts

* Update package.json

* code format

* fix:pr 2429 code format

---------

Co-authored-by: Sayo <hi@sayo.wtf>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 20, 2025
1 parent cfd1f48 commit 4d5be44
Show file tree
Hide file tree
Showing 16 changed files with 1,038 additions and 257 deletions.
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

0 comments on commit 4d5be44

Please sign in to comment.