This guide explains how to migrate the iOS MPC implementation from direct WebSocket communication to HTTP endpoints via the backend proxy.
iOS App <--WebSocket--> Duo-Node <---> Silence Labs Server
iOS App <--HTTP--> Backend <--WebSocket--> Duo-Node <---> Silence Labs Server
ProfileAPI+MPC.swift- New HTTP endpoint methodsMPCWalletServiceHTTP.swift- Updated wallet service using HTTP
The MPCKeyShareManager needs new methods to generate P1 messages:
// Add to MPCKeyShareManager.swift
extension MPCKeyShareManager {
/// Generate initial P1 messages for key generation
func getInitialP1Messages() async throws -> [[String: Any]] {
// Use Silence Labs SDK to generate P1 messages
// This replaces the WebSocket message flow
let p1KeyGen = P1KeyGen(sessionId: UUID().uuidString, x1: generateRandomBytes())
let firstMessage = try await p1KeyGen.processMessage(nil)
return [firstMessage.toDictionary()]
}
/// Generate P1 messages for signing
func getSigningP1Messages(keyShare: MPCKeyShare, message: Data) async throws -> [[String: Any]] {
// Use Silence Labs SDK to generate signing messages
let p1Signature = P1Signature(
sessionId: UUID().uuidString,
messageHash: message,
p1KeyShare: keyShare.silenceLabsKeyShare
)
let firstMessage = try await p1Signature.processMessage(nil)
return [firstMessage.toDictionary()]
}
}In your views and view models, replace MPCWalletService with MPCWalletServiceHTTP:
// Before
@StateObject private var mpcService = MPCWalletService.shared
// After
@StateObject private var mpcService = MPCWalletServiceHTTP.shared// Example in a SwiftUI View
func generateWallet() async {
do {
showLoading = true
let walletInfo = try await mpcService.generateWallet(for: profile.id)
// Update UI with new wallet
self.walletAddress = walletInfo.address
showSuccess = true
} catch {
showError = true
errorMessage = error.localizedDescription
}
showLoading = false
}func signTransaction(_ transaction: TransactionRequest) async {
do {
let signature = try await mpcService.signTransaction(
profileId: profile.id,
transaction: transaction
)
// Use signature to submit transaction
await submitTransaction(signature: signature)
} catch {
handleError(error)
}
}UserDefaults.standard.set(true, forKey: "mpcWalletEnabled")- Create a new profile
- The profile should automatically attempt MPC wallet generation
- Verify the wallet address is stored
- Create a test transaction
- Sign it using the MPC wallet
- Verify the signature is valid
Update your app's configuration:
// Development
let API_BASE_URL = "https://interspace-backend-dev-784862970473.us-central1.run.app"
// Production
let API_BASE_URL = "https://api.interspace.chat"The new HTTP implementation includes better error handling:
enum MPCError: LocalizedError {
case operationInProgress
case keyShareNotFound
case websocketNotConnected // No longer used
case requestTimeout
case operationFailed(String)
case operationCancelled(String)
case unknown(Error)
var errorDescription: String? {
switch self {
case .operationInProgress:
return "Another MPC operation is in progress"
case .keyShareNotFound:
return "No MPC wallet found for this profile"
case .requestTimeout:
return "Operation timed out"
case .operationFailed(let reason):
return "Operation failed: \(reason)"
case .operationCancelled(let reason):
return "Operation cancelled: \(reason)"
case .unknown(let error):
return error.localizedDescription
}
}
}If issues arise, you can temporarily switch back to WebSocket:
- Keep both implementations (
MPCWalletServiceandMPCWalletServiceHTTP) - Use a feature flag to toggle between them
- Monitor error rates and performance
- Remove WebSocket dependencies once HTTP is stable
- Implement proper 2FA for backup/export operations
- Add analytics to track MPC operation success rates
- Implement retry logic for failed operations