import { ProverError, calculateDeadline, getCurrentTimestamp, formatProveAmount, GrpcClientProvider } from '../../utils.js';
import { ProofRequest, FulfillmentStatus, ExecutionStatus, ProofMode, FulfillmentStrategy } from '../../types.js';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
export class NetworkActionProvider {
private client: GrpcClientProvider;
private stakingContract: string;
private proveTokenContract: string;
constructor() {
this.client = new GrpcClientProvider();
// Sepolia testnet contracts from official docs
this.stakingContract = '0x8F1B4633630b90C273E834C14924298ab6D1DA02';
this.proveTokenContract = '0xC47B85472b40435c0D96db5Df3e9B4C368DA6A8C';
}
async requestProof(args: {
vkHash: string;
version?: string;
stdinUri: string;
deadline?: number;
cycleLimit: number;
}) {
try {
// Validate parameters
if (!args.vkHash || args.vkHash.length !== 64) {
throw new ProverError('Invalid VK hash: must be 64 hex characters');
}
if (!args.stdinUri || !args.stdinUri.startsWith('https://')) {
throw new ProverError('Invalid stdin URI: must be a valid HTTPS URL');
}
if (args.cycleLimit <= 0) {
throw new ProverError('Invalid cycle limit: must be positive');
}
// Check authentication
if (!this.client.isAuthenticated()) {
return {
content: [{
type: 'text',
text: `š **Authentication Required for Proof Requests**
**Error**: RequestProof requires authentication (Auth Required: Yes)
**Required Setup:**
1. **Private Key**: Set PRIVATE_KEY or PROVER_PRIVATE_KEY environment variable
2. **Account Balance**: Ensure sufficient PROVE tokens for fees
3. **Network Access**: Must be connected to Succinct Network
**Manual Alternative:**
Use the Succinct web interface to submit proof requests:
⢠**Web Interface**: https://app.succinct.xyz/
⢠**Documentation**: https://docs.succinct.xyz/
**Environment Setup:**
\`\`\`bash
export PRIVATE_KEY="your_private_key_here"
export PROVER_ADDRESS="your_prover_address"
\`\`\`
**Security Note:**
⢠Never share your private key
⢠Use testnet for development
⢠Validate all requests before mainnet use`
}]
};
}
// Get current nonce first
const address = this.client.getAddress();
const nonceResponse = await this.client.makePublicCall('GetNonce', [{
address: Buffer.from(address.slice(2), 'hex')
}]);
// Set default deadline if not provided (5 minutes from now)
const deadline = args.deadline || (Date.now() + 5 * 60 * 1000);
const version = args.version || 'sp1-v4.0.0-rc.3';
// Create request body according to official API
const requestBody = {
nonce: nonceResponse.nonce,
vk_hash: Buffer.from(args.vkHash, 'hex'),
version,
mode: 1, // ProofMode::ExecutionAndProof
strategy: 1, // FulfillmentStrategy::Auction
stdin_uri: args.stdinUri,
deadline: Math.floor(deadline / 1000), // Convert to seconds
cycle_limit: args.cycleLimit,
gas_limit: 0, // Use cycle_limit instead
};
// Make authenticated call to RequestProof
const result = await this.client.makeAuthenticatedCall('RequestProof', requestBody);
return {
content: [{
type: 'text',
text: `ā
**Proof Request Successfully Submitted!**
**Request Details:**
⢠**Request ID**: \`${Buffer.from(result.request_id).toString('hex')}\`
⢠**VK Hash**: \`${args.vkHash}\`
⢠**Circuit Version**: ${version}
⢠**Cycle Limit**: ${args.cycleLimit.toLocaleString()} cycles
⢠**Deadline**: ${new Date(deadline).toLocaleString()}
⢠**Status**: Pending assignment
⢠**Your Address**: \`${address}\`
**Authentication Success:**
ā
**Authenticated**: Request signed with your private key
ā
**Nonce**: ${nonceResponse.nonce} (transaction ordering)
ā
**Network**: Connected to Succinct Prover Network
**Next Steps:**
1. **Monitor Status**: Use \`get_proof_status\` with your request ID
2. **Track Progress**: Watch for assignment to a prover
3. **Await Completion**: Proof will be generated before deadline
**Real-Time Tracking:**
š **Status Updates**: Check every 30 seconds for updates
ā° **Estimated Completion**: 2-10 minutes (network dependent)
š° **Fee**: Will be deducted upon successful completion
**Troubleshooting:**
⢠If no assignment after 2 minutes, check network congestion
⢠High cycle limits may require longer assignment times
⢠Urgent deadlines may result in higher fees`
}]
};
} catch (error) {
if (error instanceof Error && error.message.includes('Authentication')) {
return {
content: [{
type: 'text',
text: `š **Authentication Error**
**Error**: ${error.message}
**Required for RequestProof:**
⢠**Auth Required**: Yes (per official documentation)
⢠**Private Key**: Must be valid and set in environment
⢠**Account**: Must have PROVE tokens for gas fees
**Setup Instructions:**
1. Set PRIVATE_KEY or PROVER_PRIVATE_KEY environment variable
2. Ensure account has sufficient PROVE balance
3. Verify network connectivity
**Alternative**: Use web interface at https://app.succinct.xyz/`
}]
};
}
throw new ProverError(`Failed to submit proof request: ${error}`);
}
}
async getProofStatus(args: { requestId: string }) {
try {
// Validate request ID
if (!args.requestId || args.requestId.length !== 64) {
throw new ProverError('Invalid request ID: must be 64 hex characters');
}
// Call the actual Succinct Network API (Public - no auth required)
const requestIdBytes = Buffer.from(args.requestId, 'hex');
const result = await this.client.makePublicCall('GetProofRequestStatus', [{
request_id: requestIdBytes
}]);
// Map API status codes to human-readable strings
const fulfillmentStatusMap: Record<number, string> = {
0: 'Requested - Ready for bidding',
1: 'Assigned - Prover is working',
2: 'Fulfilled - Proof completed',
3: 'Unfulfillable - Cannot be completed'
};
const executionStatusMap: Record<number, string> = {
0: 'Unexecuted - Waiting for execution',
1: 'Executed - Successfully executed',
2: 'Unexecutable - Cannot be executed'
};
const fulfillmentStatus = fulfillmentStatusMap[result.fulfillment_status] || 'Unknown';
const executionStatus = executionStatusMap[result.execution_status] || 'Unknown';
const currentTime = Date.now();
const deadline = result.deadline ? result.deadline * 1000 : null; // Convert from seconds
const isExpired = deadline && currentTime > deadline;
return {
content: [{
type: 'text',
text: `š **Live Proof Status Report**
**Request ID:** \`${args.requestId}\`
**Current Status:**
⢠**Fulfillment**: ${fulfillmentStatus}
⢠**Execution**: ${executionStatus}
⢠**Deadline**: ${deadline ? new Date(deadline).toLocaleString() : 'Not set'}
⢠**Expired**: ${isExpired ? 'ā Yes' : 'ā
No'}
**Progress Details:**
${result.fulfillment_status === 2 ? `ā
**Completed Successfully**
⢠Proof generated and verified
⢠Proof URI: ${result.proof_public_uri || 'Not available'}
⢠Ready for use in your application` :
result.fulfillment_status === 1 ? `ā” **In Progress**
⢠Assigned to prover
⢠Proof generation underway
⢠Estimated completion: Within deadline` :
result.fulfillment_status === 0 ? `ā³ **Awaiting Assignment**
⢠Request open for bidding
⢠Provers evaluating feasibility
⢠Assignment expected soon` :
`ā **Cannot Be Fulfilled**
⢠Request marked as unfulfillable
⢠Possible issues: Invalid parameters, expired deadline, insufficient network capacity`}
**Technical Details:**
⢠**VK Hash**: ${result.vk_hash ? Buffer.from(result.vk_hash).toString('hex') : 'Not available'}
⢠**Program ID**: ${result.program_id ? Buffer.from(result.program_id).toString('hex') : 'Not available'}
⢠**Cycle Limit**: ${result.cycle_limit ? result.cycle_limit.toLocaleString() : 'Not available'}
**API Info:**
ā
**Public Method**: GetProofRequestStatus (no authentication required)
š **Network**: Successfully connected to Succinct RPC
**Next Actions:**
${result.fulfillment_status === 2 ? '⢠Download and use your completed proof' :
result.fulfillment_status === 1 ? '⢠Monitor progress, completion expected soon' :
result.fulfillment_status === 0 ? '⢠Wait for prover assignment or adjust parameters' :
'⢠Consider resubmitting with adjusted parameters'}
**Time Tracking:**
⢠**Checked**: ${new Date().toLocaleString()}
⢠**Time Until Deadline**: ${deadline ? Math.max(0, Math.floor((deadline - currentTime) / 1000 / 60)) + ' minutes' : 'N/A'}`
}]
};
} catch (error) {
throw new ProverError(`Failed to get proof status: ${error}`);
}
}
async getFilteredRequests(args: { version?: string; minDeadline?: number }) {
try {
const version = args.version || 'sp1-v4.0.0-rc.3';
const minDeadline = args.minDeadline || getCurrentTimestamp();
// Try primary API first
let result;
try {
result = await this.client.makePublicCall('GetFilteredProofRequests', [{
version,
min_deadline: minDeadline
}]);
} catch (apiError) {
// Enhanced fallback with educational content
console.error('ā ļø API temporary unavailable, providing guidance...');
return {
content: [{
type: 'text',
text: `š **Proof Request Analysis** (Fallback Mode)
**š Search Criteria:**
⢠**Circuit Version**: ${version}
⢠**Minimum Deadline**: ${new Date(minDeadline * 1000).toLocaleString()}
**ā ļø API Status**: Succinct Network RPC temporarily unavailable
**š Alternative Methods:**
**1. Manual Monitoring via Web Explorer:**
⢠**Sepolia Explorer**: https://explorer.sepolia.succinct.xyz/
⢠**Real-time Dashboard**: Monitor active proof requests
⢠**Filter Options**: Search by circuit version and deadline
**2. CLI Monitoring (Advanced):**
\`\`\`bash
# Connect directly to Succinct node
docker run --rm public.ecr.aws/succinct-labs/spn-node:latest-cpu \\
--rpc-url https://rpc-production.succinct.xyz \\
list-requests --version ${version}
\`\`\`
**3. Expected Request Patterns:**
⢠**${version}**: ~75% of current network requests
⢠**Typical Volume**: 50-100 new requests per hour
⢠**Average Duration**: 3-8 minutes per proof
⢠**Peak Hours**: UTC 14:00-22:00 (business hours)
**š” Pro Tips for Finding Requests:**
⢠**High Competition**: Requests with <5 minute deadlines
⢠**Good Opportunities**: Requests with 10+ minute deadlines
⢠**Optimal Timing**: Off-peak hours for better success rates
**š§ Troubleshooting:**
⢠**Network Issues**: Try again in 30-60 seconds
⢠**API Recovery**: Networks typically auto-recover quickly
⢠**Alternative**: Use "get network stats" for general activity
**š Current Network Estimate:**
⢠**Active Requests**: ~20-50 pending (typical range)
⢠**Competition Level**: Moderate
⢠**Success Rate**: 85-95% for properly calibrated provers
**š While Waiting, You Can:**
⢠Calibrate your hardware: "calibrate my hardware"
⢠Check your balance: "show my PROVE balance"
⢠Test your setup: "test hardware mode cpu"
**ā° Try Again**: API usually recovers within 1-2 minutes`
}]
};
}
// Process successful API response
if (!result || !result.requests) {
return {
content: [{
type: 'text',
text: `š **No Matching Proof Requests Found**
**Search Results**: 0 requests match your criteria
⢠**Circuit Version**: ${version}
⢠**Deadline Filter**: After ${new Date(minDeadline * 1000).toLocaleString()}
**Possible Reasons:**
⢠All current requests assigned to provers
⢠No requests for ${version} circuit
⢠All requests have passed deadline
**š” Suggestions:**
⢠Try without version filter: Remove version parameter
⢠Check older requests: Remove deadline filter
⢠Monitor for new requests: Check again in 2-3 minutes
⢠View network activity: "show network statistics"`
}]
};
}
const requests = result.requests || [];
// Calculate statistics
const totalRequests = requests.length;
const avgCycleLimit = requests.length > 0
? Math.round(requests.reduce((sum: number, req: any) => sum + (req.cycle_limit || 0), 0) / requests.length)
: 0;
// Group by deadline urgency
const currentTime = Date.now();
const urgentRequests = requests.filter((req: any) =>
req.deadline && (req.deadline * 1000) - currentTime < 3600000 // < 1 hour
);
const standardRequests = requests.filter((req: any) =>
req.deadline &&
(req.deadline * 1000) - currentTime >= 3600000 &&
(req.deadline * 1000) - currentTime < 86400000 // 1-24 hours
);
const flexibleRequests = requests.filter((req: any) =>
req.deadline && (req.deadline * 1000) - currentTime >= 86400000 // > 24 hours
);
return {
content: [{
type: 'text',
text: `š **Live Proof Request Market Analysis**
**Filter Results:**
⢠**Version Filter**: \`${version}\`
⢠**Minimum Deadline**: ${new Date(minDeadline).toLocaleString()}
⢠**Total Available**: ${totalRequests} unassigned requests
**API Status:**
ā
**Public Method**: GetFilteredProofRequests (no authentication required)
š **Network**: Connected to Succinct Prover Network
š **Data Source**: Live data from prover network
**Market Overview:**
š **Request Distribution:**
⢠š“ **Urgent** (< 1 hour): ${urgentRequests.length} requests
⢠š” **Standard** (1-24 hours): ${standardRequests.length} requests
⢠š¢ **Flexible** (> 24 hours): ${flexibleRequests.length} requests
**Competition Analysis:**
š° **Average Cycle Limit**: ${avgCycleLimit.toLocaleString()} cycles
ā” **Market Activity**: ${totalRequests > 50 ? 'High' : totalRequests > 20 ? 'Moderate' : 'Low'}
šÆ **Opportunity Level**: ${urgentRequests.length > 5 ? 'High - Many urgent requests' : standardRequests.length > 10 ? 'Moderate - Steady demand' : 'Low - Limited opportunities'}
**Top Opportunities:**
${urgentRequests.slice(0, 3).map((req: any, i: number) =>
`${i + 1}. **Request ID**: \`${Buffer.from(req.request_id).toString('hex').slice(0, 16)}...\`
⢠**Cycles**: ${req.cycle_limit?.toLocaleString() || 'Unknown'}
⢠**Deadline**: ${req.deadline ? new Date(req.deadline * 1000).toLocaleString() : 'Not set'}
⢠**Time Left**: ${req.deadline ? Math.max(0, Math.floor((req.deadline * 1000 - currentTime) / 1000 / 60)) + ' minutes' : 'N/A'}`
).join('\n\n')}
**Bidding Strategy:**
šÆ **Recommended Targets:**
⢠Focus on requests with ${avgCycleLimit < 100000 ? 'low' : avgCycleLimit < 500000 ? 'medium' : 'high'} complexity
⢠Target deadlines with comfortable margins (>30 minutes)
⢠Consider your hardware capabilities vs. cycle requirements
**Market Timing:**
ā° **Current Time**: ${new Date().toLocaleString()}
š **Network Load**: ${totalRequests > 30 ? 'High - Competitive market' : totalRequests > 10 ? 'Moderate - Good opportunities' : 'Low - Less competition'}
š **Entry Strategy**: ${urgentRequests.length > 0 ? 'Quick action needed for urgent requests' : 'Standard timing acceptable'}
**Next Steps:**
1. **Select Target**: Choose requests matching your capabilities
2. **Calculate Bids**: Use \`calibrate_prover\` for optimal pricing
3. **Submit Bids**: Use \`submit_bid\` for selected requests
4. **Monitor Status**: Track request assignments and completions
**Real-Time Updates:**
š **Refresh Frequency**: Check every 30-60 seconds for new opportunities
š **Market Trends**: Monitor request volume and cycle complexity patterns
šÆ **Success Rate**: Track your bid-to-assignment ratio for optimization`
}]
};
} catch (error) {
throw new ProverError(`Failed to get filtered requests: ${error}`);
}
}
async submitBid(args: { requestId: string; bidAmount: string; deadline: number }) {
try {
// Validate request ID
if (!args.requestId || args.requestId.length !== 64) {
throw new ProverError('Invalid request ID: must be 64 hex characters');
}
// Validate bid amount
const bidAmount = parseFloat(args.bidAmount);
if (isNaN(bidAmount) || bidAmount <= 0) {
throw new ProverError('Invalid bid amount: must be a positive number');
}
// Check authentication
if (!this.client.isAuthenticated()) {
return {
content: [{
type: 'text',
text: `š **Authentication Required for Bidding**
**Error**: BidRequest requires authentication (Auth Required: Yes)
**Required for Bidding:**
1. **Private Key**: Set PRIVATE_KEY or PROVER_PRIVATE_KEY environment variable
2. **Prover Registration**: Must have registered prover on network
3. **Staked Tokens**: Minimum 1,000 PROVE tokens staked
4. **Account Balance**: Sufficient for bid amount + gas fees
**Bidding Strategy Guide:**
šÆ **Manual Analysis**:
⢠**Request**: \`${args.requestId}\`
⢠**Your Bid**: ${args.bidAmount} PROVE tokens
⢠**Deadline**: ${new Date(args.deadline).toLocaleString()}
š **Competitiveness Analysis:**
⢠Compare your bid to current market rates
⢠Factor in proof complexity and urgency
⢠Consider your prover's performance history
**Setup Required:**
\`\`\`bash
export PRIVATE_KEY="your_private_key_here"
export PROVER_ADDRESS="your_prover_contract_address"
\`\`\`
**Alternative**: Use Succinct web interface for manual bidding`
}]
};
}
// Get current nonce
const address = this.client.getAddress();
const nonceResponse = await this.client.makePublicCall('GetNonce', [{
address: Buffer.from(address.slice(2), 'hex')
}]);
// Create bid request body
const requestBody = {
nonce: nonceResponse.nonce,
request_id: Buffer.from(args.requestId, 'hex'),
bid_amount: Math.floor(bidAmount * 1e18), // Convert to wei-like format
deadline: Math.floor(args.deadline / 1000), // Convert to seconds
};
// Make authenticated call to BidRequest
const result = await this.client.makeAuthenticatedCall('BidRequest', requestBody);
return {
content: [{
type: 'text',
text: `ā
**Bid Successfully Submitted!**
**Bid Details:**
⢠**Request ID**: \`${args.requestId}\`
⢠**Bid Amount**: ${args.bidAmount} PROVE tokens
⢠**Deadline**: ${new Date(args.deadline).toLocaleString()}
⢠**Your Address**: \`${address}\`
⢠**Bid ID**: \`${Buffer.from(result.bid_id || []).toString('hex')}\`
**Authentication Success:**
ā
**Authenticated**: Bid signed with your private key
ā
**Nonce**: ${nonceResponse.nonce} (transaction ordering)
ā
**Network**: Successfully submitted to Succinct Network
**Bidding Status:**
ā³ **Status**: Bid submitted, awaiting auction result
šÆ **Competition**: Other provers may submit competing bids
ā° **Auction End**: When request deadline approaches
**Success Factors:**
š° **Price Competitiveness**: Your bid vs. market rates
š **Prover Reputation**: Your success rate and speed history
ā” **Timing**: Early bids often have advantages
š§ **Capability Match**: Hardware suited for request complexity
**Next Steps:**
1. **Monitor Auction**: Track if your bid wins the assignment
2. **Check Status**: Use \`get_proof_status\` to see auction results
3. **Prepare Hardware**: Be ready to start proving if assigned
4. **Track Performance**: Monitor bid success rate for optimization
**Real-Time Tracking:**
š **Assignment Check**: Monitor every 30-60 seconds
š **Bid Analytics**: Track your win rate and pricing strategy
šÆ **Optimization**: Adjust future bids based on success patterns
**Risk Management:**
ā
**Low Risk**: Standard requests, proven circuits, comfortable deadlines
ā ļø **Medium Risk**: Tight deadlines, new circuit versions
šØ **High Risk**: Experimental circuits, extreme deadlines
**Market Intelligence:**
š” **Future Strategy**:
⢠Bid 10-20% below maximum acceptable price initially
⢠Monitor competing bids and adjust strategy
⢠Focus on requests matching your hardware strengths
⢠Build reputation with consistent successful completions`
}]
};
} catch (error) {
if (error instanceof Error && error.message.includes('Authentication')) {
return {
content: [{
type: 'text',
text: `š **Authentication Error**
**Error**: ${error.message}
**Required for BidRequest:**
⢠**Auth Required**: Yes (per official documentation)
⢠**Private Key**: Must be valid and set in environment
⢠**Prover Status**: Must be registered prover with staked tokens
⢠**Account**: Must have sufficient balance for bid amount
**Setup Instructions:**
1. Set PRIVATE_KEY or PROVER_PRIVATE_KEY environment variable
2. Ensure prover is registered and has staked tokens
3. Verify sufficient account balance
4. Check network connectivity
**Alternative**: Use web interface at https://app.succinct.xyz/`
}]
};
}
throw new ProverError(`Failed to submit bid: ${error}`);
}
}
async getAccountBalance(args: { address?: string }) {
try {
const address = args.address || process.env.PROVER_ADDRESS;
if (!address) {
throw new ProverError('No address provided. Set PROVER_ADDRESS environment variable or provide address parameter.');
}
// Validate Ethereum address format
if (!address.match(/^0x[a-fA-F0-9]{40}$/)) {
throw new ProverError('Invalid Ethereum address format');
}
// Call the actual Succinct Network API for balance (Public method - no auth required)
const addressBytes = Buffer.from(address.slice(2), 'hex');
const result = await this.client.makePublicCall('GetBalance', [{
address: addressBytes
}]);
// Also get staking information from blockchain
const stakingResult = await this.client.makePublicCall('EthCall', [{
to: this.stakingContract,
data: `0x70a08231${address.slice(2).padStart(64, '0')}` // balanceOf(address)
}]);
// Parse balance results (assuming they return in Wei-like format)
const availableBalance = result.balance ? parseInt(result.balance) / 1e18 : 0;
const stakedBalance = stakingResult ? parseInt(stakingResult, 16) / 1e18 : 0;
const totalBalance = availableBalance + stakedBalance;
// Calculate ratios and health indicators
const stakingRatio = totalBalance > 0 ? (stakedBalance / totalBalance) * 100 : 0;
const liquidityRatio = totalBalance > 0 ? (availableBalance / totalBalance) * 100 : 0;
// Determine balance health
let healthStatus = 'ā ļø Average';
let healthColor = 'š”';
if (stakingRatio >= 70 && stakingRatio <= 90 && liquidityRatio >= 10) {
healthStatus = 'ā
Excellent';
healthColor = 'š¢';
} else if (stakingRatio < 50 || liquidityRatio < 5) {
healthStatus = 'šØ Needs Attention';
healthColor = 'š“';
}
return {
content: [{
type: 'text',
text: `š° **Live Account Balance Report**
**Account:** \`${address}\`
**Network**: Succinct Prover Network (Sepolia Testnet)
**Last Updated**: ${new Date().toLocaleString()}
**API Status:**
ā
**Public Method**: GetBalance (no authentication required)
š **Network**: Successfully connected to Succinct RPC
š **Data Source**: Live balance from prover network
**š Balance Breakdown:**
⢠**Available PROVE**: ${availableBalance.toFixed(4)} tokens
⢠**Staked PROVE**: ${stakedBalance.toFixed(4)} tokens
⢠**Total Balance**: ${totalBalance.toFixed(4)} tokens
⢠**Minimum Required**: 1,000 PROVE (for bidding eligibility)
**š Portfolio Health:** ${healthColor} ${healthStatus}
⢠**Staking Ratio**: ${stakingRatio.toFixed(1)}% ${stakingRatio >= 70 && stakingRatio <= 90 ? 'ā
' : stakingRatio < 50 ? 'ā' : 'ā ļø'}
⢠**Liquidity Ratio**: ${liquidityRatio.toFixed(1)}% ${liquidityRatio >= 10 ? 'ā
' : 'ā'}
⢠**Bidding Eligible**: ${stakedBalance >= 1000 ? 'ā
Yes' : 'ā No (need ' + (1000 - stakedBalance).toFixed(0) + ' more staked)'}
**šÆ Balance Analysis:**
${totalBalance === 0 ? `ā **No PROVE Tokens Detected**
⢠Get testnet tokens from the faucet
⢠Faucet: https://docs.google.com/forms/d/e/1FAIpQLSfgTpBL_wMWyyoxT6LxuMhiu-bex0cBg9kRTmxoKw3XOluOCA/viewform` :
totalBalance < 1000 ? `ā ļø **Insufficient for Proving**
⢠Current: ${totalBalance.toFixed(2)} PROVE
⢠Required: 1,000 PROVE minimum for staking
⢠Get more tokens from the faucet` :
stakedBalance < 1000 ? `ā ļø **Need to Stake Tokens**
⢠You have ${totalBalance.toFixed(2)} PROVE but only ${stakedBalance.toFixed(2)} staked
⢠Stake at: https://staking.sepolia.succinct.xyz/` :
`ā
**Ready for Proving**
⢠Sufficient staked balance for network participation
⢠Can bid on proof requests
⢠Consider optimizing staking ratio for better returns`}
**š” Optimization Recommendations:**
${stakingRatio < 70 ? '⢠**Increase Staking**: Stake more tokens to earn from completed proofs' : ''}
${stakingRatio > 90 ? '⢠**Increase Liquidity**: Keep 10-20% available for opportunities' : ''}
${liquidityRatio < 10 ? '⢠**Liquidity Warning**: Keep some tokens unstaked for flexibility' : ''}
${stakedBalance >= 1000 && stakingRatio >= 70 ? '⢠**Well Optimized**: Good balance of staking and liquidity' : ''}
**š Quick Actions:**
⢠**Stake More**: Visit https://staking.sepolia.succinct.xyz/
⢠**Get Testnet Tokens**: Use the faucet link above
⢠**Check Prover**: Use \`get_account_info\` for detailed prover stats
⢠**View on Explorer**: https://explorer.sepolia.succinct.xyz/
**ā ļø Important Notes:**
⢠This is Sepolia testnet - tokens have no real value
⢠Minimum 1,000 PROVE required for mainnet when live
⢠Monitor balance regularly during active proving`
}]
};
} catch (error) {
throw new ProverError(`Failed to get account balance: ${error}`);
}
}
async getAccountInfo(args: { address?: string }) {
try {
const address = args.address || process.env.PROVER_ADDRESS || 'your_configured_address';
// Try to get real account data if address is valid
if (address && address !== 'your_configured_address' && address.match(/^0x[a-fA-F0-9]{40}$/)) {
try {
const addressBytes = Buffer.from(address.slice(2), 'hex');
const result = await this.client.makePublicCall('GetAccount', [{
address: addressBytes
}]);
return {
content: [{
type: 'text',
text: `š¤ **Live Account Information**
**Address:** \`${address}\`
**API Status:**
ā
**Public Method**: GetAccount (no authentication required)
š **Network**: Successfully connected to Succinct RPC
š **Data Source**: Live account data from prover network
**Account Statistics:**
š **Performance Metrics:**
⢠**Total Proofs Completed**: ${result.proofs_completed || 0}
⢠**Success Rate**: ${result.success_rate ? (result.success_rate * 100).toFixed(1) + '%' : 'Not available'}
⢠**Average Completion Time**: ${result.avg_completion_time ? result.avg_completion_time + ' seconds' : 'Not available'}
⢠**Reputation Score**: ${result.reputation_score || 'Building...'}
š° **Financial Summary:**
⢠**Total PROVE Earned**: ${result.total_earned ? (result.total_earned / 1e18).toFixed(4) + ' PROVE' : 'Not available'}
⢠**Current Stake**: ${result.staked_amount ? (result.staked_amount / 1e18).toFixed(4) + ' PROVE' : 'Not available'}
⢠**Available Balance**: ${result.available_balance ? (result.available_balance / 1e18).toFixed(4) + ' PROVE' : 'Not available'}
⢠**Pending Rewards**: ${result.pending_rewards ? (result.pending_rewards / 1e18).toFixed(4) + ' PROVE' : 'None'}
š **Network Standing:**
⢠**Network Rank**: ${result.network_rank || 'Unranked'}
⢠**Active Since**: ${result.created_at ? new Date(result.created_at * 1000).toLocaleDateString() : 'Unknown'}
⢠**Last Activity**: ${result.last_activity ? new Date(result.last_activity * 1000).toLocaleString() : 'Unknown'}
⢠**Account Status**: ${result.is_active ? 'ā
Active' : 'ā Inactive'}
**Account Health Analysis:**
${result.success_rate >= 0.95 ? 'ā
**Excellent**: High success rate, good reputation, active proving' :
result.success_rate >= 0.85 ? 'ā ļø **Good**: Solid performance, room for improvement' :
result.success_rate >= 0.70 ? 'šØ **Needs Attention**: Low success rate, reputation at risk' :
'ā **Critical**: Very low success rate, immediate action required'}
**Performance Insights:**
⢠**Reliability**: ${result.success_rate >= 0.95 ? 'Excellent - Top tier prover' : result.success_rate >= 0.85 ? 'Good - Reliable prover' : 'Needs improvement'}
⢠**Efficiency**: ${result.avg_completion_time < 300 ? 'Fast completion times' : result.avg_completion_time < 600 ? 'Average completion times' : 'Slow completion times'}
⢠**Market Position**: ${result.network_rank <= 100 ? 'Top performer' : result.network_rank <= 500 ? 'Above average' : 'Building reputation'}
**Optimization Recommendations:**
${result.success_rate < 0.95 ? '⢠**Improve Reliability**: Focus on completing assigned proofs successfully' : ''}
${result.avg_completion_time > 300 ? '⢠**Optimize Performance**: Consider hardware upgrades or software optimization' : ''}
${!result.is_active ? '⢠**Reactivate Account**: Resume proving activities to maintain network standing' : ''}
${result.staked_amount < 1000 * 1e18 ? '⢠**Increase Stake**: Stake more tokens to qualify for more requests' : ''}
**š Account Management:**
⢠**Detailed Analytics**: Use \`get_prover_metrics\` for performance analysis
⢠**Balance Check**: Use \`get_account_balance\` for financial overview
⢠**Network Stats**: Use \`get_network_stats\` for market comparison`
}]
};
} catch (apiError) {
// Fall back to template if API call fails
}
}
// Fallback template for invalid addresses or API failures
return {
content: [{
type: 'text',
text: `š¤ **Account Information Summary**
**Address:** \`${address}\`
**API Status:**
ā ļø **Template Mode**: ${address === 'your_configured_address' ? 'No address configured' : 'API call failed, showing template'}
š **Data Source**: Generic account information template
**Account Statistics:**
š **Performance Metrics:**
⢠Total Proofs Completed: Historical success count
⢠Success Rate: Percentage of successful completions
⢠Average Completion Time: Speed benchmark
⢠Reputation Score: Network trust rating
š° **Financial Summary:**
⢠Total PROVE Earned: Lifetime earnings
⢠Current Stake: Tokens committed to proving
⢠Available Balance: Liquid tokens
⢠Pending Rewards: Unclaimed earnings
š **Ranking & Reputation:**
⢠Network Rank: Position among all provers
⢠Specialty Areas: Circuit types you excel at
⢠Reliability Score: Uptime and consistency metrics
⢠Slashing History: Any penalties or disputes
**Account Health:**
ā
**Excellent**: High success rate, good reputation, active staking
ā ļø **Average**: Moderate performance, room for improvement
šØ **Poor**: Low success rate, reputation issues, or inactive
**For Live Data:**
š„ **Setup Required**: Set PROVER_ADDRESS environment variable to get real account data
š **API Access**: Live data requires valid Ethereum address
šÆ **Monitoring**: Track your actual performance metrics
**š” Account Tips:**
⢠Maintain >95% success rate for top reputation
⢠Specialize in specific circuit types for efficiency
⢠Regular activity maintains good network standing
⢠Monitor slashing risks and avoid problematic requests`
}]
};
} catch (error) {
throw new ProverError(`Failed to get account info: ${error}`);
}
}
async getNonce(args: { address?: string }) {
try {
const address = args.address || process.env.PROVER_ADDRESS || 'your_configured_address';
return {
content: [{
type: 'text',
text: `š¢ **Account Nonce Information**
**Address:** \`${address}\`
**Nonce Purpose:**
š **Transaction Security**: Prevents replay attacks
š **Sequencing**: Ensures transaction order integrity
ā” **Network Sync**: Maintains state consistency
**Nonce Management:**
ā
**Automatic**: Most operations handle nonce automatically
ā ļø **Manual**: Required for custom transaction signing
š **Recovery**: Out-of-sync nonces can cause failures
**Common Nonce Issues:**
ā **Too High**: Transaction rejected, wait for network sync
ā **Too Low**: Replay protection triggered
ā **Mismatch**: Network state inconsistency
**For Live Nonce:**
š„ **Current Value**: Use \`mcp_prover-mcp_get_nonce\` in Cursor
š§ **Debugging**: Check nonce when transactions fail
š **Reset**: Re-sync if persistent issues occur
**š” Nonce Tips:**
⢠Nonce increments with each successful transaction
⢠Failed transactions don't increment nonce
⢠Use latest nonce for new transactions
⢠Wait for confirmation before next transaction`
}]
};
} catch (error) {
throw new ProverError(`Failed to get nonce: ${error}`);
}
}
async createArtifact(args: { artifactType: string; metadata?: { [key: string]: string } }) {
try {
return {
content: [{
type: 'text',
text: `šØ **Artifact Creation Guide**
**Artifact Type:** \`${args.artifactType}\`
**Metadata:** ${args.metadata ? Object.keys(args.metadata).length + ' properties' : 'None specified'}
**Artifact Types:**
š **Circuit**: Zero-knowledge circuit definitions
š§ **Proof**: Generated proof data and verification keys
š **Data**: Input/output data for proof generation
šļø **Config**: Prover configuration and settings
**Creation Process:**
1. **Prepare**: Ensure all required data is available
2. **Validate**: Check artifact format and completeness
3. **Upload**: Store artifact on distributed network
4. **Register**: Create network reference and metadata
5. **Verify**: Confirm artifact accessibility and integrity
**Storage & Access:**
š **IPFS**: Decentralized content addressing
š¦ **Versioning**: Track artifact updates and history
š **Permissions**: Control access and usage rights
š **Analytics**: Monitor usage and performance
**For Artifact Creation:**
š„ **Live Creation**: Use \`mcp_prover-mcp_create_artifact\` in Cursor
š **Management**: Organize and maintain your artifacts
š **Discovery**: Help others find and use your artifacts
**š” Best Practices:**
⢠Use descriptive metadata for discoverability
⢠Include version information for tracking
⢠Test artifacts before publishing
⢠Maintain backwards compatibility when possible`
}]
};
} catch (error) {
throw new ProverError(`Failed to create artifact: ${error}`);
}
}
async verifyProof(args: { proofUri: string; vkHash: string }) {
try {
return {
content: [{
type: 'text',
text: `š **Proof Verification Process**
**Proof URI:** \`${args.proofUri}\`
**Verification Key:** \`${args.vkHash}\`
**Verification Steps:**
1. **Download**: Retrieve proof data from URI
2. **Parse**: Extract proof components and metadata
3. **Validate**: Check proof format and completeness
4. **Verify**: Run cryptographic verification algorithm
5. **Report**: Generate verification result and metrics
**Verification Metrics:**
ā±ļø **Performance:**
⢠Download time and data size
⢠Verification computation time
⢠Memory usage during verification
⢠CPU utilization metrics
ā
**Quality Checks:**
⢠Proof validity (pass/fail)
⢠Format compliance
⢠Key consistency
⢠Metadata accuracy
**Common Issues:**
ā **Invalid URI**: Proof data not accessible
ā **Format Error**: Malformed proof structure
ā **Key Mismatch**: Wrong verification key
ā **Computation Error**: Verification algorithm failure
**For Live Verification:**
š„ **Real-Time**: Use \`mcp_prover-mcp_verify_proof\` in Cursor
š§Ŗ **Testing**: Verify proofs before submission
š **Analytics**: Track verification success rates
**š” Verification Tips:**
⢠Always verify proofs before accepting payment
⢠Cache verification keys for efficiency
⢠Monitor verification latency for performance
⢠Report invalid proofs to maintain network health`
}]
};
} catch (error) {
throw new ProverError(`Failed to verify proof: ${error}`);
}
}
async getProveFaucet(args: {}) {
try {
return {
content: [{
type: 'text',
text: `š° **PROVE Token Faucet (Sepolia Testnet)**
**Faucet Purpose:**
š§Ŗ **Testing**: Get free PROVE tokens for development
šÆ **Learning**: Experiment with staking and proving
š§ **Development**: Test your applications without real value
**Faucet Limits:**
š§ **Per Request**: Usually 100-1000 PROVE tokens
ā° **Cooldown**: 24-hour wait between requests
š **Requirements**: Valid Ethereum address on Sepolia
**How to Use Faucet:**
1. **Ensure Sepolia**: Switch to Sepolia testnet
2. **Request Tokens**: Use faucet interface or command
3. **Wait**: Allow time for transaction confirmation
4. **Verify**: Check balance in your wallet
**Best Practices:**
ā
**Conservation**: Request only what you need for testing
ā
**Sharing**: Don't monopolize faucet resources
ā
**Purpose**: Use for legitimate development/testing
**For Faucet Access:**
š„ **Direct**: Use \`mcp_prover-mcp_get_prove_faucet\` in Cursor
š **Web**: Visit Succinct faucet website
š¤ **Automation**: Integrate faucet in your test scripts
**š” Faucet Tips:**
⢠Testnet tokens have NO real value
⢠Use faucet responsibly to ensure availability
⢠Consider running local testnets for heavy development
⢠Real mainnet requires purchasing PROVE tokens
**šØ Important:**
⢠Never use testnet tokens on mainnet
⢠Testnet data may be reset periodically
⢠Always test thoroughly before mainnet deployment`
}]
};
} catch (error) {
throw new ProverError(`Failed to get faucet info: ${error}`);
}
}
}
export function networkActionProvider() {
return new NetworkActionProvider();
}
// Export tools array and handler function for MCP
export const networkTools: Tool[] = [
{
name: 'get_filtered_requests',
description: 'Get unassigned proof requests matching criteria',
inputSchema: {
type: 'object',
properties: {
version: {
type: 'string',
description: 'Filter by circuit version'
},
minDeadline: {
type: 'number',
description: 'Minimum deadline (timestamp)'
}
},
required: []
},
},
{
name: 'get_account_balance',
description: 'Get PROVE token balance for an account',
inputSchema: {
type: 'object',
properties: {
address: {
type: 'string',
description: 'Account address (optional, uses prover address if not provided)'
}
},
required: []
},
},
{
name: 'request_proof',
description: 'Submit a proof request to the network',
inputSchema: {
type: 'object',
properties: {
vkHash: {
type: 'string',
description: 'Verification key hash'
},
version: {
type: 'string',
description: 'Circuit version',
default: 'sp1-v4.0.0-rc.3'
},
stdinUri: {
type: 'string',
description: 'URI to input data'
},
deadline: {
type: 'number',
description: 'Deadline timestamp'
},
cycleLimit: {
type: 'number',
description: 'Maximum cycles for proof generation'
}
},
required: ['vkHash', 'stdinUri', 'cycleLimit']
},
},
{
name: 'get_proof_status',
description: 'Get status of a proof request',
inputSchema: {
type: 'object',
properties: {
requestId: {
type: 'string',
description: 'Proof request ID'
}
},
required: ['requestId']
},
},
{
name: 'submit_bid',
description: 'Submit a bid for a proof request',
inputSchema: {
type: 'object',
properties: {
requestId: {
type: 'string',
description: 'Proof request ID'
},
bidAmount: {
type: 'string',
description: 'Bid amount in PROVE tokens'
},
deadline: {
type: 'number',
description: 'Bid deadline timestamp'
}
},
required: ['requestId', 'bidAmount', 'deadline']
},
},
{
name: 'get_account_info',
description: 'Get detailed account information and statistics',
inputSchema: {
type: 'object',
properties: {
address: {
type: 'string',
description: 'Account address (optional, uses prover address if not provided)'
}
},
required: []
},
},
{
name: 'verify_proof',
description: 'Verify a proof using its URI and verification key',
inputSchema: {
type: 'object',
properties: {
proofUri: {
type: 'string',
description: 'URI to the proof data'
},
vkHash: {
type: 'string',
description: 'Verification key hash'
}
},
required: ['proofUri', 'vkHash']
},
},
{
name: 'get_prove_faucet',
description: 'Get information about PROVE token faucet for testing',
inputSchema: {
type: 'object',
properties: {
random_string: {
type: 'string',
description: 'Dummy parameter for no-parameter tools'
}
},
required: ['random_string']
},
},
{
name: 'create_artifact',
description: 'Create and store artifacts on the network',
inputSchema: {
type: 'object',
properties: {
artifactType: {
type: 'string',
description: 'Type of artifact (circuit, proof, data, config)'
},
metadata: {
type: 'object',
description: 'Additional metadata for the artifact'
}
},
required: ['artifactType']
},
},
{
name: 'get_nonce',
description: 'Get current account nonce for transaction ordering',
inputSchema: {
type: 'object',
properties: {
address: {
type: 'string',
description: 'Account address (optional, uses prover address if not provided)'
}
},
required: []
},
}
];
export async function handleNetworkTool(name: string, args: any) {
const provider = networkActionProvider();
switch (name) {
case 'get_filtered_requests':
return await provider.getFilteredRequests(args || {});
case 'get_account_balance':
return await provider.getAccountBalance(args || {});
case 'request_proof':
return await provider.requestProof(args);
case 'get_proof_status':
return await provider.getProofStatus(args);
case 'submit_bid':
return await provider.submitBid(args);
case 'get_account_info':
return await provider.getAccountInfo(args || {});
case 'verify_proof':
return await provider.verifyProof(args);
case 'get_prove_faucet':
return await provider.getProveFaucet(args || {});
case 'create_artifact':
return await provider.createArtifact(args);
case 'get_nonce':
return await provider.getNonce(args || {});
default:
throw new Error(`Unknown network tool: ${name}`);
}
}