UNPKG

@skynetxbt/venice-ai-plugin

Version:

Venice AI Plugin for Skynet Framework - Staking Contract Integration

381 lines (325 loc) 9.64 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { IPlugin, log } from "@skynetxbt/core"; import { EventEmitter } from "events"; import { ethers } from "ethers"; import { StakingContract } from "./StakingContract"; import { StakingContractConfig, StakingOperationResult, StakingViewResult } from "./types"; import { TokenOperationResult, VeniceToken } from "./VeniceToken"; import { STAKING_CONTRACT_ADDRESS } from "./constants"; import axios, { AxiosInstance } from "axios"; interface RateLimit { amount: number; type: "RPM" | "RPD" | "TPM"; } interface ModelRateLimit { apiModelId: string; rateLimits: RateLimit[]; } interface VeniceBalances { VCU: number; USD: number; } interface ApiTier { id: string; isCharged: boolean; } interface RateLimitResponse { data: { apiTier: ApiTier; accessPermitted: boolean; balances: VeniceBalances; rateLimits: ModelRateLimit[]; }; } export interface VenicePluginConfig { rpcUrl?: string; privateKey?: string; stakingContractAddress?: string; veniceTokenAddress?: string; veniceApiKey?: string; veniceBaseUrl?: string; } export class VenicePlugin extends EventEmitter implements IPlugin { readonly name: string = "venice-plugin"; readonly description: string = "Interactive plugin for Venice AI staking"; private stakingContract: StakingContract; private veniceToken: VeniceToken; private provider: ethers.Provider | null = null; private signer: ethers.Signer | null = null; private apiClient: AxiosInstance | null = null; constructor(private config: VenicePluginConfig = {}) { super(); const stakingConfig: StakingContractConfig = { contractAddress: config.stakingContractAddress, privateKey: config.privateKey, rpcUrl: config.rpcUrl }; this.stakingContract = new StakingContract(stakingConfig); this.veniceToken = new VeniceToken(config.veniceTokenAddress); if (config.veniceApiKey && config.veniceBaseUrl) { this.apiClient = axios.create({ baseURL: config.veniceBaseUrl, headers: { 'Authorization': `Bearer ${config.veniceApiKey}`, 'Content-Type': 'application/json' } }); } } async initialize(): Promise<void> { try { if (!this.config.rpcUrl) { throw new Error("RPC URL is required"); } const provider = new ethers.JsonRpcProvider(this.config.rpcUrl); let signer: ethers.Signer | undefined; if (this.config.privateKey) { signer = new ethers.Wallet(this.config.privateKey, provider); } log.info("Initializing with config:", { rpcUrl: this.config.rpcUrl, stakingContractAddress: this.config.stakingContractAddress || STAKING_CONTRACT_ADDRESS, hasSigner: !!signer }); await this.stakingContract.initialize(provider, signer); await this.veniceToken.initialize(provider, signer); this.provider = provider; this.signer = signer || null; this.emit("initialized"); } catch (error) { throw new Error(`Failed to initialize plugin: ${error instanceof Error ? error.message : String(error)}`); } } async cleanup(): Promise<void> { await this.stakingContract.cleanup(); await this.veniceToken.cleanup(); this.provider = null; this.signer = null; this.emit("cleanup"); } async getVCUsBalance(vvvBalance: string): Promise<string> { return (Number(vvvBalance) * 0.212).toString(); } async getModelInfo(modelId: string): Promise<string> { const veniceModel = await axios.get(`https://api.venice.xyz/models/${modelId}`); return JSON.stringify(veniceModel.data); } async getTokenBalance(address: string): Promise<TokenOperationResult> { try { const result = await this.veniceToken.getBalance(address); if (result.success && result.data) { return { success: true, data: { raw: result.data, formatted: ethers.formatUnits(result.data, 18) } }; } return result; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Check allowance for staking contract */ async checkAllowance(ownerAddress: string): Promise<TokenOperationResult> { try { const result = await this.veniceToken.getAllowance( ownerAddress, this.config.stakingContractAddress || STAKING_CONTRACT_ADDRESS ); if (result.success && result.data) { return { success: true, data: { raw: result.data, formatted: ethers.formatUnits(result.data, 18) } }; } return result; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Approve staking contract to spend tokens */ async approveStaking(amount: string): Promise<TokenOperationResult> { try { const amountBigInt = ethers.parseUnits(amount, 18); return await this.veniceToken.approve( this.config.stakingContractAddress || STAKING_CONTRACT_ADDRESS, amountBigInt ); } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } // Staking operations async stake(recipient: string, amount: string): Promise<StakingOperationResult> { try { const amountBigInt = ethers.parseUnits(amount, 18); return await this.stakingContract.stake(recipient, amountBigInt); } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async initiateUnstake(amount: string): Promise<StakingOperationResult> { try { const amountBigInt = ethers.parseUnits(amount, 18); return await this.stakingContract.initiateUnstake(amountBigInt); } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async finalizeUnstake(): Promise<StakingOperationResult> { return await this.stakingContract.finalizeUnstake(); } async claim(): Promise<StakingOperationResult> { return await this.stakingContract.claim(); } // View functions async getPendingRewards(userAddress: string): Promise<StakingViewResult<string>> { try { const result = await this.stakingContract.getPendingRewards(userAddress); if (result.success && result.data) { return { success: true, data: ethers.formatUnits(result.data, 18) }; } return { success: false, error: result.error }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async getVenicePercentage(): Promise<StakingViewResult<string>> { try { const result = await this.stakingContract.getVenicePercentage(); if (result.success && result.data) { return { success: true, data: ethers.formatUnits(result.data, 18) }; } return { success: false, error: result.error }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async getUserStakeInfo(userAddress: string): Promise<StakingViewResult<any>> { try { const result = await this.stakingContract.getUserStakeInfo(userAddress); if (result.success && result.data) { const { stakeAmount, pendingRewards, stakeInfo } = result.data; return { success: true, data: { stakeAmount: ethers.formatUnits(stakeAmount, 18), pendingRewards: ethers.formatUnits(pendingRewards, 18), stakeInfo: { rewardDebt: ethers.formatUnits(stakeInfo.rewardDebt, 18), cooldownEnd: stakeInfo.cooldownEnd.toString(), cooldownAmount: ethers.formatUnits(stakeInfo.cooldownAmount, 18) } } }; } return { success: false, error: result.error }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async getStakingStats(): Promise<StakingViewResult<any>> { try { const result = await this.stakingContract.getStakingStats(); if (result.success && result.data) { const { totalStaked, emissionRate, cooldownDuration, venicePercentage, accRewardPerShare, lastRewardTimestamp } = result.data; return { success: true, data: { totalStaked: ethers.formatUnits(totalStaked, 18), emissionRate: ethers.formatUnits(emissionRate, 18), cooldownDuration: cooldownDuration.toString(), venicePercentage: ethers.formatUnits(venicePercentage, 18), accRewardPerShare: ethers.formatUnits(accRewardPerShare, 36), // ACC_REWARD_SCALE is 1e36 lastRewardTimestamp: lastRewardTimestamp.toString() } }; } return { success: false, error: result.error }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async getRateLimits(): Promise<RateLimitResponse | null> { if (!this.apiClient) { throw new Error("Venice API client not initialized. Please provide API key and base URL."); } try { const response = await this.apiClient.get<RateLimitResponse>('/api/v1/api_keys/rate_limits'); return response.data; } catch (error) { log.error(`Failed to fetch rate limits: ${error}`); return null; } } async getModelRateLimits(modelId: string): Promise<ModelRateLimit | null> { const rateLimits = await this.getRateLimits(); if (!rateLimits) return null; return rateLimits.data.rateLimits.find(limit => limit.apiModelId === modelId) || null; } async getVeniceBalances(): Promise<VeniceBalances | null> { const rateLimits = await this.getRateLimits(); return rateLimits?.data.balances || null; } }