Build Information
Successful build of SwiftOgmios, reference 0.3.2 (0279eb), with Swift 6.3 for macOS (SPM) on 28 Sep 2026 16:16:46 UTC.
Swift 6 data race errors: 0
Build Command
env DEVELOPER_DIR=/Applications/Xcode-26.5.0.app xcrun swift build --arch arm64Build Log
405 + }
406 + }
407 + }
408 + }
409 + }
410 +
411 + // Check inputs for watched addresses (requires UTxO lookup)
412 + // This would typically require maintaining a UTxO set
413 +
414 + return false
415 + }
416 + }
417 +
418 + // Usage in block processing
419 + private let transactionFilter = TransactionFilter(
420 + addresses: ["addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer..."],
421 + assets: ["3542acb3a64d80c29302260d62c3b87a742ad14abf855ebc6733081e.546f6b656e41"]
422 + )
423 +
424 + private func processBlock(_ block: Block) async {
425 + guard let transactions = block.body else { return }
426 +
427 + let relevantTransactions = transactions.filter { transaction in
428 + transactionFilter.isRelevant(transaction)
429 + }
430 +
431 + if !relevantTransactions.isEmpty {
432 + print("Found \(relevantTransactions.count) relevant transactions in block \(block.header.blockHeight)")
433 +
434 + for transaction in relevantTransactions {
435 + await handleRelevantTransaction(transaction)
436 + }
437 + }
438 + }
439 + ```
440 |
441 | ### Building a Block Database
warning: Unknown option 'swift path' in code block.
--> Tutorials/ChainSynchronization.md:443:1-499:4
441 | ### Building a Block Database
442 |
443 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
444 + actor BlockDatabase {
445 + private var blocks: [BlockHeight: Block] = [:]
446 + private var transactions: [String: Transaction] = [:]
447 + private var tipHeight: BlockHeight = 0
448 +
449 + func addBlock(_ block: Block) {
450 + blocks[block.header.blockHeight] = block
451 + tipHeight = max(tipHeight, block.header.blockHeight)
452 +
453 + // Index transactions
454 + if let txs = block.body {
455 + for tx in txs {
456 + transactions[tx.id] = tx
457 + }
458 + }
459 + }
460 +
461 + func rollbackTo(_ height: BlockHeight) {
462 + // Remove blocks after the rollback point
463 + let blocksToRemove = blocks.keys.filter { $0 > height }
464 +
465 + for blockHeight in blocksToRemove {
466 + if let block = blocks[blockHeight] {
467 + // Remove transactions from this block
468 + if let txs = block.body {
469 + for tx in txs {
470 + transactions.removeValue(forKey: tx.id)
471 + }
472 + }
473 +
474 + blocks.removeValue(forKey: blockHeight)
475 + }
476 + }
477 +
478 + tipHeight = height
479 + }
480 +
481 + func getBlock(at height: BlockHeight) -> Block? {
482 + return blocks[height]
483 + }
484 +
485 + func getTransaction(_ id: String) -> Transaction? {
486 + return transactions[id]
487 + }
488 +
489 + func getTipHeight() -> BlockHeight {
490 + return tipHeight
491 + }
492 +
493 + func getBlockRange(from: BlockHeight, to: BlockHeight) -> [Block] {
494 + return (from...to).compactMap { height in
495 + blocks[height]
496 + }
497 + }
498 + }
499 + ```
500 |
501 | ## Error Handling and Resilience
warning: Unknown option 'swift path' in code block.
--> Tutorials/ChainSynchronization.md:503:1-621:4
501 | ## Error Handling and Resilience
502 |
503 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
504 + actor ResilientChainSync {
505 + private let client: OgmiosClient
506 + private let database: BlockDatabase
507 + private var isRunning = false
508 + private let maxRetries = 3
509 +
510 + init(client: OgmiosClient) {
511 + self.client = client
512 + self.database = BlockDatabase()
513 + }
514 +
515 + func startSync() async {
516 + guard !isRunning else { return }
517 + isRunning = true
518 +
519 + var retryCount = 0
520 +
521 + while isRunning && retryCount < maxRetries {
522 + do {
523 + await performSync()
524 + retryCount = 0 // Reset on success
525 + } catch {
526 + retryCount += 1
527 + print("Chain sync error (attempt \(retryCount)): \(error)")
528 +
529 + if retryCount < maxRetries {
530 + // Exponential backoff
531 + let delay = min(pow(2.0, Double(retryCount)), 60.0)
532 + try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
533 + } else {
534 + print("Max retries exceeded, stopping sync")
535 + isRunning = false
536 + }
537 + }
538 + }
539 + }
540 +
541 + private func performSync() async throws {
542 + // Get current tip from database to resume sync
543 + let currentTip = await database.getTipHeight()
544 +
545 + // Find intersection point
546 + let knownPoints = await buildIntersectionPoints(from: currentTip)
547 + let intersection = try await client.chainSync.findIntersection.execute(
548 + points: knownPoints,
549 + id: .generateNextNanoId()
550 + )
551 +
552 + // Handle intersection result
553 + switch intersection.result {
554 + case .intersectionFound(let point):
555 + print("Resuming sync from: \(point)")
556 + case .intersectionNotFound:
557 + print("No intersection found, starting from genesis")
558 + }
559 +
560 + // Stream blocks
561 + while isRunning {
562 + let blockResponse = try await client.chainSync.nextBlock.execute(
563 + id: .generateNextNanoId()
564 + )
565 +
566 + await handleBlockResponse(blockResponse.result)
567 + }
568 + }
569 +
570 + private func buildIntersectionPoints(from tipHeight: BlockHeight) async -> [Point] {
571 + // Build intersection points from recent blocks
572 + var points: [Point] = []
573 +
574 + // Add recent blocks for intersection finding
575 + for height in stride(from: tipHeight, through: max(0, tipHeight - 100), by: -10) {
576 + if let block = await database.getBlock(at: height) {
577 + let point = Point(slot: block.header.slot, id: block.header.hash)
578 + points.append(point)
579 + }
580 + }
581 +
582 + return points
583 + }
584 +
585 + private func handleBlockResponse(_ result: NextBlockResult) async {
586 + switch result.direction {
587 + case .forward:
588 + await handleForwardBlock(result.block)
589 + case .backward:
590 + await handleRollback(result.block)
591 + }
592 + }
593 +
594 + private func handleForwardBlock(_ block: NextBlockResult.Block) async {
595 + switch block {
596 + case .block(let blockData):
597 + await database.addBlock(blockData)
598 + print("Added block: \(blockData.header.blockHeight)")
599 +
600 + case .origin:
601 + print("Reached origin")
602 + }
603 + }
604 +
605 + private func handleRollback(_ point: NextBlockResult.Block) async {
606 + switch point {
607 + case .block(let blockData):
608 + await database.rollbackTo(blockData.header.blockHeight)
609 + print("Rolled back to block: \(blockData.header.blockHeight)")
610 +
611 + case .origin:
612 + await database.rollbackTo(0)
613 + print("Rolled back to genesis")
614 + }
615 + }
616 +
617 + func stop() {
618 + isRunning = false
619 + }
620 + }
621 + ```
622 |
623 | ## Performance Considerations
warning: Unknown option 'swift path' in code block.
--> Tutorials/ChainSynchronization.md:627:1-665:4
625 | ### Batch Processing
626 |
627 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
628 + actor BatchProcessor {
629 + private var pendingBlocks: [Block] = []
630 + private let batchSize = 10
631 + private let processingQueue = DispatchQueue(label: "block-processing", qos: .utility)
632 +
633 + func addBlock(_ block: Block) async {
634 + pendingBlocks.append(block)
635 +
636 + if pendingBlocks.count >= batchSize {
637 + await processBatch()
638 + }
639 + }
640 +
641 + func flush() async {
642 + if !pendingBlocks.isEmpty {
643 + await processBatch()
644 + }
645 + }
646 +
647 + private func processBatch() async {
648 + let batch = pendingBlocks
649 + pendingBlocks.removeAll()
650 +
651 + await withTaskGroup(of: Void.self) { group in
652 + for block in batch {
653 + group.addTask {
654 + await self.processBlockConcurrently(block)
655 + }
656 + }
657 + }
658 + }
659 +
660 + private func processBlockConcurrently(_ block: Block) async {
661 + // Heavy processing work
662 + print("Processing block \(block.header.blockHeight) concurrently")
663 + }
664 + }
665 + ```
666 |
667 | ### Memory Management
warning: Unknown option 'swift path' in code block.
--> Tutorials/ChainSynchronization.md:669:1-693:4
667 | ### Memory Management
668 |
669 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
670 + actor MemoryEfficientChainSync {
671 + private let maxBlocksInMemory = 1000
672 + private var blockHeights: [BlockHeight] = []
673 + private weak var delegate: ChainSyncDelegate?
674 +
675 + func addBlock(_ block: Block) async {
676 + // Process immediately to avoid memory buildup
677 + await delegate?.didReceiveBlock(block)
678 +
679 + blockHeights.append(block.header.blockHeight)
680 +
681 + // Cleanup old blocks if memory limit exceeded
682 + if blockHeights.count > maxBlocksInMemory {
683 + let oldestHeight = blockHeights.removeFirst()
684 + await delegate?.shouldCleanupBlock(at: oldestHeight)
685 + }
686 + }
687 + }
688 +
689 + protocol ChainSyncDelegate: Actor {
690 + func didReceiveBlock(_ block: Block) async
691 + func shouldCleanupBlock(at height: BlockHeight) async
692 + }
693 + ```
694 |
695 | ## Testing Chain Sync
warning: Unknown option 'swift path' in code block.
--> Tutorials/ChainSynchronization.md:697:1-762:4
695 | ## Testing Chain Sync
696 |
697 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
698 + import XCTest
699 + @testable import SwiftOgmios
700 +
701 + class ChainSyncTests: XCTestCase {
702 + func testBasicChainSync() async throws {
703 + let mockClient = try await OgmiosClient(
704 + httpOnly: false,
705 + webSocketConnection: MockWebSocketConnection()
706 + )
707 +
708 + let manager = ChainSyncManager(client: mockClient)
709 + var receivedBlocks: [Block] = []
710 +
711 + let blockStream = await manager.createBlockStream()
712 +
713 + // Collect first few results
714 + var resultCount = 0
715 + for try await result in blockStream {
716 + switch result {
717 + case .blockReceived(let blockResult):
718 + if case .block(let block) = blockResult.block {
719 + receivedBlocks.append(block)
720 + }
721 +
722 + case .intersectionFound(_):
723 + break
724 + }
725 +
726 + resultCount += 1
727 + if resultCount >= 5 { break }
728 + }
729 +
730 + XCTAssertGreaterThan(receivedBlocks.count, 0)
731 + }
732 +
733 + func testRollbackHandling() async throws {
734 + let database = BlockDatabase()
735 +
736 + // Add some blocks
737 + let block1 = createMockBlock(height: 100)
738 + let block2 = createMockBlock(height: 101)
739 + let block3 = createMockBlock(height: 102)
740 +
741 + await database.addBlock(block1)
742 + await database.addBlock(block2)
743 + await database.addBlock(block3)
744 +
745 + XCTAssertEqual(await database.getTipHeight(), 102)
746 +
747 + // Simulate rollback
748 + await database.rollbackTo(100)
749 +
750 + XCTAssertEqual(await database.getTipHeight(), 100)
751 + XCTAssertNil(await database.getBlock(at: 101))
752 + XCTAssertNil(await database.getBlock(at: 102))
753 + XCTAssertNotNil(await database.getBlock(at: 100))
754 + }
755 +
756 + private func createMockBlock(height: BlockHeight) -> Block {
757 + // Create mock block for testing
758 + // Implementation depends on your Block structure
759 + fatalError("Implement mock block creation")
760 + }
761 + }
762 + ```
763 |
764 | ## Best Practices
warning: Extraneous content found after a link in task group list item
--> Tutorials/ChainSynchronization.md:776:3-776:55
774 | ## See Also
775 |
776 + - <doc:GettingStarted> - Basic setup and first queries
| ╰─suggestion: Remove extraneous content
777 | - <doc:ErrorHandling> - Handling chain sync errors and reconnections
778 | - <doc:AdvancedUsage> - Advanced concurrency patterns for chain sync
warning: Extraneous content found after a link in task group list item
--> Tutorials/ChainSynchronization.md:777:3-777:69
775 |
776 | - <doc:GettingStarted> - Basic setup and first queries
777 + - <doc:ErrorHandling> - Handling chain sync errors and reconnections
| ╰─suggestion: Remove extraneous content
778 | - <doc:AdvancedUsage> - Advanced concurrency patterns for chain sync
779 | - <doc:TransactionSubmission> - Submitting transactions after chain sync
warning: Extraneous content found after a link in task group list item
--> Tutorials/ChainSynchronization.md:778:3-778:69
776 | - <doc:GettingStarted> - Basic setup and first queries
777 | - <doc:ErrorHandling> - Handling chain sync errors and reconnections
778 + - <doc:AdvancedUsage> - Advanced concurrency patterns for chain sync
| ╰─suggestion: Remove extraneous content
779 | - <doc:TransactionSubmission> - Submitting transactions after chain sync
780 | - <doc:MempoolMonitoring> - Monitoring transactions in real-time
warning: Extraneous content found after a link in task group list item
--> Tutorials/ChainSynchronization.md:779:3-779:73
777 | - <doc:ErrorHandling> - Handling chain sync errors and reconnections
778 | - <doc:AdvancedUsage> - Advanced concurrency patterns for chain sync
779 + - <doc:TransactionSubmission> - Submitting transactions after chain sync
| ╰─suggestion: Remove extraneous content
780 | - <doc:MempoolMonitoring> - Monitoring transactions in real-time
warning: Extraneous content found after a link in task group list item
--> Tutorials/ChainSynchronization.md:780:3-780:65
778 | - <doc:AdvancedUsage> - Advanced concurrency patterns for chain sync
779 | - <doc:TransactionSubmission> - Submitting transactions after chain sync
780 + - <doc:MempoolMonitoring> - Monitoring transactions in real-time
| ╰─suggestion: Remove extraneous content
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:15:1-25:4
13 | ### OgmiosError Types
14 |
15 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
16 + public enum OgmiosError: LocalizedError, Sendable {
17 + case httpError(String)
18 + case websocketError(String)
19 + case responseError(String)
20 + case invalidResponse(String)
21 + case timeoutError(String)
22 + case invalidFormat(String)
23 + case invalidMethodError(String)
24 + }
25 + ```
26 |
27 | #### Transport Errors
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:30:1-37:4
28 |
29 | **HTTP Errors**: Connection and HTTP-specific failures
30 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
31 + do {
32 + let response = try await client.ledgerStateQuery.epoch.execute()
33 + } catch OgmiosError.httpError(let message) {
34 + print("HTTP connection failed: \(message)")
35 + // Handle: retry with backoff, switch to backup server, notify user
36 + }
37 + ```
38 |
39 | **WebSocket Errors**: WebSocket connection and communication failures
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:40:1-47:4
38 |
39 | **WebSocket Errors**: WebSocket connection and communication failures
40 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
41 + do {
42 + let response = try await client.chainSync.nextBlock.execute()
43 + } catch OgmiosError.websocketError(let message) {
44 + print("WebSocket error: \(message)")
45 + // Handle: reconnect, fallback to HTTP, notify user of connection loss
46 + }
47 + ```
48 |
49 | #### Protocol Errors
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:52:1-62:4
50 |
51 | **Response Errors**: Ogmios server returned an error response
52 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
53 + do {
54 + let response = try await client.ledgerStateQuery.constitution.execute()
55 + } catch OgmiosError.responseError(let message) {
56 + print("Server error: \(message)")
57 + // Parse error code for specific handling
58 + if message.contains("2001") {
59 + print("Era mismatch - query not available in current era")
60 + }
61 + }
62 + ```
63 |
64 | **Invalid Response**: Malformed or unexpected response format
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:65:1-72:4
63 |
64 | **Invalid Response**: Malformed or unexpected response format
65 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
66 + do {
67 + let response = try await client.ledgerStateQuery.tip.execute()
68 + } catch OgmiosError.invalidResponse(let message) {
69 + print("Invalid response format: \(message)")
70 + // Handle: log for debugging, retry, report to monitoring
71 + }
72 + ```
73 |
74 | #### Application Errors
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:77:1-84:4
75 |
76 | **Timeout Errors**: Operations that exceed time limits
77 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
78 + do {
79 + try await client.mempoolMonitor.waitForEmptyMempool(timeoutSeconds: 30.0)
80 + } catch OgmiosError.timeoutError(let message) {
81 + print("Operation timed out: \(message)")
82 + // Handle: extend timeout, cancel operation, notify user
83 + }
84 + ```
85 |
86 | ## JSON-RPC Error Codes
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:111:1-145:4
109 | ### Error Code Detection
110 |
111 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
112 + extension OgmiosError {
113 + var errorCode: Int? {
114 + switch self {
115 + case .responseError(let message):
116 + // Extract error code from message
117 + let codeRegex = try! NSRegularExpression(pattern: #"(\d{4})"#)
118 + let range = NSRange(message.startIndex..<message.endIndex, in: message)
119 +
120 + if let match = codeRegex.firstMatch(in: message, options: [], range: range) {
121 + let codeRange = Range(match.range(at: 1), in: message)!
122 + return Int(String(message[codeRange]))
123 + }
124 + return nil
125 + default:
126 + return nil
127 + }
128 + }
129 +
130 + var isRetryable: Bool {
131 + switch self {
132 + case .httpError(_), .websocketError(_):
133 + return true // Network errors are typically retryable
134 + case .responseError(let message):
135 + // Some response errors are retryable
136 + guard let code = errorCode else { return false }
137 + return code == 2003 // Acquired expired
138 + case .timeoutError(_):
139 + return true
140 + default:
141 + return false
142 + }
143 + }
144 + }
145 + ```
146 |
147 | ## Retry Strategies
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:151:1-178:4
149 | ### Basic Retry Pattern
150 |
151 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
152 + func withRetry<T>(
153 + operation: @escaping () async throws -> T,
154 + maxAttempts: Int = 3,
155 + delay: TimeInterval = 1.0
156 + ) async throws -> T {
157 + var lastError: Error?
158 +
159 + for attempt in 1...maxAttempts {
160 + do {
161 + return try await operation()
162 + } catch let error as OgmiosError where error.isRetryable && attempt < maxAttempts {
163 + print("Attempt \(attempt) failed, retrying in \(delay)s: \(error.localizedDescription)")
164 + lastError = error
165 + try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
166 + } catch {
167 + throw error // Non-retryable error or final attempt
168 + }
169 + }
170 +
171 + throw lastError ?? OgmiosError.responseError("Max attempts exceeded")
172 + }
173 +
174 + // Usage
175 + let epochResponse = try await withRetry {
176 + return try await client.ledgerStateQuery.epoch.execute()
177 + }
178 + ```
179 |
180 | ### Exponential Backoff
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:182:1-207:4
180 | ### Exponential Backoff
181 |
182 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
183 + func withExponentialBackoff<T>(
184 + operation: @escaping () async throws -> T,
185 + maxAttempts: Int = 3,
186 + baseDelay: TimeInterval = 1.0,
187 + maxDelay: TimeInterval = 60.0
188 + ) async throws -> T {
189 + var lastError: Error?
190 +
191 + for attempt in 1...maxAttempts {
192 + do {
193 + return try await operation()
194 + } catch let error as OgmiosError where error.isRetryable && attempt < maxAttempts {
195 + let delay = min(baseDelay * pow(2.0, Double(attempt - 1)), maxDelay)
196 + print("Attempt \(attempt) failed, retrying in \(String(format: "%.1f", delay))s")
197 +
198 + lastError = error
199 + try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
200 + } catch {
201 + throw error
202 + }
203 + }
204 +
205 + throw lastError ?? OgmiosError.responseError("Max attempts exceeded")
206 + }
207 + ```
208 |
209 | ### Circuit Breaker Pattern
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:211:1-270:4
209 | ### Circuit Breaker Pattern
210 |
211 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
212 + actor CircuitBreaker {
213 + enum State {
214 + case closed // Normal operation
215 + case open // Failing fast
216 + case halfOpen // Testing recovery
217 + }
218 +
219 + private var state: State = .closed
220 + private var failureCount = 0
221 + private var lastFailureTime: Date?
222 + private let failureThreshold = 5
223 + private let timeout: TimeInterval = 30.0
224 +
225 + func execute<T>(_ operation: @escaping () async throws -> T) async throws -> T {
226 + switch state {
227 + case .open:
228 + if let lastFailure = lastFailureTime,
229 + Date().timeIntervalSince(lastFailure) > timeout {
230 + state = .halfOpen
231 + } else {
232 + throw OgmiosError.responseError("Circuit breaker is open")
233 + }
234 +
235 + case .halfOpen, .closed:
236 + break
237 + }
238 +
239 + do {
240 + let result = try await operation()
241 + await onSuccess()
242 + return result
243 + } catch {
244 + await onFailure()
245 + throw error
246 + }
247 + }
248 +
249 + private func onSuccess() {
250 + failureCount = 0
251 + state = .closed
252 + }
253 +
254 + private func onFailure() {
255 + failureCount += 1
256 + lastFailureTime = Date()
257 +
258 + if failureCount >= failureThreshold {
259 + state = .open
260 + }
261 + }
262 + }
263 +
264 + // Usage
265 + let circuitBreaker = CircuitBreaker()
266 +
267 + let response = try await circuitBreaker.execute {
268 + return try await client.ledgerStateQuery.epoch.execute()
269 + }
270 + ```
271 |
272 | ## Comprehensive Error Handling Service
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:274:1-402:4
272 | ## Comprehensive Error Handling Service
273 |
274 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
275 + @MainActor
276 + class OgmiosErrorHandler: ObservableObject {
277 + @Published var connectionStatus: ConnectionStatus = .disconnected
278 + @Published var lastError: OgmiosError?
279 + @Published var errorHistory: [ErrorRecord] = []
280 +
281 + enum ConnectionStatus {
282 + case connected
283 + case disconnected
284 + case reconnecting
285 + case failed
286 + }
287 +
288 + struct ErrorRecord {
289 + let error: OgmiosError
290 + let timestamp: Date
291 + let operation: String
292 + }
293 +
294 + private let maxErrorHistory = 100
295 +
296 + func handle(_ error: Error, operation: String) {
297 + guard let ogmiosError = error as? OgmiosError else {
298 + print("Non-Ogmios error: \(error)")
299 + return
300 + }
301 +
302 + let record = ErrorRecord(
303 + error: ogmiosError,
304 + timestamp: Date(),
305 + operation: operation
306 + )
307 +
308 + // Update published properties
309 + lastError = ogmiosError
310 + errorHistory.insert(record, at: 0)
311 + if errorHistory.count > maxErrorHistory {
312 + errorHistory.removeLast()
313 + }
314 +
315 + // Update connection status
316 + updateConnectionStatus(from: ogmiosError)
317 +
318 + // Log error with context
319 + logError(record)
320 +
321 + // Send to monitoring service (if configured)
322 + reportToMonitoring(record)
323 + }
324 +
325 + private func updateConnectionStatus(from error: OgmiosError) {
326 + switch error {
327 + case .httpError(_), .websocketError(_):
328 + connectionStatus = .failed
329 + case .responseError(let message):
330 + if let code = error.errorCode, code == 2003 {
331 + // Acquired expired - connection is fine, just need to retry
332 + connectionStatus = .connected
333 + } else {
334 + connectionStatus = .connected // Server responded, connection is OK
335 + }
336 + case .timeoutError(_):
337 + connectionStatus = .reconnecting
338 + default:
339 + connectionStatus = .connected
340 + }
341 + }
342 +
343 + private func logError(_ record: ErrorRecord) {
344 + let timestamp = DateFormatter.iso8601.string(from: record.timestamp)
345 + print("[\(timestamp)] ERROR in \(record.operation): \(record.error.localizedDescription)")
346 +
347 + // Add additional context based on error type
348 + switch record.error {
349 + case .responseError(let message):
350 + if let code = record.error.errorCode {
351 + print(" Error code: \(code)")
352 + print(" Suggested action: \(suggestedAction(for: code))")
353 + }
354 + case .httpError(_), .websocketError(_):
355 + print(" Suggested action: Check network connection and server availability")
356 + case .timeoutError(_):
357 + print(" Suggested action: Consider increasing timeout or optimizing query")
358 + default:
359 + break
360 + }
361 + }
362 +
363 + private func suggestedAction(for errorCode: Int) -> String {
364 + switch errorCode {
365 + case 2001: return "Check current era and use compatible queries"
366 + case 2002: return "Query not available in current era"
367 + case 2003: return "Retry query to get fresh ledger snapshot"
368 + case 3005: return "Rebuild transaction for current era"
369 + case 3100, 3101: return "Check and fix transaction signatures"
370 + case 3122: return "Increase transaction fee"
371 + case 3123: return "Fix transaction input/output balance"
372 + default: return "Check Ogmios documentation for error code \(errorCode)"
373 + }
374 + }
375 +
376 + private func reportToMonitoring(_ record: ErrorRecord) {
377 + // Implement monitoring service integration
378 + // e.g., send to Sentry, DataDog, custom analytics
379 + }
380 +
381 + func clearErrorHistory() {
382 + errorHistory.removeAll()
383 + lastError = nil
384 + }
385 +
386 + func retry<T>(_ operation: @escaping () async throws -> T, operationName: String) async throws -> T {
387 + do {
388 + let result = try await withExponentialBackoff(operation: operation)
389 +
390 + // Success - update connection status if it was failing
391 + if connectionStatus == .failed || connectionStatus == .reconnecting {
392 + connectionStatus = .connected
393 + }
394 +
395 + return result
396 + } catch {
397 + handle(error, operation: operationName)
398 + throw error
399 + }
400 + }
401 + }
402 + ```
403 |
404 | ## Error Handling in SwiftUI
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:406:1-527:4
404 | ## Error Handling in SwiftUI
405 |
406 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
407 + import SwiftUI
408 + import SwiftOgmios
409 +
410 + struct CardanoDataView: View {
411 + @StateObject private var errorHandler = OgmiosErrorHandler()
412 + @StateObject private var dataService = CardanoDataService()
413 +
414 + @State private var currentEpoch: Int?
415 + @State private var isLoading = false
416 +
417 + var body: some View {
418 + VStack {
419 + // Connection status indicator
420 + HStack {
421 + Circle()
422 + .fill(connectionStatusColor)
423 + .frame(width: 12, height: 12)
424 + Text(connectionStatusText)
425 + .font(.caption)
426 + .foregroundColor(.secondary)
427 + }
428 +
429 + // Main content
430 + Group {
431 + if isLoading {
432 + ProgressView("Loading...")
433 + } else if let epoch = currentEpoch {
434 + VStack {
435 + Text("Current Epoch")
436 + .font(.headline)
437 + Text("\(epoch)")
438 + .font(.largeTitle)
439 + .bold()
440 + }
441 + } else {
442 + Text("No data available")
443 + .foregroundColor(.secondary)
444 + }
445 + }
446 +
447 + // Error display
448 + if let lastError = errorHandler.lastError {
449 + ErrorBannerView(error: lastError) {
450 + Task { await loadData() }
451 + }
452 + }
453 +
454 + // Retry button
455 + Button("Refresh") {
456 + Task { await loadData() }
457 + }
458 + .disabled(isLoading)
459 + }
460 + .task {
461 + await loadData()
462 + }
463 + .environmentObject(errorHandler)
464 + }
465 +
466 + private var connectionStatusColor: Color {
467 + switch errorHandler.connectionStatus {
468 + case .connected: return .green
469 + case .reconnecting: return .yellow
470 + case .disconnected, .failed: return .red
471 + }
472 + }
473 +
474 + private var connectionStatusText: String {
475 + switch errorHandler.connectionStatus {
476 + case .connected: return "Connected"
477 + case .reconnecting: return "Reconnecting..."
478 + case .disconnected: return "Disconnected"
479 + case .failed: return "Connection Failed"
480 + }
481 + }
482 +
483 + @MainActor
484 + private func loadData() async {
485 + isLoading = true
486 + defer { isLoading = false }
487 +
488 + do {
489 + currentEpoch = try await errorHandler.retry({
490 + let response = try await dataService.client.ledgerStateQuery.epoch.execute()
491 + return response.result
492 + }, operationName: "fetch-current-epoch")
493 + } catch {
494 + // Error already handled by errorHandler.retry
495 + currentEpoch = nil
496 + }
497 + }
498 + }
499 +
500 + struct ErrorBannerView: View {
501 + let error: OgmiosError
502 + let onRetry: () -> Void
503 +
504 + var body: some View {
505 + HStack {
506 + Image(systemName: "exclamationmark.triangle.fill")
507 + .foregroundColor(.red)
508 +
509 + VStack(alignment: .leading, spacing: 4) {
510 + Text("Error")
511 + .font(.headline)
512 + Text(error.localizedDescription)
513 + .font(.caption)
514 + .foregroundColor(.secondary)
515 + }
516 +
517 + Spacer()
518 +
519 + Button("Retry", action: onRetry)
520 + .buttonStyle(.bordered)
521 + }
522 + .padding()
523 + .background(Color.red.opacity(0.1))
524 + .cornerRadius(8)
525 + }
526 + }
527 + ```
528 |
529 | ## Logging and Diagnostics
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:533:1-566:4
531 | ### Structured Logging
532 |
533 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
534 + import os.log
535 +
536 + extension OgmiosClient {
537 + private static let logger = Logger(
538 + subsystem: "com.yourapp.ogmios",
539 + category: "client"
540 + )
541 +
542 + func logRequest(_ request: any JSONRPCRequest) {
543 + Self.logger.debug("Sending request: \(request.method) (ID: \(request.id?.description ?? "none"))")
544 + }
545 +
546 + func logResponse<T>(_ response: T) where T: JSONRPCResponse {
547 + Self.logger.debug("Received response: \(response.method) (ID: \(response.id?.description ?? "none"))")
548 + }
549 +
550 + func logError(_ error: OgmiosError, context: String) {
551 + Self.logger.error("Error in \(context): \(error.localizedDescription)")
552 +
553 + // Add error-specific details
554 + switch error {
555 + case .responseError(let message):
556 + if let code = error.errorCode {
557 + Self.logger.error("Error code: \(code)")
558 + }
559 + case .httpError(let message), .websocketError(let message):
560 + Self.logger.error("Transport error: \(message)")
561 + default:
562 + break
563 + }
564 + }
565 + }
566 + ```
567 |
568 | ### Diagnostic Information Collection
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:570:1-624:4
568 | ### Diagnostic Information Collection
569 |
570 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
571 + struct DiagnosticInfo: Codable {
572 + let timestamp: Date
573 + let clientVersion: String
574 + let serverHealth: Health?
575 + let connectionType: String
576 + let lastError: String?
577 + let errorHistory: [ErrorSummary]
578 +
579 + struct ErrorSummary: Codable {
580 + let error: String
581 + let timestamp: Date
582 + let operation: String
583 + }
584 + }
585 +
586 + extension OgmiosErrorHandler {
587 + func generateDiagnosticInfo(client: OgmiosClient) async -> DiagnosticInfo {
588 + let serverHealth = try? await client.getServerHealth()
589 +
590 + let errorSummaries = errorHistory.map { record in
591 + DiagnosticInfo.ErrorSummary(
592 + error: record.error.localizedDescription,
593 + timestamp: record.timestamp,
594 + operation: record.operation
595 + )
596 + }
597 +
598 + return DiagnosticInfo(
599 + timestamp: Date(),
600 + clientVersion: "1.0.0", // Your app version
601 + serverHealth: serverHealth,
602 + connectionType: client.isWebSocketTransport ? "WebSocket" : "HTTP",
603 + lastError: lastError?.localizedDescription,
604 + errorHistory: errorSummaries
605 + )
606 + }
607 +
608 + func exportDiagnostics(client: OgmiosClient) async -> URL {
609 + let diagnostics = await generateDiagnosticInfo(client: client)
610 + let encoder = JSONEncoder()
611 + encoder.dateEncodingStrategy = .iso8601
612 + encoder.outputFormatting = .prettyPrinted
613 +
614 + let data = try! encoder.encode(diagnostics)
615 +
616 + let url = URL(fileURLWithPath: NSTemporaryDirectory())
617 + .appendingPathComponent("ogmios-diagnostics-\(Date().timeIntervalSince1970)")
618 + .appendingPathExtension("json")
619 +
620 + try! data.write(to: url)
621 + return url
622 + }
623 + }
624 + ```
625 |
626 | ## Testing Error Scenarios
warning: Unknown option 'swift path' in code block.
--> Tutorials/ErrorHandling.md:628:1-695:4
626 | ## Testing Error Scenarios
627 |
628 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
629 + import XCTest
630 + @testable import SwiftOgmios
631 +
632 + class ErrorHandlingTests: XCTestCase {
633 + func testHTTPErrorHandling() async throws {
634 + let mockConnection = MockHTTPConnection()
635 + mockConnection.shouldFail = true
636 + mockConnection.errorMessage = "Connection refused"
637 +
638 + let client = try await OgmiosClient(
639 + httpOnly: true,
640 + httpConnection: mockConnection
641 + )
642 +
643 + do {
644 + _ = try await client.ledgerStateQuery.epoch.execute()
645 + XCTFail("Expected error")
646 + } catch OgmiosError.httpError(let message) {
647 + XCTAssertTrue(message.contains("Connection refused"))
648 + }
649 + }
650 +
651 + func testRetryLogic() async throws {
652 + let errorHandler = OgmiosErrorHandler()
653 + var attemptCount = 0
654 +
655 + do {
656 + _ = try await errorHandler.retry({
657 + attemptCount += 1
658 + if attemptCount < 3 {
659 + throw OgmiosError.httpError("Temporary failure")
660 + }
661 + return "Success"
662 + }, operationName: "test-operation")
663 + } catch {
664 + XCTFail("Should have succeeded after retry")
665 + }
666 +
667 + XCTAssertEqual(attemptCount, 3)
668 + }
669 +
670 + func testCircuitBreakerPattern() async throws {
671 + let circuitBreaker = CircuitBreaker()
672 +
673 + // Fail repeatedly to open circuit
674 + for _ in 1...6 {
675 + do {
676 + _ = try await circuitBreaker.execute {
677 + throw OgmiosError.responseError("Server error")
678 + }
679 + } catch {
680 + // Expected
681 + }
682 + }
683 +
684 + // Circuit should now be open
685 + do {
686 + _ = try await circuitBreaker.execute {
687 + return "Should not execute"
688 + }
689 + XCTFail("Circuit should be open")
690 + } catch OgmiosError.responseError(let message) {
691 + XCTAssertTrue(message.contains("Circuit breaker is open"))
692 + }
693 + }
694 + }
695 + ```
696 |
697 | ## Best Practices Summary
warning: Extraneous content found after a link in task group list item
--> Tutorials/ErrorHandling.md:710:3-710:55
708 | ## See Also
709 |
710 + - <doc:GettingStarted> - Basic setup and first queries
| ╰─suggestion: Remove extraneous content
711 | - <doc:TransportTypes> - Transport-specific error handling
712 | - <doc:AdvancedUsage> - Advanced error handling patterns
warning: Extraneous content found after a link in task group list item
--> Tutorials/ErrorHandling.md:711:3-711:59
709 |
710 | - <doc:GettingStarted> - Basic setup and first queries
711 + - <doc:TransportTypes> - Transport-specific error handling
| ╰─suggestion: Remove extraneous content
712 | - <doc:AdvancedUsage> - Advanced error handling patterns
713 | - <doc:LedgerStateQueries> - Query-specific error scenarios
warning: Extraneous content found after a link in task group list item
--> Tutorials/ErrorHandling.md:712:3-712:57
710 | - <doc:GettingStarted> - Basic setup and first queries
711 | - <doc:TransportTypes> - Transport-specific error handling
712 + - <doc:AdvancedUsage> - Advanced error handling patterns
| ╰─suggestion: Remove extraneous content
713 | - <doc:LedgerStateQueries> - Query-specific error scenarios
warning: Extraneous content found after a link in task group list item
--> Tutorials/ErrorHandling.md:713:3-713:60
711 | - <doc:TransportTypes> - Transport-specific error handling
712 | - <doc:AdvancedUsage> - Advanced error handling patterns
713 + - <doc:LedgerStateQueries> - Query-specific error scenarios
| ╰─suggestion: Remove extraneous content
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:89:1-111:4
87 | Add SwiftOgmios as a dependency in your `Package.swift`:
88 |
89 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
90 + // swift-tools-version: 6.1
91 + import PackageDescription
92 +
93 + let package = Package(
94 + name: "MyCardanoApp",
95 + platforms: [
96 + .iOS(.v14),
97 + .macOS(.v15)
98 + ],
99 + dependencies: [
100 + .package(url: "https://github.com/Kingpin-Apps/swift-ogmios.git", from: "1.0.0")
101 + ],
102 + targets: [
103 + .target(
104 + name: "MyCardanoApp",
105 + dependencies: [
106 + .product(name: "SwiftOgmios", package: "swift-ogmios")
107 + ]
108 + )
109 + ]
110 + )
111 + ```
112 |
113 | ## Step 2: Import and Basic Setup
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:117:1-120:4
115 | Create a new Swift file and import SwiftOgmios:
116 |
117 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
118 + import SwiftOgmios
119 + import Foundation
120 + ```
121 |
122 | ## Step 3: Create Your First Client
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:126:1-145:4
124 | SwiftOgmios supports both HTTP and WebSocket transports. For simple queries, HTTP is often sufficient and easier to manage:
125 |
126 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
127 + import SwiftOgmios
128 +
129 + @main
130 + struct HelloOgmios {
131 + static func main() async throws {
132 + // Create an HTTP client (recommended for simple queries)
133 + let client = try await OgmiosClient(
134 + host: "localhost", // Your Ogmios server host
135 + port: 1337, // Your Ogmios server port
136 + secure: false, // Use HTTP (set to true for HTTPS)
137 + httpOnly: true // Use HTTP transport
138 + )
139 +
140 + print("✅ Connected to Ogmios server!")
141 +
142 + // Your queries will go here...
143 + }
144 + }
145 + ```
146 |
147 | For real-time applications that need continuous data streaming, use WebSocket transport:
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:149:1-157:4
147 | For real-time applications that need continuous data streaming, use WebSocket transport:
148 |
149 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
150 + // Create a WebSocket client (recommended for real-time applications)
151 + let client = try await OgmiosClient(
152 + host: "localhost",
153 + port: 1337,
154 + secure: false, // Use WS (set to true for WSS)
155 + httpOnly: false // Use WebSocket transport
156 + )
157 + ```
158 |
159 | ## Step 4: Make Your First Query
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:163:1-183:4
161 | Let's query the current network tip to get basic information about the latest block:
162 |
163 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
164 + @main
165 + struct HelloOgmios {
166 + static func main() async throws {
167 + let client = try await OgmiosClient(httpOnly: true)
168 +
169 + do {
170 + // Query the current network tip
171 + let tip = try await client.ledgerStateQuery.tip.result()
172 +
173 + print("🎉 Success! Current blockchain tip:")
174 + print(" Slot: \(tip.slot)")
175 + print(" Block Hash: \(tip.id)")
176 + print(" Height: \(tip.height)")
177 +
178 + } catch {
179 + print("❌ Error querying tip: \(error)")
180 + }
181 + }
182 + }
183 + ```
184 |
185 | Expected output:
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:198:1-228:4
196 | Now let's expand our example to query more information about the current state:
197 |
198 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
199 + @main
200 + struct HelloOgmios {
201 + static func main() async throws {
202 + let client = try await OgmiosClient(httpOnly: true)
203 +
204 + do {
205 + // Query current epoch
206 + let epoch = try await client.ledgerStateQuery.epoch.result()
207 + print("📅 Current epoch: \(epoch)")
208 +
209 + // Query era summaries to understand blockchain eras
210 + let eraSummaries = try await client.ledgerStateQuery.eraSummaries.result()
211 + print("🗋 Available eras:")
212 + for era in eraSummaries {
213 + let endEpoch = era.end?.epoch ?? "current"
214 + print(" \(era.start.epoch) - \(endEpoch)")
215 + }
216 +
217 + // Check server health
218 + let health = try await client.getServerHealth()
219 + print("🏥 Server status: \(health.connectionStatus)")
220 + print(" Network: \(health.network)")
221 + print(" Version: \(health.version)")
222 +
223 + } catch {
224 + print("❌ Error: \(error)")
225 + }
226 + }
227 + }
228 + ```
229 |
230 | ## Step 6: Error Handling Best Practices
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:234:1-262:4
232 | Always implement proper error handling when working with network operations:
233 |
234 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
235 + func queryTipWithErrorHandling() async throws {
236 + let client = try await OgmiosClient(httpOnly: true)
237 +
238 + do {
239 + // Using .result() for cleaner code (recommended)
240 + let tip = try await client.ledgerStateQuery.tip.result(
241 + id: .generateNextNanoId() // Optional: provide custom request ID
242 + )
243 + print("Tip received: \(tip)")
244 +
245 + } catch OgmiosError.httpError(let message) {
246 + print("HTTP connection error: \(message)")
247 + // Handle connection issues (retry, user notification, etc.)
248 +
249 + } catch OgmiosError.responseError(let message) {
250 + print("Ogmios server error: \(message)")
251 + // Handle server-side errors
252 +
253 + } catch OgmiosError.invalidResponse(let message) {
254 + print("Invalid response format: \(message)")
255 + // Handle malformed responses
256 +
257 + } catch {
258 + print("Unexpected error: \(error)")
259 + // Handle any other errors
260 + }
261 + }
262 + ```
263 |
264 | ## Platform-Specific Considerations
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:270:1-308:4
268 | For iOS apps, consider using SwiftOgmios in a background task or with proper lifecycle management:
269 |
270 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
271 + import SwiftUI
272 + import SwiftOgmios
273 +
274 + @MainActor
275 + class CardanoDataService: ObservableObject {
276 + @Published var currentEpoch: Int?
277 + @Published var isLoading = false
278 +
279 + private var client: OgmiosClient?
280 +
281 + func connect() async {
282 + isLoading = true
283 + do {
284 + client = try await OgmiosClient(
285 + host: "your-server.com",
286 + port: 1337,
287 + secure: true, // Use HTTPS for production
288 + httpOnly: true
289 + )
290 + await fetchCurrentEpoch()
291 + } catch {
292 + print("Failed to connect: \(error)")
293 + }
294 + isLoading = false
295 + }
296 +
297 + private func fetchCurrentEpoch() async {
298 + guard let client = client else { return }
299 +
300 + do {
301 + let epoch = try await client.ledgerStateQuery.epoch.result()
302 + currentEpoch = epoch
303 + } catch {
304 + print("Failed to fetch epoch: \(error)")
305 + }
306 + }
307 + }
308 + ```
309 |
310 | ### macOS Applications
warning: Unknown option 'swift path' in code block.
--> Tutorials/GettingStarted.md:314:1-343:4
312 | For macOS apps, you have more flexibility with background processing:
313 |
314 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
315 + import AppKit
316 + import SwiftOgmios
317 +
318 + class CardanoWindowController: NSWindowController {
319 + private var client: OgmiosClient?
320 +
321 + override func windowDidLoad() {
322 + super.windowDidLoad()
323 +
324 + Task {
325 + await setupOgmiosConnection()
326 + }
327 + }
328 +
329 + private func setupOgmiosConnection() async {
330 + do {
331 + client = try await OgmiosClient(httpOnly: true)
332 + // Start background monitoring
333 + await startMonitoring()
334 + } catch {
335 + print("Connection failed: \(error)")
336 + }
337 + }
338 +
339 + private func startMonitoring() async {
340 + // Implement your monitoring logic here
341 + }
342 + }
343 + ```
344 |
345 | ## Next Steps
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:15:1-27:4
13 | All ledger state queries follow a consistent pattern:
14 |
15 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
16 + import SwiftOgmios
17 +
18 + // Basic query without parameters
19 + let response = try await client.ledgerStateQuery.epoch.execute()
20 + print("Current epoch: \(response.result)")
21 +
22 + // Query with parameters and custom ID
23 + let utxoResponse = try await client.ledgerStateQuery.utxo.execute(
24 + addresses: [Address("addr1...")],
25 + id: .generateNextNanoId()
26 + )
27 + ```
28 |
29 | ## Era and Time Queries
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:35:1-41:4
33 | Get the current epoch number:
34 |
35 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
36 + let epochResponse = try await client.ledgerStateQuery.epoch.execute()
37 + let currentEpoch: Int = epochResponse.result
38 +
39 + print("Current epoch: \(currentEpoch)")
40 + // Output: Current epoch: 425
41 + ```
42 |
43 | **Use Cases**: Epoch-based calculations, reward distribution timing, protocol parameter change scheduling.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:49:1-55:4
47 | Query when the current era started:
48 |
49 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
50 + let eraStartResponse = try await client.ledgerStateQuery.eraStart.execute()
51 + let startPoint = eraStartResponse.result
52 +
53 + print("Era started at slot: \(startPoint.slot)")
54 + print("Era started at epoch: \(startPoint.epoch)")
55 + ```
56 |
57 | **Use Cases**: Era transition analysis, historical data correlation.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:63:1-71:4
61 | Get comprehensive information about all blockchain eras:
62 |
63 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
64 + let eraSummariesResponse = try await client.ledgerStateQuery.eraSummaries.execute()
65 +
66 + for era in eraSummariesResponse.result {
67 + let endDescription = era.end?.epoch.map { String($0) } ?? "ongoing"
68 + print("Era: \(era.start.epoch) to \(endDescription)")
69 + print(" Parameters: \(era.parameters)")
70 + }
71 + ```
72 |
73 | **Use Cases**: Historical analysis, era transition planning, protocol evolution tracking.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:81:1-89:4
79 | Query the current tip of the ledger:
80 |
81 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
82 + let tipResponse = try await client.ledgerStateQuery.tip.execute()
83 + let tip = tipResponse.result
84 +
85 + print("Ledger tip:")
86 + print(" Slot: \(tip.slot)")
87 + print(" Block ID: \(tip.id)")
88 + print(" Height: \(tip.height)")
89 + ```
90 |
91 | **Use Cases**: Synchronization status, latest block information, chain progress tracking.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:97:1-106:4
95 | Access current protocol parameters:
96 |
97 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
98 + let paramsResponse = try await client.ledgerStateQuery.protocolParameters.execute()
99 + let params = paramsResponse.result
100 +
101 + print("Protocol parameters:")
102 + print(" Min fee A: \(params.minFeeCoefficient)")
103 + print(" Min fee B: \(params.minFeeConstant)")
104 + print(" Max block size: \(params.maxBlockHeaderSize)")
105 + print(" Max transaction size: \(params.maxTransactionSize)")
106 + ```
107 |
108 | **Use Cases**: Fee calculation, transaction size validation, smart contract development.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:114:1-128:4
112 | Check for pending protocol parameter updates:
113 |
114 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
115 + do {
116 + let proposedResponse = try await client.ledgerStateQuery.proposedProtocolParameters.execute()
117 + let proposedParams = proposedResponse.result
118 +
119 + if let params = proposedParams {
120 + print("Proposed parameter changes detected")
121 + // Process proposed changes
122 + } else {
123 + print("No proposed parameter changes")
124 + }
125 + } catch {
126 + print("Error querying proposed parameters: \(error)")
127 + }
128 + ```
129 |
130 | **Performance Tip**: Cache protocol parameters and only refresh when epoch changes, as they typically remain stable.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:140:1-151:4
138 | #### Query Entire UTxO Set
139 |
140 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
141 + let allUtxoResponse = try await client.ledgerStateQuery.utxo.execute(
142 + wholeUtxo: true,
143 + id: .generateNextNanoId()
144 + )
145 +
146 + print("Total UTxOs: \(allUtxoResponse.result.count)")
147 + for utxo in allUtxoResponse.result.prefix(5) { // Show first 5
148 + print("UTxO: \(utxo.transaction.id)#\(utxo.index)")
149 + print(" Value: \(utxo.value.ada.lovelace) lovelace")
150 + }
151 + ```
152 |
153 | **⚠️ Warning**: Querying the entire UTxO set returns large amounts of data. Use with caution.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:157:1-181:4
155 | #### Query UTxOs by Addresses
156 |
157 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
158 + let addresses = [
159 + Address("addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp"),
160 + Address("addr1q8v42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wnrqua2vw243tmjfjt0h5wsru6appuz8c0pfd75ur7myyeqsx9990")
161 + ]
162 +
163 + let addressUtxoResponse = try await client.ledgerStateQuery.utxo.execute(
164 + addresses: addresses,
165 + id: .generateNextNanoId()
166 + )
167 +
168 + for utxo in addressUtxoResponse.result {
169 + print("Address: \(utxo.address.value)")
170 + print("Value: \(utxo.value.ada.lovelace) lovelace")
171 +
172 + // Check for native assets
173 + if let assets = utxo.value.assets {
174 + for (policyId, tokens) in assets {
175 + for (assetName, quantity) in tokens {
176 + print("Asset: \(policyId).\(assetName) = \(quantity)")
177 + }
178 + }
179 + }
180 + }
181 + ```
182 |
183 | #### Query UTxOs by Transaction Outputs
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:185:1-199:4
183 | #### Query UTxOs by Transaction Outputs
184 |
185 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
186 + let outputReferences = [
187 + TransactionOutputReference(
188 + transaction: TransactionOutputReference.TransactionReference(
189 + id: "1234567890abcdef..."
190 + ),
191 + index: 0
192 + )
193 + ]
194 +
195 + let refUtxoResponse = try await client.ledgerStateQuery.utxo.execute(
196 + outputReferences: outputReferences,
197 + id: .generateNextNanoId()
198 + )
199 + ```
200 |
201 | **Use Cases**: Wallet balance checking, transaction input validation, asset tracking.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:214:1-226:4
212 | Get current stake distribution across all pools:
213 |
214 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
215 + let stakeResponse = try await client.ledgerStateQuery.liveStakeDistribution.execute()
216 + let distribution = stakeResponse.result
217 +
218 + print("Stake distribution across \(distribution.value.count) pools")
219 +
220 + // Find top 10 pools by stake
221 + let sortedPools = distribution.value.sorted { $0.value.stake > $1.value.stake }
222 + for (index, (poolId, poolInfo)) in sortedPools.prefix(10).enumerated() {
223 + let stakeAda = Double(poolInfo.stake) / 1_000_000 // Convert to ADA
224 + print("\(index + 1). Pool \(poolId.value.prefix(8))...: \(String(format: "%.2f", stakeAda)) ADA")
225 + }
226 + ```
227 |
228 | ### Stake Pools
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:232:1-249:4
230 | Get detailed information about all registered stake pools:
231 |
232 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
233 + let poolsResponse = try await client.ledgerStateQuery.stakePools.execute()
234 + let pools = poolsResponse.result
235 +
236 + print("Total registered pools: \(pools.count)")
237 +
238 + for (poolId, pool) in pools.prefix(3) { // Show first 3 pools
239 + print("\nPool ID: \(poolId.value)")
240 + print("Cost: \(pool.cost) lovelace")
241 + print("Margin: \(pool.margin)")
242 + print("Pledge: \(pool.pledge) lovelace")
243 +
244 + if let metadata = pool.metadata {
245 + print("Metadata URL: \(metadata.url)")
246 + print("Metadata Hash: \(metadata.hash)")
247 + }
248 + }
249 + ```
250 |
251 | ### Stake Pool Performances
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:255:1-262:4
253 | Query performance metrics for stake pools:
254 |
255 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
256 + let performanceResponse = try await client.ledgerStateQuery.stakePoolsPerformances.execute()
257 + let performances = performanceResponse.result
258 +
259 + for (poolId, performance) in performances.prefix(5) {
260 + print("Pool \(poolId.value.prefix(8))...: \(performance)")
261 + }
262 + ```
263 |
264 | ### Operational Certificates
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:268:1-273:4
266 | Get operational certificate information:
267 |
268 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
269 + let certResponse = try await client.ledgerStateQuery.operationalCertificates.execute()
270 + let certificates = certResponse.result
271 +
272 + print("Operational certificates: \(certificates.count)")
273 + ```
274 |
275 | **Use Cases**: Pool delegation decisions, performance analysis, pool operator monitoring.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:283:1-299:4
281 | Query the current constitution (Conway era):
282 |
283 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
284 + let constitutionResponse = try await client.ledgerStateQuery.constitution.execute()
285 +
286 + if let constitution = constitutionResponse.result {
287 + print("Constitution found:")
288 + if let anchor = constitution.anchor {
289 + print(" URL: \(anchor.url)")
290 + print(" Hash: \(anchor.dataHash)")
291 + }
292 +
293 + if let script = constitution.guardrails {
294 + print(" Guardrails script: \(script)")
295 + }
296 + } else {
297 + print("No constitution defined")
298 + }
299 + ```
300 |
301 | ### Constitutional Committee
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:305:1-319:4
303 | Query constitutional committee state:
304 |
305 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
306 + let committeeResponse = try await client.ledgerStateQuery.constitutionalCommittee.execute()
307 +
308 + if let committee = committeeResponse.result {
309 + print("Constitutional Committee:")
310 + print(" Threshold: \(committee.threshold)")
311 + print(" Members: \(committee.members.count)")
312 +
313 + for (keyHash, epoch) in committee.members {
314 + print(" Member \(keyHash): expires epoch \(epoch)")
315 + }
316 + } else {
317 + print("No constitutional committee")
318 + }
319 + ```
320 |
321 | ### Governance Proposals
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:325:1-336:4
323 | Query active governance proposals:
324 |
325 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
326 + let proposalsResponse = try await client.ledgerStateQuery.governanceProposals.execute()
327 +
328 + print("Active proposals: \(proposalsResponse.result.count)")
329 +
330 + for proposal in proposalsResponse.result.prefix(3) {
331 + print("\nProposal ID: \(proposal.proposal.transaction.id)")
332 + print("Index: \(proposal.proposal.index)")
333 + print("Deposit: \(proposal.deposit) lovelace")
334 + print("Return address: \(proposal.returnAddress.value)")
335 + }
336 + ```
337 |
338 | ### Delegate Representatives (DReps)
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:342:1-355:4
340 | Query registered delegate representatives:
341 |
342 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
343 + let drepsResponse = try await client.ledgerStateQuery.delegateRepresentatives.execute()
344 +
345 + print("Registered DReps: \(drepsResponse.result.count)")
346 +
347 + for drep in drepsResponse.result.prefix(5) {
348 + print("\nDRep: \(drep.id)")
349 + print("Type: \(drep.type)")
350 +
351 + if let anchor = drep.anchor {
352 + print("Metadata: \(anchor.url)")
353 + }
354 + }
355 + ```
356 |
357 | **Use Cases**: Governance participation, voting decisions, proposal tracking.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:365:1-374:4
363 | Query treasury and reserves amounts:
364 |
365 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
366 + let treasuryResponse = try await client.ledgerStateQuery.treasuryAndReserves.execute()
367 + let treasury = treasuryResponse.result
368 +
369 + let treasuryAda = Double(treasury.treasury.ada.lovelace) / 1_000_000
370 + let reservesAda = Double(treasury.reserves.ada.lovelace) / 1_000_000
371 +
372 + print("Treasury: \(String(format: "%.2f", treasuryAda)) ADA")
373 + print("Reserves: \(String(format: "%.2f", reservesAda)) ADA")
374 + ```
375 |
376 | ### Projected Rewards
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:380:1-388:4
378 | Query projected rewards for stake pools:
379 |
380 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
381 + let rewardsResponse = try await client.ledgerStateQuery.projectedRewards.execute()
382 + let rewards = rewardsResponse.result
383 +
384 + for (stakeCredential, reward) in rewards.prefix(5) {
385 + let rewardAda = Double(reward) / 1_000_000
386 + print("Stake credential \(stakeCredential): \(String(format: "%.6f", rewardAda)) ADA")
387 + }
388 + ```
389 |
390 | ### Reward Account Summaries
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:394:1-406:4
392 | Query reward account information:
393 |
394 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
395 + let accountsResponse = try await client.ledgerStateQuery.rewardAccountSummaries.execute()
396 + let accounts = accountsResponse.result
397 +
398 + for (rewardAccount, summary) in accounts.prefix(3) {
399 + print("\nReward account: \(rewardAccount)")
400 + print("Available rewards: \(summary.rewards) lovelace")
401 +
402 + if let delegation = summary.delegation {
403 + print("Delegated to: \(delegation)")
404 + }
405 + }
406 + ```
407 |
408 | **Use Cases**: Reward calculation, delegation analysis, economic modeling.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:416:1-439:4
414 | Export complete ledger state (use with caution):
415 |
416 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
417 + let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
418 + .appendingPathComponent("ledger-dump-\(Date().timeIntervalSince1970)")
419 + .appendingPathExtension("json")
420 +
421 + do {
422 + let dumpResponse = try await client.ledgerStateQuery.dump.execute(
423 + to: tempURL.path,
424 + id: .generateNextNanoId()
425 + )
426 +
427 + print("Ledger state dumped to: \(tempURL.path)")
428 + print("Dump result: \(dumpResponse.result)")
429 +
430 + // Process the dump file
431 + if FileManager.default.fileExists(atPath: tempURL.path) {
432 + let fileSize = try FileManager.default.attributesOfItem(atPath: tempURL.path)[.size] as! Int64
433 + print("Dump file size: \(fileSize) bytes")
434 + }
435 +
436 + } catch {
437 + print("Failed to dump ledger state: \(error)")
438 + }
439 + ```
440 |
441 | **⚠️ Warning**: Ledger state dumps can be very large (several GB). Use only for debugging or analysis purposes.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:447:1-453:4
445 | Query consensus nonces for randomness:
446 |
447 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
448 + let noncesResponse = try await client.ledgerStateQuery.nonces.execute()
449 + let nonces = noncesResponse.result
450 +
451 + print("Epoch nonce: \(nonces.epochNonce)")
452 + print("Extra entropy: \(nonces.extraEntropy)")
453 + ```
454 |
455 | **Use Cases**: Randomness verification, consensus debugging.
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:461:1-502:4
459 | ### Robust Error Handling
460 |
461 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
462 + func queryWithRetry<T>(
463 + query: () async throws -> T,
464 + maxRetries: Int = 3
465 + ) async throws -> T {
466 + var lastError: Error?
467 +
468 + for attempt in 1...maxRetries {
469 + do {
470 + return try await query()
471 + } catch OgmiosError.responseError(let message) {
472 + if message.contains("2001") { // Era mismatch
473 + print("Era mismatch - query not available in current era")
474 + throw OgmiosError.responseError("Query not available in current era")
475 + } else if message.contains("2002") { // Unavailable in current era
476 + print("Query unavailable in current era")
477 + throw OgmiosError.responseError("Query unavailable in current era")
478 + } else if attempt < maxRetries {
479 + print("Attempt \(attempt) failed, retrying...")
480 + lastError = OgmiosError.responseError(message)
481 + try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
482 + } else {
483 + throw OgmiosError.responseError(message)
484 + }
485 + } catch {
486 + throw error
487 + }
488 + }
489 +
490 + throw lastError ?? OgmiosError.responseError("Max retries exceeded")
491 + }
492 +
493 + // Usage
494 + do {
495 + let epoch = try await queryWithRetry {
496 + return try await client.ledgerStateQuery.epoch.execute()
497 + }
498 + print("Current epoch: \(epoch.result)")
499 + } catch {
500 + print("Failed to query epoch: \(error)")
501 + }
502 + ```
503 |
504 | ### Performance Optimization
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:508:1-533:4
506 | #### Batch Queries
507 |
508 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
509 + // Execute multiple queries concurrently
510 + async func fetchDashboardData() async throws -> (epoch: Int, tip: Point, pools: Int) {
511 + async let epochTask = client.ledgerStateQuery.epoch.execute()
512 + async let tipTask = client.ledgerStateQuery.tip.execute()
513 + async let poolsTask = client.ledgerStateQuery.stakePools.execute()
514 +
515 + let (epochResponse, tipResponse, poolsResponse) = try await (epochTask, tipTask, poolsTask)
516 +
517 + return (
518 + epoch: epochResponse.result,
519 + tip: tipResponse.result,
520 + pools: poolsResponse.result.count
521 + )
522 + }
523 +
524 + // Usage
525 + do {
526 + let dashboard = try await fetchDashboardData()
527 + print("Epoch: \(dashboard.epoch)")
528 + print("Tip: \(dashboard.tip.slot)")
529 + print("Pools: \(dashboard.pools)")
530 + } catch {
531 + print("Dashboard fetch failed: \(error)")
532 + }
533 + ```
534 |
535 | #### Caching Strategy
warning: Unknown option 'swift path' in code block.
--> Tutorials/LedgerStateQueries.md:537:1-585:4
535 | #### Caching Strategy
536 |
537 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
538 + @MainActor
539 + class LedgerStateCache: ObservableObject {
540 + @Published var currentEpoch: Int?
541 + @Published var protocolParams: ProtocolParameters?
542 + @Published var lastUpdated: Date?
543 +
544 + private let client: OgmiosClient
545 + private let cacheTimeout: TimeInterval = 300 // 5 minutes
546 +
547 + init(client: OgmiosClient) {
548 + self.client = client
549 + }
550 +
551 + func getProtocolParameters() async throws -> ProtocolParameters {
552 + if let params = protocolParams,
553 + let lastUpdated = lastUpdated,
554 + Date().timeIntervalSince(lastUpdated) < cacheTimeout {
555 + return params
556 + }
557 +
558 + let response = try await client.ledgerStateQuery.protocolParameters.execute()
559 +
560 + await MainActor.run {
561 + self.protocolParams = response.result
562 + self.lastUpdated = Date()
563 + }
564 +
565 + return response.result
566 + }
567 +
568 + func getCurrentEpoch() async throws -> Int {
569 + if let epoch = currentEpoch,
570 + let lastUpdated = lastUpdated,
571 + Date().timeIntervalSince(lastUpdated) < cacheTimeout {
572 + return epoch
573 + }
574 +
575 + let response = try await client.ledgerStateQuery.epoch.execute()
576 +
577 + await MainActor.run {
578 + self.currentEpoch = response.result
579 + self.lastUpdated = Date()
580 + }
581 +
582 + return response.result
583 + }
584 + }
585 + ```
586 |
587 | ## Query Compatibility by Era
warning: Extraneous content found after a link in task group list item
--> Tutorials/LedgerStateQueries.md:603:3-603:55
601 | ## See Also
602 |
603 + - <doc:GettingStarted> - Basic setup and first queries
| ╰─suggestion: Remove extraneous content
604 | - <doc:ErrorHandling> - Comprehensive error handling strategies
605 | - <doc:TransportTypes> - Optimizing query performance with transport selection
warning: Extraneous content found after a link in task group list item
--> Tutorials/LedgerStateQueries.md:604:3-604:64
602 |
603 | - <doc:GettingStarted> - Basic setup and first queries
604 + - <doc:ErrorHandling> - Comprehensive error handling strategies
| ╰─suggestion: Remove extraneous content
605 | - <doc:TransportTypes> - Optimizing query performance with transport selection
606 | - <doc:AdvancedUsage> - Advanced query patterns and optimization techniques
warning: Extraneous content found after a link in task group list item
--> Tutorials/LedgerStateQueries.md:605:3-605:79
603 | - <doc:GettingStarted> - Basic setup and first queries
604 | - <doc:ErrorHandling> - Comprehensive error handling strategies
605 + - <doc:TransportTypes> - Optimizing query performance with transport selection
| ╰─suggestion: Remove extraneous content
606 | - <doc:AdvancedUsage> - Advanced query patterns and optimization techniques
warning: Extraneous content found after a link in task group list item
--> Tutorials/LedgerStateQueries.md:606:3-606:76
604 | - <doc:ErrorHandling> - Comprehensive error handling strategies
605 | - <doc:TransportTypes> - Optimizing query performance with transport selection
606 + - <doc:AdvancedUsage> - Advanced query patterns and optimization techniques
| ╰─suggestion: Remove extraneous content
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:23:1-46:4
21 | ### Basic Mempool Operations
22 |
23 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
24 + import SwiftOgmios
25 +
26 + // Create client (WebSocket recommended for continuous monitoring)
27 + let client = try await OgmiosClient(httpOnly: false)
28 +
29 + // Acquire mempool snapshot
30 + let snapshot = try await client.mempoolMonitor.acquireMempool.execute()
31 + print("Acquired mempool at slot: \(snapshot.result.slot)")
32 +
33 + // Check mempool size
34 + let sizeResponse = try await client.mempoolMonitor.sizeOfMempool.execute()
35 + print("Mempool size: \(sizeResponse.result.currentSize)/\(sizeResponse.result.capacity)")
36 +
37 + // Check if specific transaction exists
38 + let hasTransaction = try await client.mempoolMonitor.hasTransaction.execute(
39 + transaction: TransactionId("abc123..."),
40 + id: .generateNextNanoId()
41 + )
42 + print("Transaction in mempool: \(hasTransaction.result)")
43 +
44 + // Release mempool snapshot
45 + let _ = try await client.mempoolMonitor.releaseMempool.execute()
46 + ```
47 |
48 | ## Getting All Mempool Transactions
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:52:1-71:4
50 | SwiftOgmios provides a convenient helper method to retrieve all transactions from the mempool:
51 |
52 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
53 + do {
54 + let transactions = try await client.mempoolMonitor.getMempoolTransactions()
55 +
56 + print("Found \(transactions.count) transactions in mempool:")
57 + for transaction in transactions {
58 + switch transaction {
59 + case .transaction(let tx):
60 + print(" TX: \(tx.id)")
61 + print(" Fee: \(tx.body.fee) lovelace")
62 + print(" Inputs: \(tx.body.inputs.count)")
63 + print(" Outputs: \(tx.body.outputs.count)")
64 + case .noMoreTransactions:
65 + break
66 + }
67 + }
68 + } catch {
69 + print("Failed to get mempool transactions: \(error)")
70 + }
71 + ```
72 |
73 | ## Advanced Mempool Monitoring
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:77:1-145:4
75 | ### Real-Time Mempool Monitor
76 |
77 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
78 + actor MempoolMonitor {
79 + private let client: OgmiosClient
80 + private var isMonitoring = false
81 + private var monitoringTask: Task<Void, Never>?
82 +
83 + init(client: OgmiosClient) {
84 + self.client = client
85 + }
86 +
87 + func startMonitoring() async {
88 + guard !isMonitoring else { return }
89 + isMonitoring = true
90 +
91 + monitoringTask = Task { [weak self] in
92 + await self?.monitorLoop()
93 + }
94 + }
95 +
96 + func stopMonitoring() {
97 + isMonitoring = false
98 + monitoringTask?.cancel()
99 + monitoringTask = nil
100 + }
101 +
102 + private func monitorLoop() async {
103 + while isMonitoring && !Task.isCancelled {
104 + do {
105 + // Check mempool size
106 + let sizeResponse = try await client.mempoolMonitor.sizeOfMempool.execute()
107 + await handleSizeUpdate(sizeResponse.result)
108 +
109 + // Get current transactions
110 + let transactions = try await client.mempoolMonitor.getMempoolTransactions()
111 + await handleTransactionUpdate(transactions)
112 +
113 + // Wait before next check
114 + try await Task.sleep(nanoseconds: 30_000_000_000) // 30 seconds
115 +
116 + } catch {
117 + print("Mempool monitoring error: \(error)")
118 +
119 + // Wait before retry
120 + try? await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds
121 + }
122 + }
123 + }
124 +
125 + private func handleSizeUpdate(_ size: SizeAndCapacity) async {
126 + let utilization = Double(size.currentSize) / Double(size.capacity) * 100
127 + print("Mempool utilization: \(String(format: "%.1f", utilization))% (\(size.currentSize)/\(size.capacity))")
128 + }
129 +
130 + private func handleTransactionUpdate(_ transactions: [NextTransactionResult]) async {
131 + var txCount = 0
132 + var totalFees: UInt64 = 0
133 +
134 + for transaction in transactions {
135 + if case .transaction(let tx) = transaction {
136 + txCount += 1
137 + totalFees += tx.body.fee
138 + }
139 + }
140 +
141 + let avgFee = txCount > 0 ? totalFees / UInt64(txCount) : 0
142 + print("Mempool stats: \(txCount) transactions, avg fee: \(avgFee) lovelace")
143 + }
144 + }
145 + ```
146 |
147 | ### Transaction Filtering
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:151:1-228:4
149 | Create filters to monitor specific types of transactions:
150 |
151 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
152 + struct TransactionFilter {
153 + let watchedAddresses: Set<String>
154 + let watchedAssets: Set<String>
155 + let minAmount: UInt64?
156 + let maxAmount: UInt64?
157 +
158 + init(
159 + addresses: [String] = [],
160 + assets: [String] = [],
161 + minAmount: UInt64? = nil,
162 + maxAmount: UInt64? = nil
163 + ) {
164 + self.watchedAddresses = Set(addresses)
165 + self.watchedAssets = Set(assets)
166 + self.minAmount = minAmount
167 + self.maxAmount = maxAmount
168 + }
169 +
170 + func matches(_ transaction: Transaction) -> Bool {
171 + // Check addresses
172 + if !watchedAddresses.isEmpty {
173 + let hasMatchingAddress = transaction.body.outputs.contains { output in
174 + watchedAddresses.contains(output.address.value)
175 + }
176 + if !hasMatchingAddress {
177 + return false
178 + }
179 + }
180 +
181 + // Check assets
182 + if !watchedAssets.isEmpty {
183 + let hasMatchingAsset = transaction.body.outputs.contains { output in
184 + guard let assets = output.value.assets else { return false }
185 +
186 + return assets.contains { (policyId, tokens) in
187 + tokens.contains { (assetName, _) in
188 + let assetId = "\(policyId).\(assetName)"
189 + return watchedAssets.contains(assetId)
190 + }
191 + }
192 + }
193 + if !hasMatchingAsset {
194 + return false
195 + }
196 + }
197 +
198 + // Check amount range
199 + let totalOutput = transaction.body.outputs.reduce(0) { sum, output in
200 + sum + output.value.ada.lovelace
201 + }
202 +
203 + if let minAmount = minAmount, totalOutput < minAmount {
204 + return false
205 + }
206 +
207 + if let maxAmount = maxAmount, totalOutput > maxAmount {
208 + return false
209 + }
210 +
211 + return true
212 + }
213 + }
214 +
215 + // Usage
216 + let filter = TransactionFilter(
217 + addresses: ["addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer..."],
218 + minAmount: 1_000_000 // 1 ADA minimum
219 + )
220 +
221 + let allTransactions = try await client.mempoolMonitor.getMempoolTransactions()
222 + let filteredTransactions = allTransactions.compactMap { result -> Transaction? in
223 + guard case .transaction(let tx) = result else { return nil }
224 + return filter.matches(tx) ? tx : nil
225 + }
226 +
227 + print("Found \(filteredTransactions.count) matching transactions")
228 + ```
229 |
230 | ### Smart Mempool Observer
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:232:1-399:4
230 | ### Smart Mempool Observer
231 |
232 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
233 + @MainActor
234 + class SmartMempoolObserver: ObservableObject {
235 + @Published var mempoolSize: SizeAndCapacity?
236 + @Published var recentTransactions: [Transaction] = []
237 + @Published var watchedTransactions: [String: TransactionStatus] = [:]
238 + @Published var isMonitoring = false
239 +
240 + private let client: OgmiosClient
241 + private let filter: TransactionFilter
242 + private var monitoringTask: Task<Void, Never>?
243 + private var lastSnapshot: Set<String> = []
244 +
245 + enum TransactionStatus {
246 + case pending(since: Date)
247 + case confirmed(at: Date)
248 + case dropped(at: Date)
249 + }
250 +
251 + init(client: OgmiosClient, filter: TransactionFilter = TransactionFilter()) {
252 + self.client = client
253 + self.filter = filter
254 + }
255 +
256 + func startMonitoring() {
257 + guard !isMonitoring else { return }
258 + isMonitoring = true
259 +
260 + monitoringTask = Task { [weak self] in
261 + while !Task.isCancelled {
262 + await self?.performMonitoringCycle()
263 +
264 + // Wait before next cycle
265 + try? await Task.sleep(nanoseconds: 15_000_000_000) // 15 seconds
266 + }
267 + }
268 + }
269 +
270 + func stopMonitoring() {
271 + isMonitoring = false
272 + monitoringTask?.cancel()
273 + monitoringTask = nil
274 + }
275 +
276 + func watchTransaction(_ transactionId: String) {
277 + watchedTransactions[transactionId] = .pending(since: Date())
278 + }
279 +
280 + private func performMonitoringCycle() async {
281 + do {
282 + // Update mempool size
283 + let sizeResponse = try await client.mempoolMonitor.sizeOfMempool.execute()
284 + await MainActor.run {
285 + self.mempoolSize = sizeResponse.result
286 + }
287 +
288 + // Get current transactions
289 + let allTransactions = try await client.mempoolMonitor.getMempoolTransactions()
290 + let currentSnapshot = Set(allTransactions.compactMap { result in
291 + guard case .transaction(let tx) = result else { return nil }
292 + return tx.id
293 + })
294 +
295 + // Detect new transactions
296 + let newTransactions = currentSnapshot.subtracting(lastSnapshot)
297 + if !newTransactions.isEmpty {
298 + await handleNewTransactions(newTransactions, from: allTransactions)
299 + }
300 +
301 + // Detect dropped transactions
302 + let droppedTransactions = lastSnapshot.subtracting(currentSnapshot)
303 + if !droppedTransactions.isEmpty {
304 + await handleDroppedTransactions(droppedTransactions)
305 + }
306 +
307 + // Update watched transactions
308 + await updateWatchedTransactions(currentSnapshot)
309 +
310 + lastSnapshot = currentSnapshot
311 +
312 + } catch {
313 + print("Monitoring cycle error: \(error)")
314 + }
315 + }
316 +
317 + private func handleNewTransactions(_ newTxIds: Set<String>, from allTransactions: [NextTransactionResult]) async {
318 + let newTxs = allTransactions.compactMap { result -> Transaction? in
319 + guard case .transaction(let tx) = result else { return nil }
320 + return newTxIds.contains(tx.id) ? tx : nil
321 + }
322 +
323 + let filteredNewTxs = newTxs.filter { filter.matches($0) }
324 +
325 + await MainActor.run {
326 + // Add to recent transactions (keep last 20)
327 + self.recentTransactions.insert(contentsOf: filteredNewTxs, at: 0)
328 + if self.recentTransactions.count > 20 {
329 + self.recentTransactions = Array(self.recentTransactions.prefix(20))
330 + }
331 + }
332 +
333 + // Notify about new transactions
334 + for tx in filteredNewTxs {
335 + print("New transaction: \(tx.id)")
336 + await handleNewTransaction(tx)
337 + }
338 + }
339 +
340 + private func handleDroppedTransactions(_ droppedTxIds: Set<String>) async {
341 + await MainActor.run {
342 + // Remove from recent transactions
343 + self.recentTransactions.removeAll { tx in
344 + droppedTxIds.contains(tx.id)
345 + }
346 + }
347 +
348 + for txId in droppedTxIds {
349 + print("Transaction dropped from mempool: \(txId)")
350 + }
351 + }
352 +
353 + private func updateWatchedTransactions(_ currentSnapshot: Set<String>) async {
354 + await MainActor.run {
355 + for (txId, status) in self.watchedTransactions {
356 + switch status {
357 + case .pending(_):
358 + if !currentSnapshot.contains(txId) {
359 + // Transaction left mempool - likely confirmed
360 + self.watchedTransactions[txId] = .confirmed(at: Date())
361 + }
362 + case .confirmed(_), .dropped(_):
363 + // Keep confirmed/dropped status
364 + break
365 + }
366 + }
367 + }
368 + }
369 +
370 + private func handleNewTransaction(_ transaction: Transaction) async {
371 + // Analyze transaction
372 + let totalOutput = transaction.body.outputs.reduce(0) { sum, output in
373 + sum + output.value.ada.lovelace
374 + }
375 +
376 + print(" Fee: \(transaction.body.fee) lovelace")
377 + print(" Total output: \(totalOutput) lovelace")
378 + print(" Inputs: \(transaction.body.inputs.count)")
379 + print(" Outputs: \(transaction.body.outputs.count)")
380 +
381 + // Check for interesting patterns
382 + if transaction.body.fee > 1_000_000 { // High fee (> 1 ADA)
383 + print(" ⚠️ High fee transaction detected!")
384 + }
385 +
386 + if transaction.body.outputs.count > 100 {
387 + print(" 📊 Large transaction with many outputs")
388 + }
389 +
390 + // Check for native assets
391 + let hasNativeAssets = transaction.body.outputs.contains { output in
392 + output.value.assets != nil
393 + }
394 + if hasNativeAssets {
395 + print(" 🎨 Contains native assets")
396 + }
397 + }
398 + }
399 + ```
400 |
401 | ## Real-Time SwiftUI Integration
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:405:1-681:4
403 | ### Mempool Dashboard View
404 |
405 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
406 + import SwiftUI
407 +
408 + struct MempoolDashboardView: View {
409 + @StateObject private var observer: SmartMempoolObserver
410 + @State private var selectedFilter = FilterType.all
411 + @State private var watchedTxId = ""
412 +
413 + enum FilterType: String, CaseIterable {
414 + case all = "All"
415 + case highFee = "High Fee"
416 + case largeAmount = "Large Amount"
417 + case nativeAssets = "Native Assets"
418 + }
419 +
420 + init(client: OgmiosClient) {
421 + self._observer = StateObject(wrappedValue: SmartMempoolObserver(client: client))
422 + }
423 +
424 + var body: some View {
425 + NavigationView {
426 + VStack(spacing: 20) {
427 + // Header with monitoring status
428 + headerView
429 +
430 + // Mempool stats
431 + if let size = observer.mempoolSize {
432 + mempoolStatsView(size)
433 + }
434 +
435 + // Transaction filter
436 + filterView
437 +
438 + // Recent transactions list
439 + transactionListView
440 +
441 + // Transaction watcher
442 + transactionWatcherView
443 + }
444 + .padding()
445 + .navigationTitle("Mempool Monitor")
446 + .onAppear {
447 + observer.startMonitoring()
448 + }
449 + .onDisappear {
450 + observer.stopMonitoring()
451 + }
452 + }
453 + }
454 +
455 + @ViewBuilder
456 + private var headerView: some View {
457 + HStack {
458 + Circle()
459 + .fill(observer.isMonitoring ? Color.green : Color.red)
460 + .frame(width: 12, height: 12)
461 +
462 + Text(observer.isMonitoring ? "Monitoring" : "Stopped")
463 + .font(.headline)
464 +
465 + Spacer()
466 +
467 + Button(observer.isMonitoring ? "Stop" : "Start") {
468 + if observer.isMonitoring {
469 + observer.stopMonitoring()
470 + } else {
471 + observer.startMonitoring()
472 + }
473 + }
474 + .buttonStyle(.bordered)
475 + }
476 + }
477 +
478 + @ViewBuilder
479 + private func mempoolStatsView(_ size: SizeAndCapacity) -> some View {
480 + VStack(spacing: 8) {
481 + HStack {
482 + Text("Mempool Size")
483 + .font(.caption)
484 + .foregroundColor(.secondary)
485 + Spacer()
486 + Text("\(size.currentSize) / \(size.capacity)")
487 + .font(.caption)
488 + .fontWeight(.semibold)
489 + }
490 +
491 + ProgressView(
492 + value: Double(size.currentSize),
493 + total: Double(size.capacity)
494 + )
495 +
496 + let utilization = Double(size.currentSize) / Double(size.capacity) * 100
497 + Text("\(String(format: "%.1f", utilization))% full")
498 + .font(.caption2)
499 + .foregroundColor(.secondary)
500 + }
501 + .padding()
502 + .background(Color.gray.opacity(0.1))
503 + .cornerRadius(8)
504 + }
505 +
506 + @ViewBuilder
507 + private var filterView: some View {
508 + VStack {
509 + Text("Filter Transactions")
510 + .font(.caption)
511 + .foregroundColor(.secondary)
512 +
513 + Picker("Filter", selection: $selectedFilter) {
514 + ForEach(FilterType.allCases, id: \.self) { filter in
515 + Text(filter.rawValue).tag(filter)
516 + }
517 + }
518 + .pickerStyle(.segmented)
519 + }
520 + }
521 +
522 + @ViewBuilder
523 + private var transactionListView: some View {
524 + VStack(alignment: .leading) {
525 + Text("Recent Transactions")
526 + .font(.headline)
527 +
528 + if observer.recentTransactions.isEmpty {
529 + Text("No transactions yet")
530 + .foregroundColor(.secondary)
531 + .frame(maxWidth: .infinity, alignment: .center)
532 + .padding()
533 + } else {
534 + List(filteredTransactions, id: \.id) { transaction in
535 + TransactionRowView(transaction: transaction)
536 + }
537 + .listStyle(.plain)
538 + .frame(maxHeight: 300)
539 + }
540 + }
541 + }
542 +
543 + @ViewBuilder
544 + private var transactionWatcherView: some View {
545 + VStack(alignment: .leading) {
546 + Text("Watch Transaction")
547 + .font(.headline)
548 +
549 + HStack {
550 + TextField("Transaction ID", text: $watchedTxId)
551 + .textFieldStyle(.roundedBorder)
552 +
553 + Button("Watch") {
554 + observer.watchTransaction(watchedTxId)
555 + watchedTxId = ""
556 + }
557 + .disabled(watchedTxId.isEmpty)
558 + .buttonStyle(.borderedProminent)
559 + }
560 +
561 + // Watched transactions status
562 + if !observer.watchedTransactions.isEmpty {
563 + ForEach(Array(observer.watchedTransactions.keys), id: \.self) { txId in
564 + if let status = observer.watchedTransactions[txId] {
565 + WatchedTransactionView(transactionId: txId, status: status)
566 + }
567 + }
568 + }
569 + }
570 + }
571 +
572 + private var filteredTransactions: [Transaction] {
573 + observer.recentTransactions.filter { transaction in
574 + switch selectedFilter {
575 + case .all:
576 + return true
577 + case .highFee:
578 + return transaction.body.fee > 1_000_000 // > 1 ADA
579 + case .largeAmount:
580 + let totalOutput = transaction.body.outputs.reduce(0) { sum, output in
581 + sum + output.value.ada.lovelace
582 + }
583 + return totalOutput > 10_000_000 // > 10 ADA
584 + case .nativeAssets:
585 + return transaction.body.outputs.contains { output in
586 + output.value.assets != nil
587 + }
588 + }
589 + }
590 + }
591 + }
592 +
593 + struct TransactionRowView: View {
594 + let transaction: Transaction
595 +
596 + var body: some View {
597 + VStack(alignment: .leading, spacing: 4) {
598 + HStack {
599 + Text(transaction.id)
600 + .font(.system(.caption, design: .monospaced))
601 + .lineLimit(1)
602 + .truncationMode(.middle)
603 +
604 + Spacer()
605 +
606 + Text("\(transaction.body.fee) lovelace")
607 + .font(.caption2)
608 + .foregroundColor(.secondary)
609 + }
610 +
611 + HStack {
612 + Text("\(transaction.body.inputs.count) inputs")
613 + Text("•")
614 + Text("\(transaction.body.outputs.count) outputs")
615 +
616 + Spacer()
617 +
618 + if transaction.body.outputs.contains(where: { $0.value.assets != nil }) {
619 + Image(systemName: "paintbrush.fill")
620 + .foregroundColor(.purple)
621 + .font(.caption2)
622 + }
623 + }
624 + .font(.caption2)
625 + .foregroundColor(.secondary)
626 + }
627 + .padding(.vertical, 2)
628 + }
629 + }
630 +
631 + struct WatchedTransactionView: View {
632 + let transactionId: String
633 + let status: SmartMempoolObserver.TransactionStatus
634 +
635 + var body: some View {
636 + HStack {
637 + statusIcon
638 +
639 + VStack(alignment: .leading) {
640 + Text(transactionId)
641 + .font(.system(.caption, design: .monospaced))
642 + .lineLimit(1)
643 + .truncationMode(.middle)
644 +
645 + Text(statusText)
646 + .font(.caption2)
647 + .foregroundColor(.secondary)
648 + }
649 +
650 + Spacer()
651 + }
652 + .padding(.vertical, 2)
653 + }
654 +
655 + @ViewBuilder
656 + private var statusIcon: some View {
657 + switch status {
658 + case .pending(_):
659 + Image(systemName: "clock")
660 + .foregroundColor(.orange)
661 + case .confirmed(_):
662 + Image(systemName: "checkmark.circle.fill")
663 + .foregroundColor(.green)
664 + case .dropped(_):
665 + Image(systemName: "xmark.circle.fill")
666 + .foregroundColor(.red)
667 + }
668 + }
669 +
670 + private var statusText: String {
671 + switch status {
672 + case .pending(let date):
673 + return "Pending since \(date.formatted(.dateTime.hour().minute()))"
674 + case .confirmed(let date):
675 + return "Confirmed at \(date.formatted(.dateTime.hour().minute()))"
676 + case .dropped(let date):
677 + return "Dropped at \(date.formatted(.dateTime.hour().minute()))"
678 + }
679 + }
680 + }
681 + ```
682 |
683 | ## Performance Optimizations
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:687:1-770:4
685 | ### Efficient Mempool Polling
686 |
687 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
688 + actor EfficientMempoolMonitor {
689 + private let client: OgmiosClient
690 + private var lastKnownSize: Int = 0
691 + private var pollInterval: TimeInterval = 30.0 // Start with 30 seconds
692 + private let minPollInterval: TimeInterval = 5.0
693 + private let maxPollInterval: TimeInterval = 120.0
694 +
695 + init(client: OgmiosClient) {
696 + self.client = client
697 + }
698 +
699 + func adaptiveMonitoring() async {
700 + while !Task.isCancelled {
701 + do {
702 + // Check mempool size first (lightweight operation)
703 + let sizeResponse = try await client.mempoolMonitor.sizeOfMempool.execute()
704 + let currentSize = sizeResponse.result.currentSize
705 +
706 + // Adapt polling interval based on activity
707 + if currentSize != lastKnownSize {
708 + // Activity detected - increase polling frequency
709 + pollInterval = max(pollInterval * 0.8, minPollInterval)
710 +
711 + // Full mempool scan on changes
712 + await performFullScan()
713 + } else {
714 + // No changes - decrease polling frequency
715 + pollInterval = min(pollInterval * 1.2, maxPollInterval)
716 + }
717 +
718 + lastKnownSize = currentSize
719 +
720 + print("Next poll in \(String(format: "%.1f", pollInterval)) seconds")
721 + try await Task.sleep(nanoseconds: UInt64(pollInterval * 1_000_000_000))
722 +
723 + } catch {
724 + print("Adaptive monitoring error: \(error)")
725 + try await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds on error
726 + }
727 + }
728 + }
729 +
730 + private func performFullScan() async {
731 + do {
732 + let transactions = try await client.mempoolMonitor.getMempoolTransactions()
733 + print("Full scan found \(transactions.count) transactions")
734 +
735 + // Process interesting transactions
736 + await analyzeTransactions(transactions)
737 +
738 + } catch {
739 + print("Full scan error: \(error)")
740 + }
741 + }
742 +
743 + private func analyzeTransactions(_ transactions: [NextTransactionResult]) async {
744 + var highFeeCount = 0
745 + var nativeAssetCount = 0
746 + var totalFees: UInt64 = 0
747 +
748 + for result in transactions {
749 + guard case .transaction(let tx) = result else { continue }
750 +
751 + totalFees += tx.body.fee
752 +
753 + if tx.body.fee > 1_000_000 { // > 1 ADA
754 + highFeeCount += 1
755 + }
756 +
757 + let hasAssets = tx.body.outputs.contains { output in
758 + output.value.assets != nil
759 + }
760 + if hasAssets {
761 + nativeAssetCount += 1
762 + }
763 + }
764 +
765 + let avgFee = transactions.count > 0 ? totalFees / UInt64(transactions.count) : 0
766 +
767 + print("Analysis: \(highFeeCount) high-fee, \(nativeAssetCount) with assets, avg fee: \(avgFee) lovelace")
768 + }
769 + }
770 + ```
771 |
772 | ### Batch Transaction Processing
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:774:1-831:4
772 | ### Batch Transaction Processing
773 |
774 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
775 + actor BatchMempoolProcessor {
776 + private let client: OgmiosClient
777 + private let batchSize = 50
778 + private var processingQueue: [Transaction] = []
779 +
780 + init(client: OgmiosClient) {
781 + self.client = client
782 + }
783 +
784 + func processMempoolInBatches() async {
785 + do {
786 + let allTransactions = try await client.mempoolMonitor.getMempoolTransactions()
787 +
788 + let transactions = allTransactions.compactMap { result -> Transaction? in
789 + guard case .transaction(let tx) = result else { return nil }
790 + return tx
791 + }
792 +
793 + // Process in batches to avoid memory pressure
794 + for batch in transactions.chunked(into: batchSize) {
795 + await processBatch(batch)
796 +
797 + // Small delay between batches to prevent overwhelming
798 + try await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
799 + }
800 +
801 + } catch {
802 + print("Batch processing error: \(error)")
803 + }
804 + }
805 +
806 + private func processBatch(_ transactions: [Transaction]) async {
807 + await withTaskGroup(of: Void.self) { group in
808 + for transaction in transactions {
809 + group.addTask {
810 + await self.processTransaction(transaction)
811 + }
812 + }
813 + }
814 + }
815 +
816 + private func processTransaction(_ transaction: Transaction) async {
817 + // Heavy processing work here
818 + // e.g., database updates, notifications, analysis
819 + print("Processing \(transaction.id)")
820 + }
821 + }
822 +
823 + // Array extension for chunking
824 + extension Array {
825 + func chunked(into size: Int) -> [[Element]] {
826 + return stride(from: 0, to: count, by: size).map {
827 + Array(self[$0..<Swift.min($0 + size, count)])
828 + }
829 + }
830 + }
831 + ```
832 |
833 | ## Testing Mempool Monitoring
warning: Unknown option 'swift path' in code block.
--> Tutorials/MempoolMonitoring.md:835:1-922:4
833 | ## Testing Mempool Monitoring
834 |
835 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
836 + import XCTest
837 + @testable import SwiftOgmios
838 +
839 + class MempoolMonitoringTests: XCTestCase {
840 + var client: OgmiosClient!
841 + var observer: SmartMempoolObserver!
842 +
843 + override func setUp() async throws {
844 + client = try await OgmiosClient(
845 + httpOnly: false,
846 + webSocketConnection: MockWebSocketConnection()
847 + )
848 + observer = SmartMempoolObserver(client: client)
849 + }
850 +
851 + func testBasicMempoolOperations() async throws {
852 + // Test acquiring mempool
853 + let snapshot = try await client.mempoolMonitor.acquireMempool.execute()
854 + XCTAssertNotNil(snapshot.result.slot)
855 +
856 + // Test getting size
857 + let size = try await client.mempoolMonitor.sizeOfMempool.execute()
858 + XCTAssertGreaterThanOrEqual(size.result.currentSize, 0)
859 + XCTAssertGreaterThan(size.result.capacity, 0)
860 +
861 + // Test checking transaction existence
862 + let hasTransaction = try await client.mempoolMonitor.hasTransaction.execute(
863 + transaction: TransactionId("test-tx-id"),
864 + id: .generateNextNanoId()
865 + )
866 + XCTAssertNotNil(hasTransaction.result)
867 +
868 + // Test releasing mempool
869 + let release = try await client.mempoolMonitor.releaseMempool.execute()
870 + XCTAssertNotNil(release)
871 + }
872 +
873 + func testTransactionFilter() {
874 + let filter = TransactionFilter(
875 + addresses: ["addr1test123"],
876 + minAmount: 1_000_000
877 + )
878 +
879 + // Create test transaction
880 + let mockTransaction = createMockTransaction(
881 + outputs: [
882 + createMockOutput(address: "addr1test123", amount: 2_000_000)
883 + ]
884 + )
885 +
886 + XCTAssertTrue(filter.matches(mockTransaction))
887 +
888 + // Test with different address
889 + let mockTransaction2 = createMockTransaction(
890 + outputs: [
891 + createMockOutput(address: "addr1different", amount: 2_000_000)
892 + ]
893 + )
894 +
895 + XCTAssertFalse(filter.matches(mockTransaction2))
896 + }
897 +
898 + func testMempoolObserver() async throws {
899 + // Start monitoring
900 + await observer.startMonitoring()
901 + XCTAssertTrue(observer.isMonitoring)
902 +
903 + // Wait for some monitoring cycles
904 + try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds
905 +
906 + // Stop monitoring
907 + await observer.stopMonitoring()
908 + XCTAssertFalse(observer.isMonitoring)
909 + }
910 +
911 + private func createMockTransaction(outputs: [TransactionOutput]) -> Transaction {
912 + // Create mock transaction for testing
913 + // Implementation depends on your Transaction structure
914 + fatalError("Implement mock transaction creation")
915 + }
916 +
917 + private func createMockOutput(address: String, amount: UInt64) -> TransactionOutput {
918 + // Create mock transaction output
919 + fatalError("Implement mock output creation")
920 + }
921 + }
922 + ```
923 |
924 | ## Best Practices
warning: Extraneous content found after a link in task group list item
--> Tutorials/MempoolMonitoring.md:946:3-946:58
944 | ## See Also
945 |
946 + - <doc:GettingStarted> - Basic setup and first operations
| ╰─suggestion: Remove extraneous content
947 | - <doc:TransactionSubmission> - Submitting transactions to monitor
948 | - <doc:ChainSynchronization> - Confirming mempool transactions in blocks
warning: Extraneous content found after a link in task group list item
--> Tutorials/MempoolMonitoring.md:947:3-947:67
945 |
946 | - <doc:GettingStarted> - Basic setup and first operations
947 + - <doc:TransactionSubmission> - Submitting transactions to monitor
| ╰─suggestion: Remove extraneous content
948 | - <doc:ChainSynchronization> - Confirming mempool transactions in blocks
949 | - <doc:ErrorHandling> - Handling mempool monitoring errors
warning: Extraneous content found after a link in task group list item
--> Tutorials/MempoolMonitoring.md:948:3-948:73
946 | - <doc:GettingStarted> - Basic setup and first operations
947 | - <doc:TransactionSubmission> - Submitting transactions to monitor
948 + - <doc:ChainSynchronization> - Confirming mempool transactions in blocks
| ╰─suggestion: Remove extraneous content
949 | - <doc:ErrorHandling> - Handling mempool monitoring errors
950 | - <doc:TransportTypes> - Optimizing connection types for monitoring
warning: Extraneous content found after a link in task group list item
--> Tutorials/MempoolMonitoring.md:949:3-949:59
947 | - <doc:TransactionSubmission> - Submitting transactions to monitor
948 | - <doc:ChainSynchronization> - Confirming mempool transactions in blocks
949 + - <doc:ErrorHandling> - Handling mempool monitoring errors
| ╰─suggestion: Remove extraneous content
950 | - <doc:TransportTypes> - Optimizing connection types for monitoring
warning: Extraneous content found after a link in task group list item
--> Tutorials/MempoolMonitoring.md:950:3-950:68
948 | - <doc:ChainSynchronization> - Confirming mempool transactions in blocks
949 | - <doc:ErrorHandling> - Handling mempool monitoring errors
950 + - <doc:TransportTypes> - Optimizing connection types for monitoring
| ╰─suggestion: Remove extraneous content
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:23:1-44:4
21 | ### Simple Submission
22 |
23 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
24 + import SwiftOgmios
25 +
26 + // Create client (HTTP or WebSocket both work for submission)
27 + let client = try await OgmiosClient(httpOnly: true)
28 +
29 + // Your transaction CBOR bytes (from transaction builder)
30 + let transactionCBOR = Data(/* your transaction bytes */)
31 +
32 + do {
33 + let response = try await client.transactionSubmission.submitTransaction.execute(
34 + transaction: transactionCBOR,
35 + id: .generateNextNanoId()
36 + )
37 +
38 + print("Transaction submitted successfully!")
39 + print("Transaction ID: \(response.result)")
40 +
41 + } catch {
42 + print("Transaction submission failed: \(error)")
43 + }
44 + ```
45 |
46 | ### Transaction Building Integration
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:48:1-93:4
46 | ### Transaction Building Integration
47 |
48 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
49 + import SwiftCardanoTxBuilder // Assumed transaction builder library
50 +
51 + class TransactionService {
52 + private let client: OgmiosClient
53 + private let txBuilder: TransactionBuilder
54 +
55 + init(client: OgmiosClient) {
56 + self.client = client
57 + self.txBuilder = TransactionBuilder()
58 + }
59 +
60 + func sendAda(
61 + from senderKey: PrivateKey,
62 + to recipientAddress: Address,
63 + amount: Lovelace,
64 + changeAddress: Address
65 + ) async throws -> String {
66 + // 1. Get protocol parameters for fee calculation
67 + let protocolParams = try await client.ledgerStateQuery.protocolParameters.execute()
68 +
69 + // 2. Get UTxOs for the sender
70 + let utxoResponse = try await client.ledgerStateQuery.utxo.execute(
71 + addresses: [senderKey.publicKey.address],
72 + id: .generateNextNanoId()
73 + )
74 +
75 + // 3. Build transaction
76 + let transaction = try txBuilder
77 + .addInputs(from: utxoResponse.result)
78 + .addOutput(address: recipientAddress, amount: amount)
79 + .addOutput(address: changeAddress, amount: .calculateChange)
80 + .calculateFee(using: protocolParams.result)
81 + .sign(with: senderKey)
82 + .build()
83 +
84 + // 4. Submit transaction
85 + let submitResponse = try await client.transactionSubmission.submitTransaction.execute(
86 + transaction: transaction.toCBOR(),
87 + id: .generateNextNanoId()
88 + )
89 +
90 + return submitResponse.result
91 + }
92 + }
93 + ```
94 |
95 | ## Comprehensive Error Handling
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:101:1-174:4
99 | ### Error Code Mapping
100 |
101 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
102 + enum TransactionSubmissionError: Error, LocalizedError {
103 + case eraMismatch
104 + case invalidSignatures
105 + case missingSignatures
106 + case missingScripts
107 + case feeTooSmall(required: UInt64, provided: UInt64)
108 + case valueNotConserved
109 + case unknownOutputReference
110 + case transactionTooLarge(size: Int, limit: Int)
111 + case networkError(String)
112 + case unknownError(String)
113 +
114 + var errorDescription: String? {
115 + switch self {
116 + case .eraMismatch:
117 + return "Transaction not valid in current era"
118 + case .invalidSignatures:
119 + return "Transaction signatures are invalid"
120 + case .missingSignatures:
121 + return "Required signatures are missing"
122 + case .missingScripts:
123 + return "Required script witnesses are missing"
124 + case .feeTooSmall(let required, let provided):
125 + return "Fee too small: required \(required), provided \(provided)"
126 + case .valueNotConserved:
127 + return "Input and output values don't match"
128 + case .unknownOutputReference:
129 + return "Referenced UTxO not found"
130 + case .transactionTooLarge(let size, let limit):
131 + return "Transaction too large: \(size) bytes (limit: \(limit))"
132 + case .networkError(let message):
133 + return "Network error: \(message)"
134 + case .unknownError(let message):
135 + return "Unknown error: \(message)"
136 + }
137 + }
138 +
139 + var recoveryStrategy: RecoveryStrategy {
140 + switch self {
141 + case .eraMismatch:
142 + return .rebuildTransaction
143 + case .invalidSignatures, .missingSignatures:
144 + return .fixSignatures
145 + case .missingScripts:
146 + return .addScripts
147 + case .feeTooSmall:
148 + return .increaseFee
149 + case .valueNotConserved:
150 + return .fixBalance
151 + case .unknownOutputReference:
152 + return .refreshUtxos
153 + case .transactionTooLarge:
154 + return .optimizeTransaction
155 + case .networkError:
156 + return .retry
157 + case .unknownError:
158 + return .investigate
159 + }
160 + }
161 + }
162 +
163 + enum RecoveryStrategy {
164 + case rebuildTransaction
165 + case fixSignatures
166 + case addScripts
167 + case increaseFee
168 + case fixBalance
169 + case refreshUtxos
170 + case optimizeTransaction
171 + case retry
172 + case investigate
173 + }
174 + ```
175 |
176 | ### Error Processing Function
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:178:1-238:4
176 | ### Error Processing Function
177 |
178 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
179 + extension TransactionService {
180 + func processSubmissionError(_ error: Error) -> TransactionSubmissionError {
181 + guard let ogmiosError = error as? OgmiosError,
182 + case .responseError(let message) = ogmiosError else {
183 + return .networkError(error.localizedDescription)
184 + }
185 +
186 + // Parse error code from message
187 + let errorCode = extractErrorCode(from: message)
188 +
189 + switch errorCode {
190 + case 3005:
191 + return .eraMismatch
192 + case 3100:
193 + return .invalidSignatures
194 + case 3101:
195 + return .missingSignatures
196 + case 3102:
197 + return .missingScripts
198 + case 3122:
199 + let (required, provided) = extractFeeInfo(from: message)
200 + return .feeTooSmall(required: required, provided: provided)
201 + case 3123:
202 + return .valueNotConserved
203 + case 3117:
204 + return .unknownOutputReference
205 + case 3119:
206 + let (size, limit) = extractSizeInfo(from: message)
207 + return .transactionTooLarge(size: size, limit: limit)
208 + default:
209 + return .unknownError(message)
210 + }
211 + }
212 +
213 + private func extractErrorCode(from message: String) -> Int {
214 + // Extract 4-digit error code from message
215 + let pattern = #"(\d{4})"#
216 + let regex = try! NSRegularExpression(pattern: pattern)
217 + let range = NSRange(message.startIndex..<message.endIndex, in: message)
218 +
219 + if let match = regex.firstMatch(in: message, options: [], range: range) {
220 + let codeRange = Range(match.range(at: 1), in: message)!
221 + return Int(String(message[codeRange])) ?? 0
222 + }
223 +
224 + return 0
225 + }
226 +
227 + private func extractFeeInfo(from message: String) -> (required: UInt64, provided: UInt64) {
228 + // Parse fee information from error message
229 + // Implementation depends on specific error message format
230 + return (required: 0, provided: 0)
231 + }
232 +
233 + private func extractSizeInfo(from message: String) -> (size: Int, limit: Int) {
234 + // Parse size information from error message
235 + return (size: 0, limit: 0)
236 + }
237 + }
238 + ```
239 |
240 | ## Retry Mechanisms with Backoff
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:244:1-322:4
242 | ### Intelligent Retry Strategy
243 |
244 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
245 + class ResilientTransactionSubmitter {
246 + private let client: OgmiosClient
247 + private let maxRetries: Int
248 + private let baseDelay: TimeInterval
249 +
250 + init(client: OgmiosClient, maxRetries: Int = 3, baseDelay: TimeInterval = 1.0) {
251 + self.client = client
252 + self.maxRetries = maxRetries
253 + self.baseDelay = baseDelay
254 + }
255 +
256 + func submitWithRetry(
257 + transaction: Data,
258 + onRetry: ((TransactionSubmissionError, Int) -> Void)? = nil
259 + ) async throws -> String {
260 + var lastError: TransactionSubmissionError?
261 +
262 + for attempt in 1...maxRetries {
263 + do {
264 + let response = try await client.transactionSubmission.submitTransaction.execute(
265 + transaction: transaction,
266 + id: .generateNextNanoId()
267 + )
268 +
269 + return response.result
270 +
271 + } catch {
272 + let submissionError = processSubmissionError(error)
273 + lastError = submissionError
274 +
275 + // Check if error is retryable
276 + if !submissionError.isRetryable || attempt == maxRetries {
277 + throw submissionError
278 + }
279 +
280 + // Notify caller of retry attempt
281 + onRetry?(submissionError, attempt)
282 +
283 + // Calculate delay with exponential backoff
284 + let delay = baseDelay * pow(2.0, Double(attempt - 1))
285 + try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
286 + }
287 + }
288 +
289 + throw lastError ?? TransactionSubmissionError.unknownError("Max retries exceeded")
290 + }
291 +
292 + private func processSubmissionError(_ error: Error) -> TransactionSubmissionError {
293 + // Implementation from previous example
294 + return .networkError(error.localizedDescription)
295 + }
296 + }
297 +
298 + extension TransactionSubmissionError {
299 + var isRetryable: Bool {
300 + switch self {
301 + case .networkError:
302 + return true
303 + case .unknownOutputReference:
304 + return true // UTxO might become available
305 + case .feeTooSmall:
306 + return false // Needs manual fix
307 + case .invalidSignatures, .missingSignatures:
308 + return false // Needs manual fix
309 + case .valueNotConserved:
310 + return false // Needs manual fix
311 + case .transactionTooLarge:
312 + return false // Needs optimization
313 + case .eraMismatch:
314 + return true // Era might change
315 + case .missingScripts:
316 + return false // Needs manual fix
317 + case .unknownError:
318 + return true // Might be temporary
319 + }
320 + }
321 + }
322 + ```
323 |
324 | ### Usage Example
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:326:1-365:4
324 | ### Usage Example
325 |
326 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
327 + let submitter = ResilientTransactionSubmitter(client: client)
328 +
329 + do {
330 + let txId = try await submitter.submitWithRetry(
331 + transaction: transactionCBOR
332 + ) { error, attempt in
333 + print("Attempt \(attempt) failed: \(error.localizedDescription)")
334 +
335 + // Handle specific errors
336 + switch error.recoveryStrategy {
337 + case .retry:
338 + print(" Retrying...")
339 + case .increaseFee:
340 + print(" Consider increasing the fee")
341 + case .refreshUtxos:
342 + print(" UTxO set might have changed, consider refreshing")
343 + default:
344 + print(" Manual intervention required")
345 + }
346 + }
347 +
348 + print("Transaction submitted: \(txId)")
349 +
350 + } catch let error as TransactionSubmissionError {
351 + print("Final submission error: \(error.localizedDescription)")
352 +
353 + // Provide user guidance based on error type
354 + switch error.recoveryStrategy {
355 + case .increaseFee:
356 + print("Please increase the transaction fee and try again")
357 + case .fixBalance:
358 + print("Please check transaction inputs and outputs")
359 + case .fixSignatures:
360 + print("Please verify transaction signatures")
361 + default:
362 + print("Please check transaction details and try again")
363 + }
364 + }
365 + ```
366 |
367 | ## Transaction Confirmation Monitoring
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:371:1-438:4
369 | ### Basic Confirmation Check
370 |
371 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
372 + class TransactionMonitor {
373 + private let client: OgmiosClient
374 +
375 + init(client: OgmiosClient) {
376 + self.client = client
377 + }
378 +
379 + func waitForConfirmation(
380 + transactionId: String,
381 + requiredConfirmations: Int = 1,
382 + timeoutSeconds: TimeInterval = 300
383 + ) async throws -> Bool {
384 + let startTime = Date()
385 +
386 + while Date().timeIntervalSince(startTime) < timeoutSeconds {
387 + // Check if transaction exists in mempool
388 + let mempoolResponse = try await client.mempoolMonitor.hasTransaction.execute(
389 + transaction: TransactionId(transactionId),
390 + id: .generateNextNanoId()
391 + )
392 +
393 + if mempoolResponse.result {
394 + print("Transaction \(transactionId) is in mempool")
395 +
396 + // Wait a bit and check again
397 + try await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds
398 + continue
399 + }
400 +
401 + // Transaction not in mempool, check if it's confirmed
402 + let confirmations = try await getConfirmationCount(transactionId: transactionId)
403 +
404 + if confirmations >= requiredConfirmations {
405 + print("Transaction \(transactionId) confirmed with \(confirmations) confirmations")
406 + return true
407 + }
408 +
409 + // Wait before next check
410 + try await Task.sleep(nanoseconds: 20_000_000_000) // 20 seconds
411 + }
412 +
413 + throw TransactionMonitorError.timeout
414 + }
415 +
416 + private func getConfirmationCount(transactionId: String) async throws -> Int {
417 + // Get current tip
418 + let tipResponse = try await client.ledgerStateQuery.tip.execute()
419 + let currentHeight = tipResponse.result.height
420 +
421 + // Find transaction block (this is simplified - in practice you'd need to
422 + // maintain a transaction index or use chain sync to track transactions)
423 +
424 + // For this example, we'll assume the transaction was found at a specific height
425 + // In a real implementation, you would:
426 + // 1. Use chain sync to track transactions
427 + // 2. Maintain a database of transaction locations
428 + // 3. Or use external indexing services
429 +
430 + return 0 // Placeholder - implement based on your transaction tracking strategy
431 + }
432 + }
433 +
434 + enum TransactionMonitorError: Error {
435 + case timeout
436 + case notFound
437 + }
438 + ```
439 |
440 | ### Advanced Confirmation with Chain Sync
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:442:1-506:4
440 | ### Advanced Confirmation with Chain Sync
441 |
442 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
443 + actor TransactionTracker {
444 + private var trackedTransactions: [String: TransactionStatus] = [:]
445 + private let client: OgmiosClient
446 +
447 + struct TransactionStatus {
448 + let transactionId: String
449 + var blockHeight: BlockHeight?
450 + var confirmations: Int = 0
451 + var isRolledBack = false
452 + let submissionTime: Date
453 + }
454 +
455 + init(client: OgmiosClient) {
456 + self.client = client
457 + }
458 +
459 + func trackTransaction(_ transactionId: String) {
460 + trackedTransactions[transactionId] = TransactionStatus(
461 + transactionId: transactionId,
462 + submissionTime: Date()
463 + )
464 + }
465 +
466 + func processBlock(_ block: Block, currentTip: BlockHeight) {
467 + guard let transactions = block.body else { return }
468 +
469 + // Check if any tracked transactions are in this block
470 + for transaction in transactions {
471 + if let status = trackedTransactions[transaction.id] {
472 + var updatedStatus = status
473 + updatedStatus.blockHeight = block.header.blockHeight
474 + updatedStatus.confirmations = Int(currentTip - block.header.blockHeight) + 1
475 + updatedStatus.isRolledBack = false
476 +
477 + trackedTransactions[transaction.id] = updatedStatus
478 +
479 + print("Transaction \(transaction.id) confirmed in block \(block.header.blockHeight)")
480 + }
481 + }
482 + }
483 +
484 + func handleRollback(to height: BlockHeight) {
485 + // Mark transactions in rolled-back blocks
486 + for (txId, var status) in trackedTransactions {
487 + if let blockHeight = status.blockHeight, blockHeight > height {
488 + status.isRolledBack = true
489 + status.blockHeight = nil
490 + status.confirmations = 0
491 + trackedTransactions[txId] = status
492 +
493 + print("Transaction \(txId) rolled back")
494 + }
495 + }
496 + }
497 +
498 + func getStatus(for transactionId: String) -> TransactionStatus? {
499 + return trackedTransactions[transactionId]
500 + }
501 +
502 + func removeTransaction(_ transactionId: String) {
503 + trackedTransactions.removeValue(forKey: transactionId)
504 + }
505 + }
506 + ```
507 |
508 | ## Transaction Evaluation
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:514:1-543:4
512 | ### Basic Transaction Evaluation
513 |
514 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
515 + func evaluateTransaction(
516 + transaction: Data,
517 + additionalUtxo: [TransactionInput: TransactionOutput] = [:]
518 + ) async throws -> EvaluationResult {
519 + let response = try await client.transactionSubmission.evaluateTransaction.execute(
520 + transaction: transaction,
521 + additionalUtxo: additionalUtxo,
522 + id: .generateNextNanoId()
523 + )
524 +
525 + return response.result
526 + }
527 +
528 + // Usage
529 + do {
530 + let evaluation = try await evaluateTransaction(transaction: transactionCBOR)
531 +
532 + print("Evaluation successful:")
533 + for (redeemer, budget) in evaluation {
534 + print(" Redeemer: \(redeemer)")
535 + print(" CPU: \(budget.cpu) steps")
536 + print(" Memory: \(budget.memory) units")
537 + }
538 +
539 + } catch {
540 + print("Evaluation failed: \(error)")
541 + // Transaction would fail if submitted
542 + }
543 + ```
544 |
545 | ### Pre-Submission Validation
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:547:1-649:4
545 | ### Pre-Submission Validation
546 |
547 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
548 + class TransactionValidator {
549 + private let client: OgmiosClient
550 +
551 + init(client: OgmiosClient) {
552 + self.client = client
553 + }
554 +
555 + func validateBeforeSubmission(transaction: Data) async throws -> ValidationResult {
556 + var issues: [ValidationIssue] = []
557 +
558 + // 1. Evaluate transaction execution
559 + do {
560 + let evaluation = try await client.transactionSubmission.evaluateTransaction.execute(
561 + transaction: transaction,
562 + additionalUtxo: [:],
563 + id: .generateNextNanoId()
564 + )
565 +
566 + // Check execution budgets
567 + for (_, budget) in evaluation.result {
568 + if budget.cpu > 10_000_000 { // Example limit
569 + issues.append(.highCpuUsage(budget.cpu))
570 + }
571 + if budget.memory > 14_000_000 { // Example limit
572 + issues.append(.highMemoryUsage(budget.memory))
573 + }
574 + }
575 +
576 + } catch {
577 + issues.append(.evaluationFailed(error.localizedDescription))
578 + }
579 +
580 + // 2. Check transaction size
581 + if transaction.count > 16_384 { // 16KB limit example
582 + issues.append(.transactionTooLarge(transaction.count))
583 + }
584 +
585 + // 3. Get protocol parameters and validate fee
586 + do {
587 + let protocolParams = try await client.ledgerStateQuery.protocolParameters.execute()
588 + let minFee = calculateMinimumFee(for: transaction, using: protocolParams.result)
589 +
590 + // Extract actual fee from transaction (implementation depends on transaction format)
591 + let actualFee = extractFee(from: transaction)
592 +
593 + if actualFee < minFee {
594 + issues.append(.feeTooLow(required: minFee, provided: actualFee))
595 + }
596 +
597 + } catch {
598 + issues.append(.parameterCheckFailed(error.localizedDescription))
599 + }
600 +
601 + return ValidationResult(issues: issues, isValid: issues.isEmpty)
602 + }
603 +
604 + private func calculateMinimumFee(for transaction: Data, using params: ProtocolParameters) -> UInt64 {
605 + // Implement fee calculation based on protocol parameters
606 + // This is a simplified calculation - use proper fee calculation library
607 + let baseFee = params.minFeeConstant
608 + let sizeFee = UInt64(transaction.count) * params.minFeeCoefficient
609 + return baseFee + sizeFee
610 + }
611 +
612 + private func extractFee(from transaction: Data) -> UInt64 {
613 + // Extract fee from transaction CBOR
614 + // Implementation depends on transaction format
615 + return 0 // Placeholder
616 + }
617 + }
618 +
619 + struct ValidationResult {
620 + let issues: [ValidationIssue]
621 + let isValid: Bool
622 + }
623 +
624 + enum ValidationIssue {
625 + case highCpuUsage(UInt64)
626 + case highMemoryUsage(UInt64)
627 + case transactionTooLarge(Int)
628 + case feeTooLow(required: UInt64, provided: UInt64)
629 + case evaluationFailed(String)
630 + case parameterCheckFailed(String)
631 +
632 + var description: String {
633 + switch self {
634 + case .highCpuUsage(let cpu):
635 + return "High CPU usage: \(cpu) steps"
636 + case .highMemoryUsage(let memory):
637 + return "High memory usage: \(memory) units"
638 + case .transactionTooLarge(let size):
639 + return "Transaction too large: \(size) bytes"
640 + case .feeTooLow(let required, let provided):
641 + return "Fee too low: \(provided) (required: \(required))"
642 + case .evaluationFailed(let error):
643 + return "Evaluation failed: \(error)"
644 + case .parameterCheckFailed(let error):
645 + return "Parameter check failed: \(error)"
646 + }
647 + }
648 + }
649 + ```
650 |
651 | ## Complete Transaction Workflow
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:655:1-760:4
653 | ### Production-Ready Transaction Service
654 |
655 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
656 + @MainActor
657 + class ProductionTransactionService: ObservableObject {
658 + @Published var isSubmitting = false
659 + @Published var submissionStatus: SubmissionStatus = .idle
660 + @Published var lastTransactionId: String?
661 + @Published var confirmationCount = 0
662 +
663 + private let client: OgmiosClient
664 + private let validator: TransactionValidator
665 + private let submitter: ResilientTransactionSubmitter
666 + private let monitor: TransactionMonitor
667 + private let tracker: TransactionTracker
668 +
669 + enum SubmissionStatus {
670 + case idle
671 + case validating
672 + case submitting
673 + case waitingConfirmation
674 + case confirmed
675 + case failed(String)
676 + case rolledBack
677 + }
678 +
679 + init(client: OgmiosClient) {
680 + self.client = client
681 + self.validator = TransactionValidator(client: client)
682 + self.submitter = ResilientTransactionSubmitter(client: client)
683 + self.monitor = TransactionMonitor(client: client)
684 + self.tracker = TransactionTracker(client: client)
685 + }
686 +
687 + func submitTransaction(
688 + _ transaction: Data,
689 + requiredConfirmations: Int = 1
690 + ) async {
691 + isSubmitting = true
692 + submissionStatus = .validating
693 +
694 + defer {
695 + isSubmitting = false
696 + }
697 +
698 + do {
699 + // 1. Validate transaction
700 + let validation = try await validator.validateBeforeSubmission(transaction: transaction)
701 +
702 + if !validation.isValid {
703 + let issues = validation.issues.map { $0.description }.joined(separator: ", ")
704 + submissionStatus = .failed("Validation failed: \(issues)")
705 + return
706 + }
707 +
708 + // 2. Submit transaction
709 + submissionStatus = .submitting
710 +
711 + let transactionId = try await submitter.submitWithRetry(
712 + transaction: transaction
713 + ) { error, attempt in
714 + print("Submission attempt \(attempt) failed: \(error.localizedDescription)")
715 + }
716 +
717 + lastTransactionId = transactionId
718 +
719 + // 3. Track transaction
720 + await tracker.trackTransaction(transactionId)
721 +
722 + // 4. Wait for confirmation
723 + submissionStatus = .waitingConfirmation
724 +
725 + let confirmed = try await monitor.waitForConfirmation(
726 + transactionId: transactionId,
727 + requiredConfirmations: requiredConfirmations
728 + )
729 +
730 + if confirmed {
731 + submissionStatus = .confirmed
732 + confirmationCount = requiredConfirmations
733 + } else {
734 + submissionStatus = .failed("Confirmation timeout")
735 + }
736 +
737 + } catch let error as TransactionSubmissionError {
738 + submissionStatus = .failed(error.localizedDescription)
739 + } catch {
740 + submissionStatus = .failed("Unknown error: \(error.localizedDescription)")
741 + }
742 + }
743 +
744 + func checkTransactionStatus() async {
745 + guard let txId = lastTransactionId else { return }
746 +
747 + let status = await tracker.getStatus(for: txId)
748 +
749 + if let status = status {
750 + confirmationCount = status.confirmations
751 +
752 + if status.isRolledBack {
753 + submissionStatus = .rolledBack
754 + } else if status.confirmations > 0 {
755 + submissionStatus = .confirmed
756 + }
757 + }
758 + }
759 + }
760 + ```
761 |
762 | ### SwiftUI Integration
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransactionSubmission.md:764:1-878:4
762 | ### SwiftUI Integration
763 |
764 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
765 + struct TransactionSubmissionView: View {
766 + @StateObject private var transactionService: ProductionTransactionService
767 + @State private var transactionData = Data()
768 + @State private var showingDetails = false
769 +
770 + init(client: OgmiosClient) {
771 + self._transactionService = StateObject(wrappedValue: ProductionTransactionService(client: client))
772 + }
773 +
774 + var body: some View {
775 + VStack(spacing: 20) {
776 + // Status indicator
777 + statusView
778 +
779 + // Transaction ID
780 + if let txId = transactionService.lastTransactionId {
781 + VStack {
782 + Text("Transaction ID")
783 + .font(.caption)
784 + .foregroundColor(.secondary)
785 +
786 + Text(txId)
787 + .font(.system(.caption, design: .monospaced))
788 + .lineLimit(1)
789 + .truncationMode(.middle)
790 + }
791 + }
792 +
793 + // Confirmation count
794 + if transactionService.confirmationCount > 0 {
795 + HStack {
796 + Text("Confirmations:")
797 + Text("\(transactionService.confirmationCount)")
798 + .fontWeight(.semibold)
799 + }
800 + .font(.caption)
801 + }
802 +
803 + // Submit button
804 + Button("Submit Transaction") {
805 + Task {
806 + await transactionService.submitTransaction(
807 + transactionData,
808 + requiredConfirmations: 3
809 + )
810 + }
811 + }
812 + .disabled(transactionService.isSubmitting || transactionData.isEmpty)
813 + .buttonStyle(.borderedProminent)
814 +
815 + // Details button
816 + Button("View Details") {
817 + showingDetails = true
818 + }
819 + .buttonStyle(.bordered)
820 + }
821 + .padding()
822 + .sheet(isPresented: $showingDetails) {
823 + TransactionDetailsView(service: transactionService)
824 + }
825 + .task {
826 + // Periodically check transaction status
827 + while !Task.isCancelled {
828 + await transactionService.checkTransactionStatus()
829 + try? await Task.sleep(nanoseconds: 30_000_000_000) // 30 seconds
830 + }
831 + }
832 + }
833 +
834 + @ViewBuilder
835 + private var statusView: some View {
836 + HStack {
837 + Circle()
838 + .fill(statusColor)
839 + .frame(width: 12, height: 12)
840 +
841 + Text(statusText)
842 + .font(.headline)
843 + }
844 + }
845 +
846 + private var statusColor: Color {
847 + switch transactionService.submissionStatus {
848 + case .idle:
849 + return .gray
850 + case .validating, .submitting, .waitingConfirmation:
851 + return .orange
852 + case .confirmed:
853 + return .green
854 + case .failed, .rolledBack:
855 + return .red
856 + }
857 + }
858 +
859 + private var statusText: String {
860 + switch transactionService.submissionStatus {
861 + case .idle:
862 + return "Ready"
863 + case .validating:
864 + return "Validating..."
865 + case .submitting:
866 + return "Submitting..."
867 + case .waitingConfirmation:
868 + return "Waiting for Confirmation"
869 + case .confirmed:
870 + return "Confirmed"
871 + case .failed(let error):
872 + return "Failed: \(error)"
873 + case .rolledBack:
874 + return "Rolled Back"
875 + }
876 + }
877 + }
878 + ```
879 |
880 | ## Best Practices
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransactionSubmission.md:893:3-893:58
891 | ## See Also
892 |
893 + - <doc:GettingStarted> - Basic setup and first operations
| ╰─suggestion: Remove extraneous content
894 | - <doc:ErrorHandling> - Comprehensive error handling strategies
895 | - <doc:LedgerStateQueries> - Querying protocol parameters and UTxOs
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransactionSubmission.md:894:3-894:64
892 |
893 | - <doc:GettingStarted> - Basic setup and first operations
894 + - <doc:ErrorHandling> - Comprehensive error handling strategies
| ╰─suggestion: Remove extraneous content
895 | - <doc:LedgerStateQueries> - Querying protocol parameters and UTxOs
896 | - <doc:ChainSynchronization> - Monitoring transaction confirmations
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransactionSubmission.md:895:3-895:68
893 | - <doc:GettingStarted> - Basic setup and first operations
894 | - <doc:ErrorHandling> - Comprehensive error handling strategies
895 + - <doc:LedgerStateQueries> - Querying protocol parameters and UTxOs
| ╰─suggestion: Remove extraneous content
896 | - <doc:ChainSynchronization> - Monitoring transaction confirmations
897 | - <doc:MempoolMonitoring> - Tracking transactions in mempool
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransactionSubmission.md:896:3-896:68
894 | - <doc:ErrorHandling> - Comprehensive error handling strategies
895 | - <doc:LedgerStateQueries> - Querying protocol parameters and UTxOs
896 + - <doc:ChainSynchronization> - Monitoring transaction confirmations
| ╰─suggestion: Remove extraneous content
897 | - <doc:MempoolMonitoring> - Tracking transactions in mempool
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransactionSubmission.md:897:3-897:61
895 | - <doc:LedgerStateQueries> - Querying protocol parameters and UTxOs
896 | - <doc:ChainSynchronization> - Monitoring transaction confirmations
897 + - <doc:MempoolMonitoring> - Tracking transactions in mempool
| ╰─suggestion: Remove extraneous content
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:37:1-54:4
35 | ### Basic HTTP Usage
36 |
37 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
38 + import SwiftOgmios
39 +
40 + // Create HTTP client
41 + let httpClient = try await OgmiosClient(
42 + host: "localhost",
43 + port: 1337,
44 + secure: false, // Use HTTP
45 + httpOnly: true // Force HTTP transport
46 + )
47 +
48 + // Perform simple queries
49 + let epochResponse = try await httpClient.ledgerStateQuery.epoch.execute()
50 + print("Current epoch: \(epochResponse.result)")
51 +
52 + // HTTP is automatically closed after each request
53 + // No need to manage persistent connections
54 + ```
55 |
56 | ### HTTPS for Production
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:58:1-70:4
56 | ### HTTPS for Production
57 |
58 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
59 + // Production HTTPS client
60 + let httpsClient = try await OgmiosClient(
61 + host: "your-ogmios-server.com",
62 + port: 443,
63 + secure: true, // Use HTTPS
64 + httpOnly: true // Force HTTPS transport
65 + )
66 +
67 + // All communication is encrypted via TLS
68 + let tipResponse = try await httpsClient.ledgerStateQuery.tip.execute()
69 + print("Secure tip query: \(tipResponse.result)")
70 + ```
71 |
72 | ### HTTP Performance Optimization
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:74:1-116:4
72 | ### HTTP Performance Optimization
73 |
74 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
75 + class OptimizedHTTPClient {
76 + private let client: OgmiosClient
77 + private let session: URLSession
78 +
79 + init(host: String, port: Int, secure: Bool = true) async throws {
80 + // Configure optimized URLSession
81 + let config = URLSessionConfiguration.default
82 + config.httpMaximumConnectionsPerHost = 6
83 + config.timeoutIntervalForRequest = 30
84 + config.timeoutIntervalForResource = 300
85 + config.requestCachePolicy = .useProtocolCachePolicy
86 +
87 + // Enable HTTP/2 and connection reuse
88 + config.httpShouldUsePipelining = true
89 + config.urlCache = URLCache(memoryCapacity: 4 * 1024 * 1024, // 4MB
90 + diskCapacity: 20 * 1024 * 1024) // 20MB
91 +
92 + self.session = URLSession(configuration: config)
93 + self.client = try await OgmiosClient(
94 + host: host,
95 + port: port,
96 + secure: secure,
97 + httpOnly: true,
98 + configuration: config
99 + )
100 + }
101 +
102 + func batchQueries() async throws {
103 + // Execute multiple queries concurrently
104 + async let epochTask = client.ledgerStateQuery.epoch.execute()
105 + async let tipTask = client.ledgerStateQuery.tip.execute()
106 + async let poolsTask = client.ledgerStateQuery.stakePools.execute()
107 +
108 + let (epochResponse, tipResponse, poolsResponse) = try await (epochTask, tipTask, poolsTask)
109 +
110 + print("Batch results:")
111 + print(" Epoch: \(epochResponse.result)")
112 + print(" Tip: \(tipResponse.result.slot)")
113 + print(" Pools: \(poolsResponse.result.count)")
114 + }
115 + }
116 + ```
117 |
118 | ## WebSocket Transport
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:131:1-149:4
129 | ### Basic WebSocket Usage
130 |
131 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
132 + // Create WebSocket client
133 + let wsClient = try await OgmiosClient(
134 + host: "localhost",
135 + port: 1337,
136 + secure: false, // Use WS
137 + httpOnly: false // Use WebSocket transport
138 + )
139 +
140 + // Connection remains open for multiple requests
141 + let epochResponse = try await wsClient.ledgerStateQuery.epoch.execute()
142 + let tipResponse = try await wsClient.ledgerStateQuery.tip.execute()
143 +
144 + // WebSocket connection is reused automatically
145 + print("Epoch: \(epochResponse.result)")
146 + print("Tip: \(tipResponse.result.slot)")
147 +
148 + // Remember to clean up when done (handled automatically by deinit)
149 + ```
150 |
151 | ### WSS for Production
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:153:1-163:4
151 | ### WSS for Production
152 |
153 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
154 + // Production WSS client with enhanced security
155 + let wssClient = try await OgmiosClient(
156 + host: "secure-ogmios-server.com",
157 + port: 443,
158 + secure: true, // Use WSS (WebSocket Secure)
159 + httpOnly: false
160 + )
161 +
162 + // All WebSocket communication is encrypted via TLS
163 + ```
164 |
165 | ### WebSocket Connection Management
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:167:1-307:4
165 | ### WebSocket Connection Management
166 |
167 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
168 + actor WebSocketManager {
169 + private var client: OgmiosClient?
170 + private let config: ConnectionConfig
171 + private var reconnectAttempts = 0
172 + private let maxReconnectAttempts = 5
173 + private var heartbeatTask: Task<Void, Never>?
174 +
175 + struct ConnectionConfig {
176 + let host: String
177 + let port: Int
178 + let secure: Bool
179 + let heartbeatInterval: TimeInterval = 30.0
180 + let reconnectDelay: TimeInterval = 5.0
181 + }
182 +
183 + init(config: ConnectionConfig) {
184 + self.config = config
185 + }
186 +
187 + func connect() async throws {
188 + guard client == nil else { return }
189 +
190 + do {
191 + client = try await OgmiosClient(
192 + host: config.host,
193 + port: config.port,
194 + secure: config.secure,
195 + httpOnly: false
196 + )
197 +
198 + reconnectAttempts = 0
199 + startHeartbeat()
200 + print("WebSocket connected successfully")
201 +
202 + } catch {
203 + print("Connection failed: \(error)")
204 + await scheduleReconnect()
205 + throw error
206 + }
207 + }
208 +
209 + func disconnect() {
210 + heartbeatTask?.cancel()
211 + client = nil
212 + print("WebSocket disconnected")
213 + }
214 +
215 + func execute<T>(_ operation: (OgmiosClient) async throws -> T) async throws -> T {
216 + guard let client = client else {
217 + throw TransportError.notConnected
218 + }
219 +
220 + do {
221 + return try await operation(client)
222 + } catch {
223 + print("Operation failed: \(error)")
224 +
225 + // Check if we need to reconnect
226 + if shouldReconnectOnError(error) {
227 + await scheduleReconnect()
228 + }
229 +
230 + throw error
231 + }
232 + }
233 +
234 + private func startHeartbeat() {
235 + heartbeatTask = Task { [weak self] in
236 + while !Task.isCancelled {
237 + do {
238 + try await Task.sleep(nanoseconds: UInt64(config.heartbeatInterval * 1_000_000_000))
239 + await self?.performHeartbeat()
240 + } catch {
241 + break
242 + }
243 + }
244 + }
245 + }
246 +
247 + private func performHeartbeat() async {
248 + do {
249 + _ = try await client?.ledgerStateQuery.tip.execute()
250 + } catch {
251 + print("Heartbeat failed: \(error)")
252 + await scheduleReconnect()
253 + }
254 + }
255 +
256 + private func scheduleReconnect() async {
257 + guard reconnectAttempts < maxReconnectAttempts else {
258 + print("Max reconnect attempts exceeded")
259 + return
260 + }
261 +
262 + reconnectAttempts += 1
263 + disconnect()
264 +
265 + print("Reconnecting in \(config.reconnectDelay) seconds (attempt \(reconnectAttempts))")
266 +
267 + try? await Task.sleep(nanoseconds: UInt64(config.reconnectDelay * 1_000_000_000))
268 +
269 + do {
270 + try await connect()
271 + } catch {
272 + print("Reconnect failed: \(error)")
273 + }
274 + }
275 +
276 + private func shouldReconnectOnError(_ error: Error) -> Bool {
277 + switch error {
278 + case OgmiosError.websocketError(_),
279 + OgmiosError.httpError(_):
280 + return true
281 + default:
282 + return false
283 + }
284 + }
285 + }
286 +
287 + enum TransportError: Error {
288 + case notConnected
289 + case connectionLost
290 + case reconnectFailed
291 + }
292 +
293 + // Usage
294 + let manager = WebSocketManager(config: WebSocketManager.ConnectionConfig(
295 + host: "localhost",
296 + port: 1337,
297 + secure: false
298 + ))
299 +
300 + try await manager.connect()
301 +
302 + let epoch = try await manager.execute { client in
303 + return try await client.ledgerStateQuery.epoch.execute()
304 + }
305 +
306 + print("Current epoch: \(epoch.result)")
307 + ```
308 |
309 | ## Security Considerations
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:313:1-369:4
311 | ### TLS Configuration
312 |
313 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
314 + class SecureTransportClient {
315 + static func createSecureClient(
316 + host: String,
317 + port: Int,
318 + certificatePinning: Bool = false
319 + ) async throws -> OgmiosClient {
320 +
321 + let config = URLSessionConfiguration.default
322 +
323 + // Enhanced security settings
324 + config.tlsMinimumSupportedProtocolVersion = .TLSv12
325 + config.tlsMaximumSupportedProtocolVersion = .TLSv13
326 +
327 + if certificatePinning {
328 + // Implement certificate pinning for enhanced security
329 + config.urlSessionDidReceiveChallenge = { session, challenge in
330 + return await handleCertificateChallenge(challenge)
331 + }
332 + }
333 +
334 + // Disable insecure connections
335 + config.httpShouldUsePipelining = false
336 + config.httpCookieAcceptPolicy = .never
337 +
338 + return try await OgmiosClient(
339 + host: host,
340 + port: port,
341 + secure: true, // Always use TLS in production
342 + httpOnly: false,
343 + configuration: config
344 + )
345 + }
346 +
347 + private static func handleCertificateChallenge(
348 + _ challenge: URLAuthenticationChallenge
349 + ) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
350 +
351 + // Implement certificate pinning logic
352 + guard let serverTrust = challenge.protectionSpace.serverTrust else {
353 + return (.cancelAuthenticationChallenge, nil)
354 + }
355 +
356 + // Validate certificate against pinned certificates
357 + // This is a simplified example - implement proper certificate validation
358 + let credential = URLCredential(trust: serverTrust)
359 + return (.useCredential, credential)
360 + }
361 + }
362 +
363 + // Usage
364 + let secureClient = try await SecureTransportClient.createSecureClient(
365 + host: "production-ogmios.example.com",
366 + port: 443,
367 + certificatePinning: true
368 + )
369 + ```
370 |
371 | ### Authentication and Authorization
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:373:1-425:4
371 | ### Authentication and Authorization
372 |
373 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
374 + class AuthenticatedOgmiosClient {
375 + private let baseClient: OgmiosClient
376 + private let apiKey: String
377 +
378 + init(host: String, port: Int, apiKey: String) async throws {
379 + self.apiKey = apiKey
380 +
381 + let config = URLSessionConfiguration.default
382 + config.httpAdditionalHeaders = [
383 + "Authorization": "Bearer \(apiKey)",
384 + "User-Agent": "SwiftOgmios/1.0.0",
385 + "Accept": "application/json"
386 + ]
387 +
388 + self.baseClient = try await OgmiosClient(
389 + host: host,
390 + port: port,
391 + secure: true,
392 + httpOnly: true,
393 + configuration: config
394 + )
395 + }
396 +
397 + func authenticatedQuery<T: JSONRPCResponse>(
398 + _ operation: (OgmiosClient) async throws -> T
399 + ) async throws -> T {
400 + do {
401 + return try await operation(baseClient)
402 + } catch OgmiosError.httpError(let message) where message.contains("401") {
403 + throw AuthenticationError.invalidCredentials
404 + } catch OgmiosError.httpError(let message) where message.contains("403") {
405 + throw AuthenticationError.accessDenied
406 + } catch {
407 + throw error
408 + }
409 + }
410 + }
411 +
412 + enum AuthenticationError: Error, LocalizedError {
413 + case invalidCredentials
414 + case accessDenied
415 +
416 + var errorDescription: String? {
417 + switch self {
418 + case .invalidCredentials:
419 + return "Invalid API credentials"
420 + case .accessDenied:
421 + return "Access denied - insufficient permissions"
422 + }
423 + }
424 + }
425 + ```
426 |
427 | ## Mobile Optimization
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:431:1-558:4
429 | ### Battery-Efficient Patterns
430 |
431 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
432 + @MainActor
433 + class MobileOptimizedClient: ObservableObject {
434 + @Published var connectionState: ConnectionState = .disconnected
435 + @Published var isBackgroundMode = false
436 +
437 + private var client: OgmiosClient?
438 + private let config: MobileConfig
439 + private var backgroundTask: UIBackgroundTaskIdentifier = .invalid
440 +
441 + enum ConnectionState {
442 + case disconnected
443 + case connecting
444 + case connected
445 + case backgrounded
446 + }
447 +
448 + struct MobileConfig {
449 + let useHTTPInBackground: Bool = true
450 + let backgroundSyncInterval: TimeInterval = 300 // 5 minutes
451 + let foregroundSyncInterval: TimeInterval = 30 // 30 seconds
452 + let batteryOptimizationEnabled: Bool = true
453 + }
454 +
455 + init(config: MobileConfig = MobileConfig()) {
456 + self.config = config
457 + setupAppLifecycleObservers()
458 + }
459 +
460 + private func setupAppLifecycleObservers() {
461 + NotificationCenter.default.addObserver(
462 + self,
463 + selector: #selector(appDidEnterBackground),
464 + name: UIApplication.didEnterBackgroundNotification,
465 + object: nil
466 + )
467 +
468 + NotificationCenter.default.addObserver(
469 + self,
470 + selector: #selector(appWillEnterForeground),
471 + name: UIApplication.willEnterForegroundNotification,
472 + object: nil
473 + )
474 + }
475 +
476 + @objc private func appDidEnterBackground() {
477 + isBackgroundMode = true
478 +
479 + if config.useHTTPInBackground {
480 + // Switch to HTTP for background mode
481 + Task {
482 + await switchToBackgroundMode()
483 + }
484 + }
485 + }
486 +
487 + @objc private func appWillEnterForeground() {
488 + isBackgroundMode = false
489 + endBackgroundTask()
490 +
491 + Task {
492 + await switchToForegroundMode()
493 + }
494 + }
495 +
496 + private func switchToBackgroundMode() async {
497 + beginBackgroundTask()
498 +
499 + // Close WebSocket connections to save battery
500 + client = nil
501 + connectionState = .backgrounded
502 +
503 + // Create HTTP client for occasional background updates
504 + do {
505 + client = try await OgmiosClient(
506 + host: "your-server.com",
507 + port: 443,
508 + secure: true,
509 + httpOnly: true
510 + )
511 + } catch {
512 + print("Failed to create background HTTP client: \(error)")
513 + }
514 + }
515 +
516 + private func switchToForegroundMode() async {
517 + connectionState = .connecting
518 +
519 + do {
520 + // Recreate WebSocket client for real-time updates
521 + client = try await OgmiosClient(
522 + host: "your-server.com",
523 + port: 443,
524 + secure: true,
525 + httpOnly: false
526 + )
527 +
528 + connectionState = .connected
529 + } catch {
530 + print("Failed to create foreground WebSocket client: \(error)")
531 + connectionState = .disconnected
532 + }
533 + }
534 +
535 + private func beginBackgroundTask() {
536 + backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
537 + self?.endBackgroundTask()
538 + }
539 + }
540 +
541 + private func endBackgroundTask() {
542 + if backgroundTask != .invalid {
543 + UIApplication.shared.endBackgroundTask(backgroundTask)
544 + backgroundTask = .invalid
545 + }
546 + }
547 +
548 + func performOptimizedQuery<T>(
549 + _ operation: (OgmiosClient) async throws -> T
550 + ) async throws -> T {
551 + guard let client = client else {
552 + throw TransportError.notConnected
553 + }
554 +
555 + return try await operation(client)
556 + }
557 + }
558 + ```
559 |
560 | ### Network Change Handling
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:562:1-657:4
560 | ### Network Change Handling
561 |
562 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
563 + import Network
564 +
565 + class NetworkAwareClient: ObservableObject {
566 + @Published var networkStatus: NetworkStatus = .unknown
567 + @Published var connectionQuality: ConnectionQuality = .unknown
568 +
569 + private let monitor = NWPathMonitor()
570 + private let queue = DispatchQueue(label: "NetworkMonitor")
571 + private var client: OgmiosClient?
572 +
573 + enum NetworkStatus {
574 + case unknown
575 + case unavailable
576 + case wifi
577 + case cellular
578 + case ethernet
579 + }
580 +
581 + enum ConnectionQuality {
582 + case unknown
583 + case poor
584 + case moderate
585 + case good
586 + case excellent
587 + }
588 +
589 + init() {
590 + startNetworkMonitoring()
591 + }
592 +
593 + private func startNetworkMonitoring() {
594 + monitor.pathUpdateHandler = { [weak self] path in
595 + Task { @MainActor in
596 + self?.handleNetworkChange(path)
597 + }
598 + }
599 + monitor.start(queue: queue)
600 + }
601 +
602 + @MainActor
603 + private func handleNetworkChange(_ path: NWPath) {
604 + // Update network status
605 + if path.status == .satisfied {
606 + if path.usesInterfaceType(.wifi) {
607 + networkStatus = .wifi
608 + connectionQuality = .excellent
609 + } else if path.usesInterfaceType(.cellular) {
610 + networkStatus = .cellular
611 + connectionQuality = assessCellularQuality(path)
612 + } else if path.usesInterfaceType(.wiredEthernet) {
613 + networkStatus = .ethernet
614 + connectionQuality = .excellent
615 + }
616 + } else {
617 + networkStatus = .unavailable
618 + connectionQuality = .unknown
619 + }
620 +
621 + // Adapt transport based on network quality
622 + Task {
623 + await adaptTransportToNetwork()
624 + }
625 + }
626 +
627 + private func assessCellularQuality(_ path: NWPath) -> ConnectionQuality {
628 + // Simplified cellular quality assessment
629 + if path.isExpensive {
630 + return .moderate // Cellular data
631 + }
632 + return .good
633 + }
634 +
635 + private func adaptTransportToNetwork() async {
636 + let shouldUseHTTP = networkStatus == .cellular || connectionQuality == .poor
637 +
638 + do {
639 + client = try await OgmiosClient(
640 + host: "your-server.com",
641 + port: 443,
642 + secure: true,
643 + httpOnly: shouldUseHTTP
644 + )
645 +
646 + print("Adapted transport: \(shouldUseHTTP ? "HTTP" : "WebSocket") for \(networkStatus)")
647 +
648 + } catch {
649 + print("Failed to adapt transport: \(error)")
650 + }
651 + }
652 +
653 + deinit {
654 + monitor.cancel()
655 + }
656 + }
657 + ```
658 |
659 | ## Performance Benchmarking
warning: Unknown option 'swift path' in code block.
--> Tutorials/TransportTypes.md:663:1-821:4
661 | ### Transport Performance Comparison
662 |
663 + ```swift path=null start=null
| ╰─suggestion: If 'swift path' is the language for this code block, then write 'swift path' as the first option.
664 + class TransportBenchmark {
665 + struct BenchmarkResult {
666 + let transportType: String
667 + let operationsPerSecond: Double
668 + let averageLatency: TimeInterval
669 + let totalTime: TimeInterval
670 + let successRate: Double
671 + }
672 +
673 + static func benchmarkTransports(
674 + host: String,
675 + port: Int,
676 + operations: Int = 100
677 + ) async throws -> [BenchmarkResult] {
678 +
679 + var results: [BenchmarkResult] = []
680 +
681 + // Benchmark HTTP
682 + let httpResult = try await benchmarkHTTP(
683 + host: host,
684 + port: port,
685 + operations: operations
686 + )
687 + results.append(httpResult)
688 +
689 + // Benchmark WebSocket
690 + let wsResult = try await benchmarkWebSocket(
691 + host: host,
692 + port: port,
693 + operations: operations
694 + )
695 + results.append(wsResult)
696 +
697 + return results
698 + }
699 +
700 + private static func benchmarkHTTP(
701 + host: String,
702 + port: Int,
703 + operations: Int
704 + ) async throws -> BenchmarkResult {
705 +
706 + let client = try await OgmiosClient(
707 + host: host,
708 + port: port,
709 + secure: false,
710 + httpOnly: true
711 + )
712 +
713 + let startTime = Date()
714 + var latencies: [TimeInterval] = []
715 + var successes = 0
716 +
717 + for _ in 0..<operations {
718 + let operationStart = Date()
719 +
720 + do {
721 + _ = try await client.ledgerStateQuery.tip.execute()
722 + let latency = Date().timeIntervalSince(operationStart)
723 + latencies.append(latency)
724 + successes += 1
725 + } catch {
726 + print("HTTP operation failed: \(error)")
727 + }
728 + }
729 +
730 + let totalTime = Date().timeIntervalSince(startTime)
731 + let averageLatency = latencies.reduce(0, +) / Double(latencies.count)
732 + let opsPerSecond = Double(successes) / totalTime
733 + let successRate = Double(successes) / Double(operations)
734 +
735 + return BenchmarkResult(
736 + transportType: "HTTP",
737 + operationsPerSecond: opsPerSecond,
738 + averageLatency: averageLatency,
739 + totalTime: totalTime,
740 + successRate: successRate
741 + )
742 + }
743 +
744 + private static func benchmarkWebSocket(
745 + host: String,
746 + port: Int,
747 + operations: Int
748 + ) async throws -> BenchmarkResult {
749 +
750 + let client = try await OgmiosClient(
751 + host: host,
752 + port: port,
753 + secure: false,
754 + httpOnly: false
755 + )
756 +
757 + let startTime = Date()
758 + var latencies: [TimeInterval] = []
759 + var successes = 0
760 +
761 + for _ in 0..<operations {
762 + let operationStart = Date()
763 +
764 + do {
765 + _ = try await client.ledgerStateQuery.tip.execute()
766 + let latency = Date().timeIntervalSince(operationStart)
767 + latencies.append(latency)
768 + successes += 1
769 + } catch {
770 + print("WebSocket operation failed: \(error)")
771 + }
772 + }
773 +
774 + let totalTime = Date().timeIntervalSince(startTime)
775 + let averageLatency = latencies.reduce(0, +) / Double(latencies.count)
776 + let opsPerSecond = Double(successes) / totalTime
777 + let successRate = Double(successes) / Double(operations)
778 +
779 + return BenchmarkResult(
780 + transportType: "WebSocket",
781 + operationsPerSecond: opsPerSecond,
782 + averageLatency: averageLatency,
783 + totalTime: totalTime,
784 + successRate: successRate
785 + )
786 + }
787 +
788 + static func printBenchmarkResults(_ results: [BenchmarkResult]) {
789 + print("\n📊 Transport Benchmark Results:")
790 + print("=" * 50)
791 +
792 + for result in results {
793 + print("\n\(result.transportType):")
794 + print(" Operations/sec: \(String(format: "%.2f", result.operationsPerSecond))")
795 + print(" Avg latency: \(String(format: "%.3f", result.averageLatency))s")
796 + print(" Total time: \(String(format: "%.2f", result.totalTime))s")
797 + print(" Success rate: \(String(format: "%.1f", result.successRate * 100))%")
798 + }
799 +
800 + if results.count >= 2 {
801 + let httpResult = results.first { $0.transportType == "HTTP" }!
802 + let wsResult = results.first { $0.transportType == "WebSocket" }!
803 +
804 + let speedupFactor = wsResult.operationsPerSecond / httpResult.operationsPerSecond
805 + let latencyImprovement = (httpResult.averageLatency - wsResult.averageLatency) / httpResult.averageLatency * 100
806 +
807 + print("\n🚀 Performance Comparison:")
808 + print(" WebSocket is \(String(format: "%.2f", speedupFactor))x faster")
809 + print(" WebSocket has \(String(format: "%.1f", latencyImprovement))% lower latency")
810 + }
811 + }
812 + }
813 +
814 + // Usage
815 + let results = try await TransportBenchmark.benchmarkTransports(
816 + host: "localhost",
817 + port: 1337,
818 + operations: 50
819 + )
820 + TransportBenchmark.printBenchmarkResults(results)
821 + ```
822 |
823 | ## Best Practices Summary
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransportTypes.md:863:3-863:61
861 | ## See Also
862 |
863 + - <doc:GettingStarted> - Basic setup and transport selection
| ╰─suggestion: Remove extraneous content
864 | - <doc:ErrorHandling> - Transport-specific error handling strategies
865 | - <doc:ChainSynchronization> - WebSocket usage for real-time block streaming
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransportTypes.md:864:3-864:69
862 |
863 | - <doc:GettingStarted> - Basic setup and transport selection
864 + - <doc:ErrorHandling> - Transport-specific error handling strategies
| ╰─suggestion: Remove extraneous content
865 | - <doc:ChainSynchronization> - WebSocket usage for real-time block streaming
866 | - <doc:MempoolMonitoring> - Transport considerations for mempool monitoring
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransportTypes.md:865:3-865:77
863 | - <doc:GettingStarted> - Basic setup and transport selection
864 | - <doc:ErrorHandling> - Transport-specific error handling strategies
865 + - <doc:ChainSynchronization> - WebSocket usage for real-time block streaming
| ╰─suggestion: Remove extraneous content
866 | - <doc:MempoolMonitoring> - Transport considerations for mempool monitoring
867 | - <doc:AdvancedUsage> - Advanced transport patterns and optimization techniques
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransportTypes.md:866:3-866:76
864 | - <doc:ErrorHandling> - Transport-specific error handling strategies
865 | - <doc:ChainSynchronization> - WebSocket usage for real-time block streaming
866 + - <doc:MempoolMonitoring> - Transport considerations for mempool monitoring
| ╰─suggestion: Remove extraneous content
867 | - <doc:AdvancedUsage> - Advanced transport patterns and optimization techniques
warning: Extraneous content found after a link in task group list item
--> Tutorials/TransportTypes.md:867:3-867:80
865 | - <doc:ChainSynchronization> - WebSocket usage for real-time block streaming
866 | - <doc:MempoolMonitoring> - Transport considerations for mempool monitoring
867 + - <doc:AdvancedUsage> - Advanced transport patterns and optimization techniques
| ╰─suggestion: Remove extraneous content
Finished building documentation for 'SwiftOgmios' (4.69s)
Generated documentation archive at:
/Users/admin/builder/spi-builder-workspace/.docs/kingpin-apps/swift-ogmios/0.3.2
Updating https://github.com/Frizlab/swift-fraction-number.git
Updating https://github.com/norio-nomura/Base32.git
Updating https://github.com/swift-server/swift-service-lifecycle.git
Updating https://github.com/apple/swift-nio-ssl.git
Updating https://github.com/apple/swift-http-structured-headers.git
Updating https://github.com/apple/swift-crypto.git
Updating https://github.com/Kingpin-Apps/swift-cbor-codable.git
Updated https://github.com/Frizlab/swift-fraction-number.git (0.58s)
Updating https://github.com/apple/swift-atomics.git
Updated https://github.com/apple/swift-http-structured-headers.git (0.77s)
Updated https://github.com/norio-nomura/Base32.git (0.77s)
Updated https://github.com/Kingpin-Apps/swift-cbor-codable.git (0.77s)
Updated https://github.com/apple/swift-nio-ssl.git (0.77s)
Updated https://github.com/swift-server/swift-service-lifecycle.git (0.77s)
Updated https://github.com/apple/swift-crypto.git (0.77s)
Updating https://github.com/Kingpin-Apps/swift-cardano-core.git
Updating https://github.com/apple/swift-system.git
Updating https://github.com/apple/swift-nio.git
Updating https://github.com/attaswift/BigInt.git
Updating https://github.com/mxcl/Version.git
Updating https://github.com/apple/swift-http-types.git
Updated https://github.com/apple/swift-atomics.git (0.51s)
Updating https://github.com/apple/swift-nio-http2.git
Updated https://github.com/Kingpin-Apps/swift-cardano-core.git (0.56s)
Updating https://github.com/krzyzanowskim/CryptoSwift.git
Updated https://github.com/apple/swift-nio.git (0.89s)
Updated https://github.com/mxcl/Version.git (0.89s)
Updated https://github.com/attaswift/BigInt.git (0.89s)
Updated https://github.com/apple/swift-system.git (0.89s)
Updated https://github.com/apple/swift-http-types.git (0.89s)
Updating https://github.com/Kingpin-Apps/swift-nacl.git
Updating https://github.com/Kingpin-Apps/swift-base58.git
Updating https://github.com/apple/swift-numerics
Updating https://github.com/apple/swift-nio-transport-services.git
Updating https://github.com/Kingpin-Apps/swift-mnemonic.git
Updated https://github.com/apple/swift-nio-http2.git (0.63s)
Updating https://github.com/apple/swift-collections.git
Updated https://github.com/krzyzanowskim/CryptoSwift.git (0.62s)
Updating https://github.com/apple/swift-log.git
Updated https://github.com/Kingpin-Apps/swift-base58.git (0.59s)
Updated https://github.com/Kingpin-Apps/swift-nacl.git (0.59s)
Updated https://github.com/apple/swift-nio-transport-services.git (0.60s)
Updated https://github.com/Kingpin-Apps/swift-mnemonic.git (0.60s)
Updated https://github.com/apple/swift-numerics (0.61s)
Updating https://github.com/apple/swift-async-algorithms.git
Updating https://github.com/apple/swift-nio-extras.git
Updating https://github.com/SusanDoggie/Float16.git
Updating https://github.com/Kingpin-Apps/swift-kes.git
Updated https://github.com/apple/swift-collections.git (0.64s)
Updating https://github.com/apple/swift-asn1.git
Updated https://github.com/apple/swift-log.git (0.62s)
Updating https://github.com/vapor/websocket-kit.git
Updated https://github.com/SusanDoggie/Float16.git (0.53s)
Updated https://github.com/Kingpin-Apps/swift-kes.git (0.54s)
Fetching https://github.com/swiftlang/swift-docc-plugin
Updating https://github.com/apple/swift-certificates.git
Updated https://github.com/vapor/websocket-kit.git (0.57s)
Updated https://github.com/apple/swift-async-algorithms.git (0.90s)
Updated https://github.com/apple/swift-nio-extras.git (0.90s)
Updating https://github.com/apple/swift-algorithms.git
Updated https://github.com/apple/swift-asn1.git (0.80s)
Updated https://github.com/apple/swift-certificates.git (0.62s)
[1/2345] Fetching swift-docc-plugin
Updated https://github.com/apple/swift-algorithms.git (0.60s)
Fetched https://github.com/swiftlang/swift-docc-plugin from cache (1.40s)
Computing version for https://github.com/vapor/websocket-kit.git
Computed https://github.com/vapor/websocket-kit.git at 2.16.2 (4.90s)
Computing version for https://github.com/Kingpin-Apps/swift-cardano-core.git
Computed https://github.com/Kingpin-Apps/swift-cardano-core.git at 0.5.3 (0.54s)
Computing version for https://github.com/apple/swift-atomics.git
Computed https://github.com/apple/swift-atomics.git at 1.3.1 (0.53s)
Computing version for https://github.com/apple/swift-nio-transport-services.git
Computed https://github.com/apple/swift-nio-transport-services.git at 1.28.0 (0.54s)
Computing version for https://github.com/apple/swift-nio-ssl.git
Computed https://github.com/apple/swift-nio-ssl.git at 2.37.5 (0.58s)
Computing version for https://github.com/apple/swift-nio-extras.git
Computed https://github.com/apple/swift-nio-extras.git at 1.35.1 (0.71s)
Computing version for https://github.com/apple/swift-nio.git
Computed https://github.com/apple/swift-nio.git at 2.103.0 (0.66s)
Computing version for https://github.com/mxcl/Version.git
Computed https://github.com/mxcl/Version.git at 2.2.1 (0.62s)
Computing version for https://github.com/krzyzanowskim/CryptoSwift.git
Computed https://github.com/krzyzanowskim/CryptoSwift.git at 1.10.0 (0.62s)
Computing version for https://github.com/Kingpin-Apps/swift-nacl.git
Computed https://github.com/Kingpin-Apps/swift-nacl.git at 1.0.3 (0.62s)
Computing version for https://github.com/Kingpin-Apps/swift-mnemonic.git
Computed https://github.com/Kingpin-Apps/swift-mnemonic.git at 0.2.5 (0.53s)
Computing version for https://github.com/Kingpin-Apps/swift-kes.git
Computed https://github.com/Kingpin-Apps/swift-kes.git at 1.0.2 (0.53s)
Computing version for https://github.com/Kingpin-Apps/swift-cbor-codable.git
Computed https://github.com/Kingpin-Apps/swift-cbor-codable.git at 0.3.3 (0.53s)
Computing version for https://github.com/Kingpin-Apps/swift-base58.git
Computed https://github.com/Kingpin-Apps/swift-base58.git at 0.1.5 (0.53s)
Computing version for https://github.com/Frizlab/swift-fraction-number.git
Computed https://github.com/Frizlab/swift-fraction-number.git at 0.2.0 (0.52s)
Computing version for https://github.com/attaswift/BigInt.git
Computed https://github.com/attaswift/BigInt.git at 6.0.1 (0.52s)
Computing version for https://github.com/apple/swift-log.git
Computed https://github.com/apple/swift-log.git at 1.15.1 (0.55s)
Computing version for https://github.com/apple/swift-crypto.git
Computed https://github.com/apple/swift-crypto.git at 4.5.2 (0.73s)
Computing version for https://github.com/apple/swift-async-algorithms.git
Computed https://github.com/apple/swift-async-algorithms.git at 1.1.5 (0.59s)
Computing version for https://github.com/swift-server/swift-service-lifecycle.git
Computed https://github.com/swift-server/swift-service-lifecycle.git at 2.12.0 (0.52s)
Computing version for https://github.com/apple/swift-asn1.git
Computed https://github.com/apple/swift-asn1.git at 1.7.3 (0.62s)
Computing version for https://github.com/apple/swift-certificates.git
Computed https://github.com/apple/swift-certificates.git at 1.20.0 (0.65s)
Computing version for https://github.com/apple/swift-algorithms.git
Computed https://github.com/apple/swift-algorithms.git at 1.2.1 (0.60s)
Computing version for https://github.com/apple/swift-http-structured-headers.git
Computed https://github.com/apple/swift-http-structured-headers.git at 1.7.0 (0.52s)
Computing version for https://github.com/apple/swift-http-types.git
Computed https://github.com/apple/swift-http-types.git at 1.8.0 (0.55s)
Computing version for https://github.com/apple/swift-nio-http2.git
Computed https://github.com/apple/swift-nio-http2.git at 1.46.0 (0.53s)
Computing version for https://github.com/apple/swift-system.git
Computed https://github.com/apple/swift-system.git at 1.8.1 (0.53s)
Computing version for https://github.com/apple/swift-collections.git
Computed https://github.com/apple/swift-collections.git at 1.6.0 (0.60s)
Computing version for https://github.com/norio-nomura/Base32.git
Computed https://github.com/norio-nomura/Base32.git at 0.9.0 (0.60s)
Computing version for https://github.com/SusanDoggie/Float16.git
Computed https://github.com/SusanDoggie/Float16.git at 1.1.1 (0.59s)
Computing version for https://github.com/apple/swift-numerics.git
Computed https://github.com/apple/swift-numerics.git at 1.1.1 (0.51s)
Computing version for https://github.com/swiftlang/swift-docc-plugin
Computed https://github.com/swiftlang/swift-docc-plugin at 1.5.0 (0.62s)
Fetching https://github.com/swiftlang/swift-docc-symbolkit
[1/3832] Fetching swift-docc-symbolkit
Fetched https://github.com/swiftlang/swift-docc-symbolkit from cache (1.38s)
Computing version for https://github.com/swiftlang/swift-docc-symbolkit
Computed https://github.com/swiftlang/swift-docc-symbolkit at 1.0.0 (2.06s)
Creating working copy for https://github.com/swiftlang/swift-docc-plugin
Working copy of https://github.com/swiftlang/swift-docc-plugin resolved at 1.5.0
Creating working copy for https://github.com/swiftlang/swift-docc-symbolkit
Working copy of https://github.com/swiftlang/swift-docc-symbolkit resolved at 1.0.0
Building for debugging...
[0/8] Write sources
[0/8] Write snippet-extract-tool-entitlement.plist
[3/8] Write sources
[4/8] Write swift-version-69A768CDF2A0BEE1.txt
[6/54] Compiling SymbolKit GenericConstraint.swift
[7/54] Compiling SymbolKit GenericParameter.swift
[8/54] Compiling SymbolKit Generics.swift
[9/54] Compiling SymbolKit Namespace.swift
[10/58] Compiling SymbolKit Mixin+Equals.swift
[11/58] Compiling SymbolKit Mixin+Hash.swift
[12/58] Compiling SymbolKit Mixin.swift
[13/58] Compiling SymbolKit LineList.swift
[14/58] Compiling SymbolKit Position.swift
[15/58] Emitting module SymbolKit
[16/58] Compiling SymbolKit Names.swift
[17/58] Compiling SymbolKit SPI.swift
[18/58] Compiling SymbolKit Snippet.swift
[19/58] Compiling SymbolKit Extension.swift
[20/58] Compiling SymbolKit DeclarationFragments.swift
[21/58] Compiling SymbolKit Fragment.swift
[22/58] Compiling SymbolKit FragmentKind.swift
[23/58] Compiling SymbolKit FunctionParameter.swift
[24/58] Compiling SymbolKit FunctionSignature.swift
[25/58] Compiling SymbolKit SemanticVersion.swift
[26/58] Compiling SymbolKit AccessControl.swift
[27/58] Compiling SymbolKit Availability.swift
[28/58] Compiling SymbolKit AvailabilityItem.swift
[29/58] Compiling SymbolKit Domain.swift
[30/58] Compiling SymbolKit Identifier.swift
[31/58] Compiling SymbolKit KindIdentifier.swift
[32/58] Compiling SymbolKit Location.swift
[33/58] Compiling SymbolKit Mutability.swift
[34/58] Compiling SymbolKit Relationship.swift
[35/58] Compiling SymbolKit RelationshipKind.swift
[36/58] Compiling SymbolKit SourceOrigin.swift
[37/58] Compiling SymbolKit GenericConstraints.swift
[38/58] Compiling SymbolKit Swift.swift
[39/58] Compiling SymbolKit SourceRange.swift
[40/58] Compiling SymbolKit Metadata.swift
[41/58] Compiling SymbolKit Module.swift
[42/58] Compiling SymbolKit OperatingSystem.swift
[43/58] Compiling SymbolKit Platform.swift
[44/58] Compiling SymbolKit Symbol.swift
[45/58] Compiling SymbolKit SymbolKind.swift
[46/58] Compiling SymbolKit SymbolGraph.swift
[47/58] Compiling SymbolKit GraphCollector.swift
[48/58] Compiling SymbolKit UnifiedSymbol+Encodable.swift
[49/58] Compiling SymbolKit UnifiedSymbol.swift
[50/58] Compiling SymbolKit UnifiedSymbolGraph+Encodable.swift
[51/58] Compiling SymbolKit UnifiedSymbolGraph.swift
[52/58] Emitting module Snippets
[53/58] Compiling Snippets Snippet.swift
[54/58] Compiling Snippets SnippetParser.swift
[55/58] Compiling Snippets SnippetLanguage.swift
[56/62] Compiling snippet_extract SnippetBuildCommand.swift
[57/62] Compiling snippet_extract URL+Status.swift
[58/62] Compiling snippet_extract SymbolGraph+Snippet.swift
[59/62] Emitting module snippet_extract
[59/62] Write Objects.LinkFileList
[60/62] Linking snippet-extract-tool
[61/62] Applying snippet-extract-tool
Build of product 'snippet-extract' complete! (7.07s)
Building for debugging...
[0/1] Write swift-version-69A768CDF2A0BEE1.txt
Build of target: 'SwiftOgmios' complete! (3.58s)
16050
87 /Users/admin/builder/spi-builder-workspace/.docs/kingpin-apps/swift-ogmios/0.3.2
✅ Doc result (uploading) reported
========================================
SyncDocs
========================================
Doc archive source directory: $PWD/.docs/kingpin-apps/swift-ogmios/0.3.2
File count: 16050
Doc size: 87.0MB
Preparing doc bundle ...
Using presigned URL upload - AWS credentials not required
Uploading -kingpin-apps-swift-ogmios-0.3.2-194aea9d.zip to presigned URL
Starting upload of 20004264 bytes...
Upload completed successfully
Done.