From 3094df90dc4ad119b24ec8349981f0f9c846551e Mon Sep 17 00:00:00 2001 From: Uwe Cerron Date: Fri, 29 Nov 2024 13:55:17 -0600 Subject: [PATCH] convergence plugin --- packages/plugin-convergence/README.md | 0 packages/plugin-convergence/package.json | 34 +++ packages/plugin-convergence/src/index.ts | 219 ++++++++++++++++++ packages/plugin-convergence/src/types.ts | 31 +++ .../plugin-convergence/src/utils/token.ts | 18 ++ .../plugin-convergence/tsconfig.base.json | 11 + packages/plugin-convergence/tsconfig.json | 12 + packages/plugin-convergence/yarn-error.log | 69 ++++++ 8 files changed, 394 insertions(+) create mode 100644 packages/plugin-convergence/README.md create mode 100644 packages/plugin-convergence/package.json create mode 100644 packages/plugin-convergence/src/index.ts create mode 100644 packages/plugin-convergence/src/types.ts create mode 100644 packages/plugin-convergence/src/utils/token.ts create mode 100644 packages/plugin-convergence/tsconfig.base.json create mode 100644 packages/plugin-convergence/tsconfig.json create mode 100644 packages/plugin-convergence/yarn-error.log diff --git a/packages/plugin-convergence/README.md b/packages/plugin-convergence/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/plugin-convergence/package.json b/packages/plugin-convergence/package.json new file mode 100644 index 0000000000..3c44ca16e0 --- /dev/null +++ b/packages/plugin-convergence/package.json @@ -0,0 +1,34 @@ +{ + "name": "eliza-convergence-plugin-workspace", + "version": "0.1.0", + "private": true, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "clean": "rimraf dist", + "dev": "tsc -w", + "lint": "eslint src --ext .ts", + "test": "jest" + }, + "dependencies": { + "@convergence-rfq/sdk": "^0.1.0", + "@eliza/core": "workspace:*", + "@solana/spl-token": "^0.3.8", + "@solana/web3.js": "^1.78.4", + "bn.js": "^5.2.1", + "decimal.js": "^10.4.3" + }, + "devDependencies": { + "@types/bn.js": "^5.1.1", + "@types/jest": "^29.5.3", + "@types/node": "^20.4.5", + "@typescript-eslint/eslint-plugin": "^6.2.0", + "@typescript-eslint/parser": "^6.2.0", + "eslint": "^8.45.0", + "jest": "^29.6.2", + "rimraf": "^5.0.1", + "ts-jest": "^29.1.1", + "typescript": "^5.1.6" + } + } \ No newline at end of file diff --git a/packages/plugin-convergence/src/index.ts b/packages/plugin-convergence/src/index.ts new file mode 100644 index 0000000000..fdc9dddc8e --- /dev/null +++ b/packages/plugin-convergence/src/index.ts @@ -0,0 +1,219 @@ +import { Plugin } from '@eliza/core'; +import { + Convergence, + RfqState, + MarketMakerState, + CollateralAccount, + QuoteState +} from '@convergence-rfq/sdk'; +import { Connection, PublicKey, Transaction } from '@solana/web3.js'; +import { RfqRequest, QuoteMetadata } from './types'; + +export class ConvergencePlugin implements Plugin { + private convergence: Convergence; + + constructor(config: ConvergencePluginConfig) { + const connection = new Connection(config.endpoint, config.commitment); + this.convergence = new Convergence(connection); + } + + async onMessage(message: string): Promise { + const parts = message.split(' '); + const command = parts[1]?.toLowerCase(); + + switch (command) { + // RFQ Management + case 'create': + return this.handleCreateRfq(parts.slice(2)); + case 'cancel': + return this.handleCancelRfq(parts.slice(2)); + case 'status': + return this.handleStatusCheck(parts[2]); + + // Quote Management + case 'respond': + return this.handleRespond(parts.slice(2)); + case 'revoke': + return this.handleRevokeQuote(parts.slice(2)); + case 'accept': + return this.handleAcceptQuote(parts.slice(2)); + + // Collateral Management + case 'deposit': + return this.handleDeposit(parts.slice(2)); + case 'withdraw': + return this.handleWithdraw(parts.slice(2)); + case 'balance': + return this.handleCheckBalance(parts.slice(2)); + + // Market Maker Management + case 'register': + return this.handleRegisterMM(parts.slice(2)); + case 'verify': + return this.handleVerifyIdentity(parts.slice(2)); + + // Settlement + case 'settle': + return this.handleSettle(parts.slice(2)); + + default: + return this.getHelpMessage(); + } + } + + // RFQ Management Methods + private async handleCreateRfq(args: string[]): Promise { + try { + const [baseToken, quoteToken, amount, side, expiryMins, ...options] = args; + + const request: RfqRequest = { + baseToken: await this.getTokenInfo(baseToken, parseFloat(amount)), + quoteToken: await this.getTokenInfo(quoteToken, 0), + side: side as 'buy' | 'sell', + expiry: Math.floor(Date.now() / 1000) + (parseInt(expiryMins) * 60), + minCompetitors: options.includes('--min-competitors') ? 3 : undefined, + allowPartialFills: options.includes('--partial-fills'), + requireVerifiedIdentity: options.includes('--verified-only') + }; + + const rfq = await this.convergence.createRfq(request); + return `RFQ created with ID: ${rfq.id}`; + } catch (error) { + return `Error creating RFQ: ${error.message}`; + } + } + + private async handleCancelRfq(args: string[]): Promise { + try { + const [rfqId] = args; + await this.convergence.cancelRfq(rfqId); + return `RFQ ${rfqId} cancelled successfully`; + } catch (error) { + return `Error cancelling RFQ: ${error.message}`; + } + } + + // Quote Management Methods + private async handleRespond(args: string[]): Promise { + try { + const [rfqId, price, size] = args; + + const quote = await this.convergence.createQuote({ + rfqId, + price: parseFloat(price), + size: parseFloat(size), + expiresAt: Math.floor(Date.now() / 1000) + 300 // 5 minutes + }); + + return `Quote submitted: ${quote.id}`; + } catch (error) { + return `Error submitting quote: ${error.message}`; + } + } + + private async handleRevokeQuote(args: string[]): Promise { + try { + const [quoteId] = args; + await this.convergence.revokeQuote(quoteId); + return `Quote ${quoteId} revoked successfully`; + } catch (error) { + return `Error revoking quote: ${error.message}`; + } + } + + // Collateral Management Methods + private async handleDeposit(args: string[]): Promise { + try { + const [token, amount] = args; + const tx = await this.convergence.depositCollateral({ + mint: new PublicKey(token), + amount: parseFloat(amount) + }); + return `Deposit successful: ${tx}`; + } catch (error) { + return `Error depositing collateral: ${error.message}`; + } + } + + private async handleWithdraw(args: string[]): Promise { + try { + const [token, amount] = args; + const tx = await this.convergence.withdrawCollateral({ + mint: new PublicKey(token), + amount: parseFloat(amount) + }); + return `Withdrawal successful: ${tx}`; + } catch (error) { + return `Error withdrawing collateral: ${error.message}`; + } + } + + private async handleCheckBalance(args: string[]): Promise { + try { + const [token] = args; + const balance = await this.convergence.getCollateralBalance(new PublicKey(token)); + return `Balance: ${balance.amount} ${balance.symbol}`; + } catch (error) { + return `Error checking balance: ${error.message}`; + } + } + + // Market Maker Methods + private async handleRegisterMM(args: string[]): Promise { + try { + const [identity] = args; + await this.convergence.registerMarketMaker(identity); + return `Successfully registered as market maker`; + } catch (error) { + return `Error registering as market maker: ${error.message}`; + } + } + + private async handleVerifyIdentity(args: string[]): Promise { + try { + const [proof] = args; + await this.convergence.verifyIdentity(proof); + return `Identity verified successfully`; + } catch (error) { + return `Error verifying identity: ${error.message}`; + } + } + + // Settlement Methods + private async handleSettle(args: string[]): Promise { + try { + const [rfqId] = args; + const tx = await this.convergence.settleRfq(rfqId); + return `RFQ settled successfully: ${tx}`; + } catch (error) { + return `Error settling RFQ: ${error.message}`; + } + } + + private getHelpMessage(): string { + return ` +Available commands: + RFQ Management: + /rfq create [--min-competitors] [--partial-fills] [--verified-only] + /rfq cancel + /rfq status + + Quote Management: + /rfq respond + /rfq revoke + /rfq accept + + Collateral Management: + /rfq deposit + /rfq withdraw + /rfq balance + + Market Maker: + /rfq register + /rfq verify + + Settlement: + /rfq settle + `; + } +} \ No newline at end of file diff --git a/packages/plugin-convergence/src/types.ts b/packages/plugin-convergence/src/types.ts new file mode 100644 index 0000000000..b64d3fd0d8 --- /dev/null +++ b/packages/plugin-convergence/src/types.ts @@ -0,0 +1,31 @@ +import { PublicKey } from '@solana/web3.js'; + +export interface TokenInfo { + mint: PublicKey; + amount: number; + decimals: number; +} + +export interface RfqRequest { + baseToken: TokenInfo; + quoteToken: TokenInfo; + side: 'buy' | 'sell'; + expiry: number; + minCompetitors?: number; + allowPartialFills?: boolean; + requireVerifiedIdentity?: boolean; +} + +export interface MarketMakerInfo { + pubkey: PublicKey; + identity: string; + verified: boolean; +} + +export interface QuoteMetadata { + rfqId: string; + price: number; + size: number; + expiresAt: number; + marketMaker: MarketMakerInfo; +} \ No newline at end of file diff --git a/packages/plugin-convergence/src/utils/token.ts b/packages/plugin-convergence/src/utils/token.ts new file mode 100644 index 0000000000..dd91491938 --- /dev/null +++ b/packages/plugin-convergence/src/utils/token.ts @@ -0,0 +1,18 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { TokenInfo } from '../types'; +import { getMint } from '@solana/spl-token'; + +export async function getTokenInfo( + connection: Connection, + mintAddress: string, + amount: number +): Promise { + const mint = new PublicKey(mintAddress); + const mintInfo = await getMint(connection, mint); + + return { + mint, + amount, + decimals: mintInfo.decimals + }; +} \ No newline at end of file diff --git a/packages/plugin-convergence/tsconfig.base.json b/packages/plugin-convergence/tsconfig.base.json new file mode 100644 index 0000000000..0fb59d8a9d --- /dev/null +++ b/packages/plugin-convergence/tsconfig.base.json @@ -0,0 +1,11 @@ +import { ConvergencePlugin } from '@eliza/plugin-convergence'; + +export const config = { + plugins: [ + new ConvergencePlugin({ + endpoint: process.env.SOLANA_RPC_ENDPOINT || 'https://api.mainnet-beta.solana.com', + commitment: 'confirmed' + }) + ], + // ... other configuration options +}; \ No newline at end of file diff --git a/packages/plugin-convergence/tsconfig.json b/packages/plugin-convergence/tsconfig.json new file mode 100644 index 0000000000..dda19ed966 --- /dev/null +++ b/packages/plugin-convergence/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + "include": ["src/**/*"], + "references": [ + { "path": "../core" } + ] + } \ No newline at end of file diff --git a/packages/plugin-convergence/yarn-error.log b/packages/plugin-convergence/yarn-error.log new file mode 100644 index 0000000000..622efab994 --- /dev/null +++ b/packages/plugin-convergence/yarn-error.log @@ -0,0 +1,69 @@ +Arguments: + /opt/homebrew/Cellar/node/23.1.0_1/bin/node /opt/homebrew/Cellar/yarn/1.22.19/libexec/bin/yarn.js install + +PATH: + /Users/uwecerron/.local/share/solana/install/active_release/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin:/Users/uwecerron/.local/share/solana/install/active_release/bin:/Users/uwecerron/.cargo/bin:/usr/local/bin:/Users/uwecerron/Library/Python/3.9/bin + +Yarn version: + 1.22.19 + +Node version: + 23.1.0 + +Platform: + darwin arm64 + +Trace: + Error: https://registry.yarnpkg.com/@eliza%2fcore: Not found + at params.callback [as _callback] (/opt/homebrew/Cellar/yarn/1.22.19/libexec/lib/cli.js:66145:18) + at self.callback (/opt/homebrew/Cellar/yarn/1.22.19/libexec/lib/cli.js:140890:22) + at Request.emit (node:events:507:28) + at Request. (/opt/homebrew/Cellar/yarn/1.22.19/libexec/lib/cli.js:141862:10) + at Request.emit (node:events:507:28) + at IncomingMessage. (/opt/homebrew/Cellar/yarn/1.22.19/libexec/lib/cli.js:141784:12) + at Object.onceWrapper (node:events:621:28) + at IncomingMessage.emit (node:events:519:35) + at endReadableNT (node:internal/streams/readable:1696:12) + at process.processTicksAndRejections (node:internal/process/task_queues:90:21) + +npm manifest: + { + "name": "eliza-convergence-plugin-workspace", + "version": "0.1.0", + "private": true, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "clean": "rimraf dist", + "dev": "tsc -w", + "lint": "eslint src --ext .ts", + "test": "jest" + }, + "dependencies": { + "@convergence-rfq/sdk": "^0.1.0", + "@eliza/core": "workspace:*", + "@solana/spl-token": "^0.3.8", + "@solana/web3.js": "^1.78.4", + "bn.js": "^5.2.1", + "decimal.js": "^10.4.3" + }, + "devDependencies": { + "@types/bn.js": "^5.1.1", + "@types/jest": "^29.5.3", + "@types/node": "^20.4.5", + "@typescript-eslint/eslint-plugin": "^6.2.0", + "@typescript-eslint/parser": "^6.2.0", + "eslint": "^8.45.0", + "jest": "^29.6.2", + "rimraf": "^5.0.1", + "ts-jest": "^29.1.1", + "typescript": "^5.1.6" + } + } + +yarn manifest: + No manifest + +Lockfile: + No lockfile