seiai
Version:
The Future Of AI On Sei
546 lines (498 loc) • 17.1 kB
JavaScript
import axios from 'axios';
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
import { SigningStargateClient, StargateClient } from '@cosmjs/stargate';
import { ChatOpenAI } from '@langchain/openai';
import { ChatAnthropic } from '@langchain/anthropic';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { ChatGroq } from '@langchain/groq';
import { ConversationChain } from 'langchain/chains';
import { BufferMemory } from 'langchain/memory';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
class SeiWallet {
async importWallet(mnemonicPhrase) {
try {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonicPhrase, { prefix: 'sei' });
const [account] = await wallet.getAccounts();
return {
address: account.address,
publicKey: account.pubkey,
wallet
};
} catch (e) {
throw new Error(`Invalid mnemonic: ${e.message}`);
}
}
async _importWalletInstance(mnemonicPhrase) {
try {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonicPhrase, { prefix: 'sei' });
return wallet;
} catch (e) {
throw new Error(`Invalid mnemonic: ${e.message}`);
}
}
}
class SeiTransaction {
constructor(rpcNodes, chainId, wallet) {
this.rpcNodes = rpcNodes;
this.chainId = chainId;
this.wallet = wallet;
this.currentRpcIndex = 0;
}
async buildAndSignTx(toAddress, amount, denom = 'usei') {
try {
const client = await SigningStargateClient.connectWithSigner(
this.rpcNodes[this.currentRpcIndex],
this.wallet
);
const [account] = await this.wallet.getAccounts();
return await client.sendTokens(account.address, toAddress, [{
denom,
amount: amount.toString()
}], 'auto');
} catch (e) {
throw new Error(`Transaction failed: ${e.message}`);
}
}
async checkTxStatus(txHash) {
try {
const client = await StargateClient.connect(this.rpcNodes[this.currentRpcIndex]);
const result = await client.getTx(txHash);
return result;
} catch (error) {
throw new Error(`Failed to check transaction status: ${error.message}`);
}
}
// Added method for DEX transaction building (assuming a simplified message structure)
async buildDexTransaction(message) {
const client = await SigningStargateClient.connectWithSigner(
this.rpcNodes[this.currentRpcIndex],
this.wallet
);
const [account] = await this.wallet.getAccounts();
const tx = await client.signAndBroadcast(account.address, [message], 'auto');
return tx;
}
}
class DeFiData {
async getTokenPrice(tokenAddress, source = 'dexscreener') {
try {
if (source === 'dexscreener') {
const { data } = await axios.get(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`);
return data.pairs?.[0]?.priceUsd || 0;
}
return 0;
} catch (e) {
return 0;
}
}
}
class AIEngine {
constructor(config) {
this.model = config.model;
this.apiKey = config.apiKey;
this.systemPrompt = config.systemPrompt;
this.maxTokens = config.maxTokens || 300;
this.temperature = config.temperature || 0.7;
this.feature_type = config.feature_type;
this.memory = new BufferMemory();
this.initLLM();
}
initLLM() {
try {
switch (this.model) {
case 'openai':
this.llm = new ChatOpenAI({
modelName: 'gpt-4-turbo-preview', // Latest GPT-4 model
apiKey: this.apiKey,
maxTokens: this.maxTokens,
temperature: this.temperature
});
break;
case 'anthropic':
this.llm = new ChatAnthropic({
modelName: 'claude-3-opus-20240229', // Latest Claude model
apiKey: this.apiKey,
maxTokens: this.maxTokens,
temperature: this.temperature
});
break;
case 'gemini':
const genAI = new GoogleGenerativeAI(this.apiKey);
this.llm = genAI.getGenerativeModel({
model: "gemini-pro",
generationConfig: {
maxOutputTokens: this.maxTokens,
temperature: this.temperature
}
});
break;
case 'groq':
this.llm = new ChatGroq({
modelName: 'mixtral-8x7b-32768', // Latest Mixtral model
apiKey: this.apiKey,
maxTokens: this.maxTokens,
temperature: this.temperature
});
break;
default:
throw new Error('Unsupported model');
}
const prompt = ChatPromptTemplate.fromMessages([
['system', this.systemPrompt || `You are a helpful AI assistant specializing in ${this.feature_type || 'general tasks'}`],
MessagesPlaceholder('history'),
['human', '{input}']
]);
this.chain = new ConversationChain({
llm: this.llm,
memory: this.memory,
prompt
});
} catch (error) {
console.error('Failed to initialize LLM:', error);
throw error;
}
}
async analyze(input, defiData = null) {
try {
const message = defiData ? `${input} (Current SEI price: $${defiData} USD)` : input;
const response = await this.chain.call({ input: message });
return response.response;
} catch (error) {
console.error('AI chat error:', error);
return 'Sorry, I encountered an error processing your request.';
}
}
}
class CustomChatbot {
constructor(config) {
this.engine = new AIEngine({
model: config.model,
apiKey: config.apiKey,
systemPrompt: config.systemPrompt,
maxTokens: config.maxTokens,
temperature: config.temperature
});
this.intents = config.intents || {};
this.defiData = config.defiData;
}
async processMessage(message) {
try {
const price = await this.defiData?.getTokenPrice('SEI');
for (const [intent, handler] of Object.entries(this.intents)) {
if (message.toLowerCase().includes(intent.toLowerCase())) {
const response = await handler(message, price);
if (response) return response;
}
}
return this.engine.analyze(message, price);
} catch (error) {
console.error('Message processing error:', error);
return 'Sorry, I could not process your message.';
}
}
}
class SeiAISDK {
constructor(config) {
if (!config?.rpcNodes?.length) {
throw new Error("Please provide at least one RPC node in the configuration");
}
if (!config?.ai?.model || !config?.ai?.apiKey) {
throw new Error("AI configuration (model and apiKey) must be provided");
}
this.config = config;
this.aiConfig = {
model: config.ai.model,
apiKey: config.ai.apiKey,
maxTokens: config.ai.maxTokens || 300,
temperature: config.ai.temperature || 0.7
};
this.wallet = new SeiWallet();
this.defiData = new DeFiData();
this.rpcNodes = config.rpcNodes;
this.chainId = config.chainId || 'atlantic-2';
this.aiAgents = {
market: new AIEngine(this.aiConfig),
trading: new AIEngine({ ...this.aiConfig, feature_type: 'trading_signals' }),
chat: new AIEngine({ ...this.aiConfig, feature_type: 'chatbot' })
};
this.tx_handler = null; // Initialize transaction handler
}
async importWallet(mnemonic) {
const result = await this.wallet.importWallet(mnemonic);
this.walletInstance = await this.wallet._importWalletInstance(mnemonic);
this.tx_handler = new SeiTransaction(this.rpcNodes, this.chainId, this.walletInstance); // Assign tx handler after wallet import
return result;
}
async getTokenPrice(token, source = 'dexscreener') {
return await this.defiData.getTokenPrice(token, source);
}
async analyzeMarket(data) {
return await this.aiAgents.market.analyze(data);
}
async generateTradingSignals(data) {
return await this.aiAgents.trading.analyze(data);
}
async chat(message) {
const price = await this.getTokenPrice('SEI');
return await this.aiAgents.chat.analyze(message, price);
}
createCustomChatbot(config = {}) {
return new CustomChatbot({
model: config.model || this.aiConfig.model,
apiKey: config.apiKey || this.aiConfig.apiKey,
intents: config.intents,
systemPrompt: config.systemPrompt,
maxTokens: config.maxTokens || this.aiConfig.maxTokens,
temperature: config.temperature || this.aiConfig.temperature,
defiData: this.defiData
});
}
async sendTokens(toAddress, amount, denom = 'usei') {
if (!this.tx_handler) {
throw new Error('Wallet not imported');
}
return await this.tx_handler.buildAndSignTx(toAddress, amount, denom);
}
async checkTxStatus(txHash) {
if (!this.tx_handler) {
throw new Error('Wallet not imported');
}
return await this.tx_handler.checkTxStatus(txHash);
}
// Added DEX swap function
async swapTokensDex(tokenFrom, tokenTo, amount, slippage = 0.5) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
try {
const swapMsg = {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", // Example type URL - adjust as needed for your DEX
value: {
sender: this.walletInstance.getAccounts()[0].address,
contract: "YOUR_DEX_CONTRACT_ADDRESS", // Replace with your DEX contract address
msg: Buffer.from(JSON.stringify({
swap_exact_tokens_for_tokens: {
amount_in: String(Math.floor(amount * 1e6)),
min_amount_out: String(Math.floor(amount * (1 - slippage / 100) * 1e6)),
path: [tokenFrom, tokenTo],
recipient: this.walletInstance.getAccounts()[0].address
}
})).toString('base64'),
funds: [] // Add funds if needed
}
};
return await this.tx_handler.buildDexTransaction(swapMsg);
} catch (error) {
console.error("DEX swap failed:", error);
throw error;
}
}
// Added add liquidity function
async addLiquidity(tokenA, tokenB, amountA, amountB) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
try {
const addLiquidityMsg = {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", // Example type URL - adjust as needed for your DEX
value: {
sender: this.walletInstance.getAccounts()[0].address,
contract: "YOUR_DEX_CONTRACT_ADDRESS", // Replace with your DEX contract address
msg: Buffer.from(JSON.stringify({
add_liquidity: {
token_a: tokenA,
token_b: tokenB,
amount_a_desired: String(Math.floor(amountA * 1e6)),
amount_b_desired: String(Math.floor(amountB * 1e6)),
min_shares: "1"
}
})).toString('base64'),
funds: [] // Add funds if necessary.
}
};
return await this.tx_handler.buildDexTransaction(addLiquidityMsg);
} catch (error) {
console.error("Adding liquidity failed:", error);
throw error;
}
}
async stake_tokens(validator_address, amount, denom = "usei") {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const stakeMsg = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: {
delegatorAddress: this.walletInstance.getAccounts()[0].address,
validatorAddress: validator_address,
amount: { denom, amount: String(Math.floor(amount * 1e6)) }
}
};
return await this.tx_handler.buildDexTransaction(stakeMsg);
}
async unstake_tokens(validator_address, amount, denom = "usei") {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const unstakeMsg = {
typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate",
value: {
delegatorAddress: this.walletInstance.getAccounts()[0].address,
validatorAddress: validator_address,
amount: { denom, amount: String(Math.floor(amount * 1e6)) }
}
};
return await this.tx_handler.buildDexTransaction(unstakeMsg);
}
async vote_governance(proposal_id, vote) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const voteMsg = {
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: {
proposalId: proposal_id,
voter: this.walletInstance.getAccounts()[0].address,
option: vote
}
};
return await this.tx_handler.buildDexTransaction(voteMsg);
}
async create_limit_order(pair, side, price, quantity) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const orderMsg = {
typeUrl: "/sei.dex.MsgPlaceLimitOrder",
value: {
creator: this.walletInstance.getAccounts()[0].address,
pair,
side, // "BUY" or "SELL"
price: String(price),
quantity: String(quantity),
orderType: "LIMIT"
}
};
return await this.tx_handler.buildDexTransaction(orderMsg);
}
async cancel_order(orderId) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const cancelMsg = {
typeUrl: "/sei.dex.MsgCancelOrder",
value: {
creator: this.walletInstance.getAccounts()[0].address,
orderId
}
};
return await this.tx_handler.buildDexTransaction(cancelMsg);
}
async get_order_history() {
try {
const [account] = await this.walletInstance.getAccounts();
const response = await axios.get(`${this.rpcNodes[0]}/sei/orders/${account.address}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch order history: ${error.message}`);
}
}
async get_validator_list() {
try {
const response = await axios.get(`${this.rpcNodes[0]}/cosmos/staking/v1beta1/validators`);
return response.data.validators;
} catch (error) {
throw new Error(`Failed to fetch validators: ${error.message}`);
}
}
async get_governance_proposals() {
try {
const response = await axios.get(`${this.rpcNodes[0]}/cosmos/gov/v1beta1/proposals`);
return response.data.proposals;
} catch (error) {
throw new Error(`Failed to fetch proposals: ${error.message}`);
}
}
async create_market_order(pair, side, quantity) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const orderMsg = {
typeUrl: "/sei.dex.MsgPlaceMarketOrder",
value: {
creator: this.walletInstance.getAccounts()[0].address,
pair,
side,
quantity: String(quantity),
orderType: "MARKET"
}
};
return await this.tx_handler.buildDexTransaction(orderMsg);
}
async get_order_book(pair) {
try {
const response = await axios.get(`${this.rpcNodes[0]}/sei/orderbook/${pair}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch order book: ${error.message}`);
}
}
async compound_rewards(validator_address) {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
const compoundMsg = {
typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
value: {
delegatorAddress: this.walletInstance.getAccounts()[0].address,
validatorAddress: validator_address
}
};
return await this.tx_handler.buildDexTransaction(compoundMsg);
}
async get_market_depth(pair) {
try {
const response = await axios.get(`${this.rpcNodes[0]}/sei/market/depth/${pair}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch market depth: ${error.message}`);
}
}
async get_price_history(pair, timeframe = "1h") {
try {
const response = await axios.get(`${this.rpcNodes[0]}/sei/price/history/${pair}/${timeframe}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch price history: ${error.message}`);
}
}
async analyze_liquidity_pools() {
try {
const response = await axios.get(`${this.rpcNodes[0]}/sei/liquiditypools`);
return response.data;
} catch (error) {
throw new Error(`Failed to analyze liquidity pools: ${error.message}`);
}
}
async get_account_positions() {
if (!this.tx_handler) {
throw new Error("Wallet not imported");
}
try {
const [account] = await this.walletInstance.getAccounts();
const response = await axios.get(`${this.rpcNodes[0]}/sei/positions/${account.address}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch positions: ${error.message}`);
}
}
async get_market_summary(pair) {
try {
const response = await axios.get(`${this.rpcNodes[0]}/sei/market/summary/${pair}`);
return response.data;
} catch (error) {
throw new Error(`Failed to fetch market summary: ${error.message}`);
}
}
}
export default SeiAISDK;