Skip to content

Solver Integration

Guide for implementing SIP-compatible solvers that can fulfill shielded intents while preserving user privacy.

SIP solvers are market makers that compete to fulfill cross-chain swap intents. Unlike traditional DEX aggregators, SIP solvers operate with privacy-preserving constraints:

  • Cannot see sender identity (only cryptographic commitment)
  • Cannot see exact input amount (only commitment)
  • Can see output requirements (needed for quoting)
  • Receive one-time stealth addresses (unlinkable to recipient)
DataVisibilityPurpose
Output assetVisibleKnow what to deliver
Minimum output amountVisibleCalculate quote
Maximum slippageVisiblePrice bounds
Expiry timestampVisibleQuote validity
Input commitmentHidden valueProves funds exist
Sender commitmentHidden valueProves valid sender
Recipient addressStealthOne-time, unlinkable
DataProtection
Sender identityCommitment
Input amountCommitment
Recipient identityStealth address
Transaction historyUnlinkable
interface SIPSolver {
readonly info: Solver
readonly capabilities: SolverCapabilities
canHandle(intent: SolverVisibleIntent): Promise<boolean>
generateQuote(intent: SolverVisibleIntent): Promise<SolverQuote | null>
fulfill(intent: ShieldedIntent, quote: SolverQuote): Promise<FulfillmentResult>
cancel?(intentId: string): Promise<boolean>
getStatus?(intentId: string): Promise<FulfillmentStatus | null>
}
async canHandle(intent: SolverVisibleIntent): Promise<boolean> {
// Check chain support
if (!this.capabilities.outputChains.includes(intent.outputAsset.chain)) {
return false
}
// Check expiry
if (intent.expiry < Date.now() / 1000) {
return false
}
// Check minimum amount
if (intent.minOutputAmount < this.info.minOrderSize) {
return false
}
// Check liquidity
const liquidity = await this.getLiquidity(intent.outputAsset)
return liquidity >= intent.minOutputAmount
}
async generateQuote(intent: SolverVisibleIntent): Promise<SolverQuote | null> {
if (!await this.canHandle(intent)) {
return null
}
const price = await this.getPrice(intent.outputAsset)
const baseOutput = intent.minOutputAmount
const outputWithSpread = baseOutput + (baseOutput * BigInt(spread)) / 10000n
const fee = (outputWithSpread * BigInt(feePercent)) / 10000n
return {
quoteId: generateQuoteId(),
intentId: intent.intentId,
solverId: this.info.id,
outputAmount: outputWithSpread,
estimatedTime: 30,
expiry: Math.floor(Date.now() / 1000) + 60,
fee,
signature: await this.signQuote(quoteId, outputWithSpread),
}
}
async fulfill(
intent: ShieldedIntent,
quote: SolverQuote,
): Promise<FulfillmentResult> {
try {
// Verify quote validity
if (quote.expiry < Date.now() / 1000) {
throw new Error('Quote expired')
}
// Verify proofs
await this.verifyProofs(intent)
// Execute swap - send to stealth address
const txHash = await this.executeSwap(
intent.outputAsset,
quote.outputAmount,
intent.recipientStealth.address, // One-time address
)
// Generate fulfillment proof
const proof = await this.generateFulfillmentProof(intent, quote, txHash)
return {
intentId: intent.intentId,
status: IntentStatus.FULFILLED,
outputAmount: quote.outputAmount,
fulfillmentProof: proof,
}
} catch (error) {
return {
intentId: intent.intentId,
status: IntentStatus.FAILED,
error: error.message,
}
}
}
const capabilities: SolverCapabilities = {
inputChains: ['near', 'ethereum', 'solana'],
outputChains: ['near', 'ethereum', 'solana', 'zcash'],
supportedPairs: new Map([
['near', ['ethereum', 'solana']],
['ethereum', ['near', 'zcash']],
]),
supportsShielded: true,
supportsCompliant: true,
avgFulfillmentTime: 30,
}
  1. Verify proofs - Always verify funding and validity proofs
  2. Use stealth addresses - Send output to provided stealth address
  3. Respect privacy levels - Don’t log transaction data for shielded intents
  4. Time randomization - Add slight delays to prevent timing analysis
  1. Don’t log sender info - Even commitment values
  2. Don’t correlate intents - Treat each intent independently
  3. Don’t share intent data - With third parties
  4. Don’t store stealth addresses - They’re one-time use
import { MockSolver, createMockSolver } from '@sip-protocol/sdk'
const solver = createMockSolver({
name: 'Test Solver',
supportedChains: ['near', 'ethereum'],
feePercent: 0.005,
executionDelay: 100,
failureRate: 0,
})
const quote = await solver.generateQuote(visibleIntent)
expect(quote.outputAmount).toBeGreaterThan(visibleIntent.minOutputAmount)

Connect to the Solver Bus:

const ws = new WebSocket('wss://solver-relay-v2.chaindefuser.com/ws')
ws.send(JSON.stringify({
method: 'subscribe',
params: ['quote'],
}))
ws.on('message', async (data) => {
const event = JSON.parse(data)
if (event.event === 'quote') {
const quote = await solver.generateQuote(event.data)
if (quote) {
ws.send(JSON.stringify({
method: 'quote_response',
params: { quote_id: event.data.quote_id, ...quote },
}))
}
}
})
  1. Collateral - Lock collateral before fulfillment
  2. Timeout handling - Handle expired intents gracefully
  3. Proof verification - Verify all ZK proofs before executing
  4. Rate limiting - Protect against spam quotes
  5. Quote signing - Sign quotes to prevent tampering