Skip to content

Commit

Permalink
feat: impl assets provider
Browse files Browse the repository at this point in the history
  • Loading branch information
yubing744 committed Jan 23, 2025
1 parent e190eae commit f814e53
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 17 deletions.
5 changes: 4 additions & 1 deletion packages/plugin-rooch/src/plugins/roochPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Plugin } from "@elizaos/core";
import { assetsProvider } from "../providers/assetsProvider"

export const roochPlugin: Plugin = {
name: "rooch",
description: "Rooch Plugin for Eliza",
actions: [],
evaluators: [],
providers: [],
providers: [
assetsProvider
],
};

export default roochPlugin;
173 changes: 173 additions & 0 deletions packages/plugin-rooch/src/providers/assetsProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import BigNumber from "bignumber.js";
import {
Provider,
IAgentRuntime,
Memory,
State,
elizaLogger,
settings
} from "@elizaos/core";
import {
getRoochNodeUrl,
NetworkType,
RoochClient,
Address,
PaginatedUTXOStateViews,
IndexerStateIDView,
PaginatedBalanceInfoViews,
} from '@roochnetwork/rooch-sdk/dist/esm';
import {
BTCUTXO,
RoochCoin
} from "../types";
import {
parseBitcoinAddress,
} from "../utils";

export class Assets {
utxos: Array<BTCUTXO>
coins: Array<RoochCoin>
}

export class AssetsProvider {
private roochClient: RoochClient
private address: Address

constructor(roochClient: RoochClient, address: Address) {
this.roochClient = roochClient;
this.address = address;
}

async fetchBTCUTXOs(): Promise<Array<BTCUTXO>> {
try {
const limit = 100
let cursor: IndexerStateIDView | null = null
const allUTXOs = new Array<BTCUTXO>();

while (true) {
const response: PaginatedUTXOStateViews = await this.roochClient.queryUTXO({
filter: {
owner: this.address.toStr(),
},
cursor: cursor,
limit: limit.toString(),
})

for (const utxo of response.data) {
allUTXOs.push({
utxo: utxo.id,
sats: utxo.value.value
})
}

if (!response.has_next_page || !response.next_cursor) {
break
}

cursor = response.next_cursor
}

return allUTXOs;
} catch (error) {
console.error("Error fetching UTXOs:", error);
throw error;
}
}

async fetchRoochCoins(): Promise<Array<RoochCoin>> {
try {
const limit = 100
let cursor: IndexerStateIDView | null = null
const allCoins = new Array<RoochCoin>();

while (true) {
const response: PaginatedBalanceInfoViews = await this.roochClient.getBalances({
owner: this.address.toStr(),
cursor: cursor,
limit: limit.toString(),
})

for (const coin of response.data) {
allCoins.push({
symbol: coin.symbol,
name: coin.name,
balance: coin.fixedBalance
})
}

if (!response.has_next_page || !response.next_cursor) {
break
}

cursor = response.next_cursor
}

return allCoins;
} catch (error) {
console.error("Error fetching UTXOs:", error);
throw error;
}
}

async fetchAssets(): Promise<Assets> {
try {
const assets = new Assets();
assets.utxos = await this.fetchBTCUTXOs();
assets.coins = await this.fetchRoochCoins();
return assets
} catch (error) {
console.error("Error fetching portfolio:", error);
throw error;
}
}

formatAssets(runtime, assets: Assets): string {
let output = `${runtime.character.name}\n`;
output += `Assets(${this.address}): \n`;

output += "Rooch network coin assets:\n"
for (const coin of assets.coins) {
const coinBalanceFormatted = new BigNumber(coin.balance).toFixed(2);
output += `${coin.symbol}(${coin.name}) Balance:${coinBalanceFormatted}\n`
}

output += "BTC assets:"
for (const utxo of assets.utxos) {
const satsFormatted = new BigNumber(utxo.sats).toFixed(2);
output += `UTXO(${utxo.utxo}) ${satsFormatted} Sats\n`
}

return output;
}

async getFormattedAssets(runtime): Promise<string> {
try {
const assets = await this.fetchAssets();
return this.formatAssets(runtime, assets);
} catch (error) {
console.error("Error generating assets report:", error);
return "Unable to fetch wallet information. Please try again later.";
}
}
}

export const assetsProvider: Provider = {
get: async (runtime: IAgentRuntime, message: Memory, state: State) => {
const roochAddress = parseBitcoinAddress(runtime);

try {
const url = getRoochNodeUrl(settings["ROOCH_NETWORK"] as NetworkType);
elizaLogger.info(
`getRoochNodeUrl: ${url}`
);

const roochClient = new RoochClient({ url: url })
const provider = new AssetsProvider(roochClient, roochAddress)

return await provider.getFormattedAssets(runtime);
} catch (error) {
console.error("Error in assets provider:", error);
return error;
}
},
};
14 changes: 0 additions & 14 deletions packages/plugin-rooch/src/providers/sampleProvider.ts

This file was deleted.

12 changes: 12 additions & 0 deletions packages/plugin-rooch/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ export interface ExamplePluginConfig {
apiSecret: string;
endpoint?: string;
}


export interface BTCUTXO {
utxo?: string;
sats?: string;
}

export interface RoochCoin {
symbol: string;
name: string;
balance: number;
}
10 changes: 8 additions & 2 deletions packages/plugin-rooch/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IAgentRuntime } from "@elizaos/core";
import bs58check from 'bs58check';
import { ParsedKeypair } from "@roochnetwork/rooch-sdk/dist/esm";
import { ParsedKeypair, Secp256k1Keypair, BitcoinAddress } from "@roochnetwork/rooch-sdk/dist/esm";

const parseKeypair = (runtime: IAgentRuntime): ParsedKeypair => {
const wifPrivateKey = runtime.getSetting("BITCOIN_PRIVATE_KEY");
Expand All @@ -24,6 +24,12 @@ const parseKeypair = (runtime: IAgentRuntime): ParsedKeypair => {
}
};

const parseBitcoinAddress = (runtime: IAgentRuntime): BitcoinAddress => {
const parsedKeypair = parseKeypair(runtime)
const keypair = Secp256k1Keypair.fromSecretKey(parsedKeypair.secretKey, false)
return keypair.getBitcoinAddress()
}

const parseAccessPath = (uri: string): string => {
// Adjust the regex to ensure correct matching
const match = uri.match(/^rooch:\/\/object\/(0x[a-fA-F0-9]+)$/);
Expand All @@ -33,4 +39,4 @@ const parseAccessPath = (uri: string): string => {
throw new Error("Invalid URI format");
};

export { parseKeypair, parseAccessPath };
export { parseKeypair, parseBitcoinAddress, parseAccessPath };

0 comments on commit f814e53

Please sign in to comment.