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

chore(update-plugin-initialization): btcfun & trikon plugin #2643

Merged
merged 9 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
"@0glabs/0g-ts-sdk": "0.2.1",
"@coinbase/coinbase-sdk": "0.10.0",
"@deepgram/sdk": "^3.9.0",
"@okxweb3/coin-bitcoin": "1.2.0",
"@okxweb3/crypto-lib": "1.0.10",
"@injectivelabs/sdk-ts": "^1.14.33",
"@vitest/eslint-plugin": "1.0.1",
"amqplib": "0.10.5",
Expand All @@ -66,6 +68,8 @@
"sharp": "0.33.5",
"tslog": "4.9.3",
"bs58": "4.0.0"
"tiny-secp256k1": "2.2.3",
"tslog": "4.9.3"
wtfsayo marked this conversation as resolved.
Show resolved Hide resolved
},
"packageManager": "[email protected]+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee",
"workspaces": [
Expand Down
16 changes: 16 additions & 0 deletions packages/plugin-btcfun/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# `@elizaos/plugin-btcfun`

This plugin provides actions and providers to interact with btcfun via bitcoin network.

---

## Configuration

### Default Setup

By default, **Bitcoin mainnet** is enabled. To use it, simply add your private key to the `.env` file:

```env
BTC_PRIVATE_KEY_WIF=your-private-key-here
ADDRESS=your-address-here
BTCFUN_API_URL=https://api-testnet-new.btc.fun
3 changes: 3 additions & 0 deletions packages/plugin-btcfun/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];
24 changes: 24 additions & 0 deletions packages/plugin-btcfun/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@elizaos/plugin-btcfun",
"version": "0.1.7-alpha.2",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"@lifi/data-types": "5.15.5",
"@lifi/sdk": "3.4.1",
"@lifi/types": "16.3.0",
"tsup": "8.3.5",
"viem": "2.21.53"
},
wtfsayo marked this conversation as resolved.
Show resolved Hide resolved
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"test": "vitest run",
"lint": "eslint --fix --cache ."
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
135 changes: 135 additions & 0 deletions packages/plugin-btcfun/src/actions/btcfun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { ByteArray, formatEther, parseEther, type Hex } from "viem";
import {
composeContext,
generateObjectDeprecated,
HandlerCallback,
ModelClass,
type IAgentRuntime,
type Memory,
type State,
} from "@elizaos/core";

import { networks, Psbt } from 'bitcoinjs-lib';
import { BIP32Factory } from 'bip32';
import {randomBytes} from 'crypto';
import * as ecc from 'tiny-secp256k1';
import { BtcWallet, privateKeyFromWIF } from "@okxweb3/coin-bitcoin";
import { base } from "@okxweb3/crypto-lib";
import { mintTemplate } from "../templates";
import {initBtcFunProvider} from "../providers/btcfun.ts";
export { mintTemplate };

export const btcfunMintAction = {
name: "mint",
description: "btcfun mint brc20",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback?: HandlerCallback
) => {
console.log("btcfun action handler called");
const btcfunProvider = initBtcFunProvider(runtime);

const chainCode = randomBytes(32);
const bip32Factory = BIP32Factory(ecc);
const network = networks.bitcoin;
const privateKeyWif = runtime.getSetting("BTC_PRIVATE_KEY_WIF") ?? process.env.BTC_PRIVATE_KEY_WIF;
let address = runtime.getSetting("ADDRESS") ?? process.env.ADDRESS;

const privateKey = base.fromHex(privateKeyFromWIF(privateKeyWif, network));
const privateKeyHex = base.toHex(privateKey);
const privateKeyBuffer = Buffer.from(privateKeyHex, 'hex');
const keyPair = bip32Factory.fromPrivateKey(privateKeyBuffer, chainCode, network);
const publicKeyBuffer = Buffer.from(keyPair.publicKey);
const publicKeyHex = publicKeyBuffer.toString('hex');

// Compose mint context
const mintContext = composeContext({
state,
template: mintTemplate,
});
const content = await generateObjectDeprecated({
runtime,
context: mintContext,
modelClass: ModelClass.LARGE,
});
let tick = content.inputToken;
let mintcap = content.mintcap ?? runtime.getSetting("MINTCAP");
let mintdeadline = content.mintdeadline ?? runtime.getSetting("MINTDEADLINE");
let addressFundraisingCap = content.addressFundraisingCap ?? runtime.getSetting("ADDRESS_FUNDRAISING_CAP");
console.log("begin to mint token", tick, content)
//validateBrc20
await btcfunProvider.validateBrc20(address, tick);
console.log("validateBrc20 success")

try {
let {order_id, psbt_hex} = await btcfunProvider.createBrc20Order(
publicKeyHex, address, publicKeyHex, address, 10,
tick, addressFundraisingCap, mintdeadline, mintcap)
const psbt = Psbt.fromHex(psbt_hex)
let wallet = new BtcWallet()
const toSignInputs = [];
psbt.data.inputs.forEach((input, index)=>{
toSignInputs.push({
index: index,
address: address,
sighashTypes: [0],
disableTweakSigner: false,
});
})

let params = {
type: 3,
psbt: psbt_hex,
autoFinalized: false,
toSignInputs: toSignInputs,
};

let signParams = {
privateKey: privateKeyWif,
data: params,
};
let signedPsbtHex = await wallet.signTransaction(signParams);

const txHash = await btcfunProvider.broadcastOrder(order_id, signedPsbtHex)
console.log('signedPsbtHex: ', signedPsbtHex, 'orderID: ', order_id, 'txhash', txHash)
if (callback) {
callback({
text: `Successfully mint ${tick} tokens, mintcap ${mintcap}, mintdeadline ${mintdeadline}, addressFundraisingCap ${addressFundraisingCap} ,txhash ${txHash}`,
content: {
success: true,
orderID: order_id,
},
});
}
} catch (error) {
console.error('Error:', error);
}
},
template: mintTemplate,
validate: async (runtime: IAgentRuntime) => {
const privateKey = runtime.getSetting("BTC_PRIVATE_KEY_WIF");
return typeof privateKey === "string" && privateKey.length > 0;
},
examples: [
[
{
user: "assistant",
content: {
text: "I'll help you mint 100000000 Party",
action: "MINT_BRC20",
},
},
{
user: "user",
content: {
text: "import token BRC20 `Party`, mintcap 100000, addressFundraisingCap 10 mintdeadline 864000",
action: "MINT_BRC20",
},
},
],
],
similes: ["MINT_BRC20","MINT_RUNES"],
};
16 changes: 16 additions & 0 deletions packages/plugin-btcfun/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {btcfunMintAction} from "./actions/btcfun.ts";

export * from "./providers/btcfun";

import type { Plugin } from "@elizaos/core";

export const btcfunPlugin: Plugin = {
name: "btcfun",
description: "btcfun plugin",
providers: [],
evaluators: [],
services: [],
actions: [btcfunMintAction],
};

export default btcfunPlugin;
95 changes: 95 additions & 0 deletions packages/plugin-btcfun/src/providers/btcfun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import fetch from 'node-fetch';
import type {IAgentRuntime} from "@elizaos/core";

export const initBtcFunProvider = (runtime: IAgentRuntime) => {

const btcfunApiURL = runtime.getSetting("BTCFUN_API_URL") ?? process.env.BTCFUN_API_URL
if (!btcfunApiURL) {
throw new Error("BTCFUN_API_URL is not set");
}

return new BtcfunProvider(btcfunApiURL);
};

export class BtcfunProvider {
private apiUrl: string;

constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}

async validateBrc20(address: string, ticker: string) {
const response = await fetch(`${this.apiUrl}/api/v1/import/brc20_validate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
address: address,
ticker: ticker,
}),
});

if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}

return response.json();
}

async createBrc20Order(paymentFromPubKey: string, paymentFrom: string, ordinalsFromPubKey: string, ordinalsFrom: string, feeRate: number, tick: string, addressFundraisingCap: string, mintDeadline: number, mintCap: string) {
const response = await fetch(`${this.apiUrl}/api/v1/import/brc20_order`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
payment_from_pub_key: paymentFromPubKey,
payment_from: paymentFrom,
ordinals_from_pub_key: ordinalsFromPubKey,
ordinals_from: ordinalsFrom,
fee_rate: feeRate,
tick: tick,
address_fundraising_cap: addressFundraisingCap,
mint_deadline: mintDeadline,
mint_cap: mintCap,
}),
});

if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}

const result = await response.json();

if (result.code === "OK" && result.data) {
const { order_id, psbt_hex } = result.data;
return { order_id, psbt_hex };
} else {
console.log("Invalid response", result)
throw new Error("Invalid response");
}
}

async broadcastOrder(orderId: string, signedPsbtHex: string) {
const response = await fetch(`${this.apiUrl}/api/v1/import/broadcast`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_id: orderId,
signed_psbt_hex: signedPsbtHex,
}),
});

if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}
const result = await response.json();

if (result.code === "OK" && result.data) {
return result.data;
}
}
}
24 changes: 24 additions & 0 deletions packages/plugin-btcfun/src/templates/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const mintTemplate = `Given the recent messages and wallet information below:

{{recentMessages}}

{{walletInfo}}

Extract the following information about the requested token swap:
- Input token symbol (the token being mint), eg: mint token abc
- Input token mintcap eg: "10000"
- Input token addressFundraisingCap everyone can offer eg: "10"
- Input token mintdeadline ,duration Using seconds as the unit eg: 864000


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

\`\`\`json
{
"inputToken": string | null,
"mintcap": string | 1000,
"addressFundraisingCap": string | 10,
"mintdeadline" : number | 864000,
}
\`\`\`
`;
15 changes: 15 additions & 0 deletions packages/plugin-btcfun/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "./src",
"typeRoots": [
"./node_modules/@types",
"./src/types"
],
"declaration": true
},
"include": [
"src"
]
}
22 changes: 22 additions & 0 deletions packages/plugin-btcfun/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"], // Ensure you're targeting CommonJS
external: [
"dotenv", // Externalize dotenv to prevent bundling
"fs", // Externalize fs to use Node.js built-in module
"path", // Externalize other built-ins if necessary
"@reflink/reflink",
"@node-llama-cpp",
"https",
"http",
"agentkeepalive",
"viem",
"@lifi/sdk",
"@okxweb3/crypto-lib",
],
});
Loading