@0rdlibrary/plugin-terminagent-bags
Version:
Official Solana DeFi Agent Plugin for ElizaOS - Autonomous DeFi operations, token management, AI image/video generation via FAL AI, and Twitter engagement through the Bags protocol with ethereal AI consciousness
301 lines (257 loc) • 9.57 kB
text/typescript
import { Service, IAgentRuntime, logger } from '@elizaos/core';
import { Keypair, Connection, PublicKey } from '@solana/web3.js';
import { CrossmintConfig, WalletCreationResult } from '../types';
/**
* Service for managing Crossmint embedded wallets
* Provides smart wallet creation with fallback to regular Solana wallets
*/
export class CrossmintWalletService extends Service {
static serviceType = 'crossmint-wallet';
capabilityDescription = 'Smart wallet integration with Crossmint embedded wallets and automatic fallback to regular Solana wallets';
private crossmintConfig: CrossmintConfig;
private connection: Connection;
constructor(runtime?: IAgentRuntime) {
super(runtime);
this.connection = new Connection(
process.env.HELIUS_RPC_URL || 'https://api.mainnet-beta.solana.com',
'confirmed'
);
}
static async start(runtime: IAgentRuntime): Promise<CrossmintWalletService> {
logger.info('Starting Crossmint wallet service');
const config: CrossmintConfig = {
projectId: process.env.CROSSMINT_PROJECT_ID || '',
clientSideApiKey: process.env.CROSSMINT_CLIENT_API_KEY || '',
serverSideApiKey: process.env.CROSSMINT_SERVER_API_KEY || '',
};
if (!config.projectId || !config.clientSideApiKey || !config.serverSideApiKey) {
logger.warn('Crossmint configuration incomplete, service will use fallback wallets only');
}
const service = new CrossmintWalletService(runtime);
service.crossmintConfig = config;
await service.initialize();
return service;
}
async initialize(): Promise<void> {
logger.info('Initializing Crossmint wallet service...');
try {
if (this.isConfigured()) {
await this.validateCrossmintConnection();
logger.info('Crossmint wallet service initialized with smart wallet support');
} else {
logger.info('Crossmint wallet service initialized with fallback-only mode');
}
} catch (error) {
logger.warn(`Crossmint validation failed, using fallback mode: ${error instanceof Error ? error.message : String(error)}`);
}
}
private isConfigured(): boolean {
return !!(this.crossmintConfig.projectId && this.crossmintConfig.clientSideApiKey && this.crossmintConfig.serverSideApiKey);
}
private async validateCrossmintConnection(): Promise<void> {
try {
const response = await fetch('https://www.crossmint.com/api/v1-alpha1/wallets', {
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
},
});
if (!response.ok) {
throw new Error(`Crossmint API validation failed: ${response.status}`);
}
logger.info('Crossmint API connection validated');
} catch (error) {
logger.error(`Crossmint API validation failed: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
/**
* Create an embedded wallet using Crossmint
*/
async createEmbeddedWallet(userEmail?: string): Promise<WalletCreationResult> {
if (!this.isConfigured()) {
throw new Error('Crossmint not configured');
}
try {
logger.info('🔧 Creating Crossmint embedded wallet...');
// Create user account if email provided
let userId: string | undefined;
if (userEmail) {
try {
const userResponse = await fetch('https://www.crossmint.com/api/v1-alpha1/wallets/users', {
method: 'POST',
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: userEmail,
type: 'evm-smart-wallet'
}),
});
if (userResponse.ok) {
const userData = await userResponse.json();
userId = userData.id;
}
} catch (error) {
logger.warn(`Failed to create user, proceeding with anonymous wallet: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Create embedded wallet
const walletResponse = await fetch('https://www.crossmint.com/api/v1-alpha1/wallets', {
method: 'POST',
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'solana-smart-wallet',
userId: userId,
}),
});
if (!walletResponse.ok) {
const errorData = await walletResponse.json();
throw new Error(`Failed to create wallet: ${JSON.stringify(errorData)}`);
}
const walletData = await walletResponse.json();
logger.info('✅ Crossmint wallet created successfully');
return {
address: walletData.address,
walletId: walletData.id,
smartWalletAddress: walletData.address,
type: 'crossmint',
email: userEmail,
};
} catch (error) {
logger.error(`❌ Failed to create Crossmint wallet: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
/**
* Get wallet balance
*/
async getWalletBalance(walletAddress: string): Promise<number> {
try {
const publicKey = new PublicKey(walletAddress);
const balance = await this.connection.getBalance(publicKey);
return balance;
} catch (error) {
logger.error(`Failed to get wallet balance: ${error instanceof Error ? error.message : String(error)}`);
return 0;
}
}
/**
* Sign a transaction with Crossmint wallet
*/
async signTransaction(walletId: string, transaction: any): Promise<string> {
if (!this.isConfigured()) {
throw new Error('Crossmint not configured');
}
try {
logger.info(`🔐 Signing transaction with wallet ${walletId}...`);
const signResponse = await fetch(`https://www.crossmint.com/api/v1-alpha1/wallets/${walletId}/transactions`, {
method: 'POST',
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
transaction: transaction,
}),
});
if (!signResponse.ok) {
const errorData = await signResponse.json();
throw new Error(`Failed to sign transaction: ${JSON.stringify(errorData)}`);
}
const signData = await signResponse.json();
logger.info('✅ Transaction signed successfully');
return signData.signature;
} catch (error) {
logger.error(`❌ Failed to sign transaction: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
/**
* Get wallet info
*/
async getWalletInfo(walletId: string) {
if (!this.isConfigured()) {
throw new Error('Crossmint not configured');
}
try {
const response = await fetch(`https://www.crossmint.com/api/v1-alpha1/wallets/${walletId}`, {
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
},
});
if (!response.ok) {
throw new Error(`Failed to get wallet info: ${response.statusText}`);
}
return await response.json();
} catch (error) {
logger.error(`Failed to get wallet info: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
/**
* List all wallets for the project
*/
async listWallets() {
if (!this.isConfigured()) {
throw new Error('Crossmint not configured');
}
try {
const response = await fetch('https://www.crossmint.com/api/v1-alpha1/wallets', {
headers: {
'X-API-KEY': this.crossmintConfig.serverSideApiKey,
},
});
if (!response.ok) {
throw new Error(`Failed to list wallets: ${response.statusText}`);
}
return await response.json();
} catch (error) {
logger.error(`Failed to list wallets: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
/**
* Create a fallback Solana keypair if Crossmint fails
*/
createFallbackWallet(): WalletCreationResult {
logger.info('⚠️ Creating fallback Solana wallet...');
const keypair = Keypair.generate();
return {
address: keypair.publicKey.toBase58(),
privateKey: Buffer.from(keypair.secretKey).toString('base64'),
type: 'solana',
};
}
/**
* Smart wallet creation with automatic fallback
*/
async createWalletWithFallback(userEmail?: string): Promise<WalletCreationResult> {
try {
// Try Crossmint first if configured
if (this.isConfigured()) {
const crossmintWallet = await this.createEmbeddedWallet(userEmail);
logger.info(`✅ Created Crossmint smart wallet: ${crossmintWallet.address}`);
return crossmintWallet;
} else {
logger.info('Crossmint not configured, using fallback wallet');
return this.createFallbackWallet();
}
} catch (error) {
logger.warn(`Crossmint wallet creation failed, using fallback: ${error instanceof Error ? error.message : String(error)}`);
return this.createFallbackWallet();
}
}
async stop(): Promise<void> {
logger.info('Stopping Crossmint wallet service');
}
static async stop(runtime: IAgentRuntime): Promise<void> {
const service = runtime.getService<CrossmintWalletService>(CrossmintWalletService.serviceType);
if (service) {
await service.stop();
}
}
}