/** * Synor Compiler SDK for Swift * * Smart contract compilation, optimization, and ABI generation. */ import Foundation // MARK: - Enumerations public enum OptimizationLevel: String, Codable { case none case basic case size case aggressive } // MARK: - Configuration public struct StripOptions: Codable { public var stripDebug: Bool = true public var stripProducers: Bool = true public var stripNames: Bool = true public var stripCustom: Bool = true public var preserveSections: [String] = [] public var stripUnused: Bool = true public init() {} private enum CodingKeys: String, CodingKey { case stripDebug = "strip_debug" case stripProducers = "strip_producers" case stripNames = "strip_names" case stripCustom = "strip_custom" case preserveSections = "preserve_sections" case stripUnused = "strip_unused" } } public struct CompilerConfig { public let apiKey: String public var endpoint: String = "https://compiler.synor.io/v1" public var timeout: TimeInterval = 60 public var retries: Int = 3 public var debug: Bool = false public var optimizationLevel: OptimizationLevel = .size public var stripOptions: StripOptions? public var maxContractSize: Int = 256 * 1024 public var useWasmOpt: Bool = true public var validate: Bool = true public var extractMetadata: Bool = true public var generateAbi: Bool = true public init(apiKey: String) { self.apiKey = apiKey } } // MARK: - Types public struct ValidationError: Codable { public let code: String public let message: String public let location: String? } public struct ValidationResult: Codable { public let valid: Bool public let errors: [ValidationError] public let warnings: [String] public let exportCount: Int public let importCount: Int public let functionCount: Int public let memoryPages: [Int?] private enum CodingKeys: String, CodingKey { case valid, errors, warnings case exportCount = "export_count" case importCount = "import_count" case functionCount = "function_count" case memoryPages = "memory_pages" } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) valid = try container.decode(Bool.self, forKey: .valid) errors = try container.decodeIfPresent([ValidationError].self, forKey: .errors) ?? [] warnings = try container.decodeIfPresent([String].self, forKey: .warnings) ?? [] exportCount = try container.decodeIfPresent(Int.self, forKey: .exportCount) ?? 0 importCount = try container.decodeIfPresent(Int.self, forKey: .importCount) ?? 0 functionCount = try container.decodeIfPresent(Int.self, forKey: .functionCount) ?? 0 memoryPages = try container.decodeIfPresent([Int?].self, forKey: .memoryPages) ?? [0, nil] } } public struct ContractMetadata: Codable { public let name: String? public let version: String? public let authors: [String] public let description: String? public let license: String? public let repository: String? public let buildTimestamp: Int64? public let rustVersion: String? public let sdkVersion: String? public let custom: [String: String] private enum CodingKeys: String, CodingKey { case name, version, authors, description, license, repository, custom case buildTimestamp = "build_timestamp" case rustVersion = "rust_version" case sdkVersion = "sdk_version" } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decodeIfPresent(String.self, forKey: .name) version = try container.decodeIfPresent(String.self, forKey: .version) authors = try container.decodeIfPresent([String].self, forKey: .authors) ?? [] description = try container.decodeIfPresent(String.self, forKey: .description) license = try container.decodeIfPresent(String.self, forKey: .license) repository = try container.decodeIfPresent(String.self, forKey: .repository) buildTimestamp = try container.decodeIfPresent(Int64.self, forKey: .buildTimestamp) rustVersion = try container.decodeIfPresent(String.self, forKey: .rustVersion) sdkVersion = try container.decodeIfPresent(String.self, forKey: .sdkVersion) custom = try container.decodeIfPresent([String: String].self, forKey: .custom) ?? [:] } } public struct TypeInfo: Codable { public let kind: String public let size: Int? public let element: TypeInfo? public let inner: TypeInfo? public let elements: [TypeInfo]? public let name: String? public var typeName: String { switch kind { case "u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128", "bool", "string", "bytes", "address", "hash256": return kind case "fixedBytes": return "bytes\(size ?? 0)" case "array": return "\(element?.typeName ?? "")[]" case "fixedArray": return "\(element?.typeName ?? "")[\(size ?? 0)]" case "option": return "\(inner?.typeName ?? "")?" case "tuple": return "(\(elements?.map { $0.typeName }.joined(separator: ", ") ?? ""))" case "struct": return name ?? "struct" default: return name ?? "unknown" } } } public struct ParamAbi: Codable { public let name: String public let type: TypeInfo public let indexed: Bool private enum CodingKeys: String, CodingKey { case name, type, indexed } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) type = try container.decode(TypeInfo.self, forKey: .type) indexed = try container.decodeIfPresent(Bool.self, forKey: .indexed) ?? false } } public struct FunctionAbi: Codable { public let name: String public let selector: String public let inputs: [ParamAbi] public let outputs: [ParamAbi] public let view: Bool public let payable: Bool public let doc: String? public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) selector = try container.decode(String.self, forKey: .selector) inputs = try container.decodeIfPresent([ParamAbi].self, forKey: .inputs) ?? [] outputs = try container.decodeIfPresent([ParamAbi].self, forKey: .outputs) ?? [] view = try container.decodeIfPresent(Bool.self, forKey: .view) ?? false payable = try container.decodeIfPresent(Bool.self, forKey: .payable) ?? false doc = try container.decodeIfPresent(String.self, forKey: .doc) } } public struct EventAbi: Codable { public let name: String public let topic: String public let params: [ParamAbi] public let doc: String? public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) topic = try container.decode(String.self, forKey: .topic) params = try container.decodeIfPresent([ParamAbi].self, forKey: .params) ?? [] doc = try container.decodeIfPresent(String.self, forKey: .doc) } } public struct ErrorAbi: Codable { public let name: String public let selector: String public let params: [ParamAbi] public let doc: String? public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) selector = try container.decode(String.self, forKey: .selector) params = try container.decodeIfPresent([ParamAbi].self, forKey: .params) ?? [] doc = try container.decodeIfPresent(String.self, forKey: .doc) } } public struct ContractAbi: Codable { public let name: String public let version: String public let functions: [FunctionAbi] public let events: [EventAbi] public let errors: [ErrorAbi] public func findFunction(name: String) -> FunctionAbi? { functions.first { $0.name == name } } public func findBySelector(_ selector: String) -> FunctionAbi? { functions.first { $0.selector == selector } } } public struct CompilationResult: Codable { public let contractId: String public let code: String public let codeHash: String public let originalSize: Int public let optimizedSize: Int public let sizeReduction: Double public let metadata: ContractMetadata? public let abi: ContractAbi? public let estimatedDeployGas: Int64 public let validation: ValidationResult? public let warnings: [String] private enum CodingKeys: String, CodingKey { case contractId = "contract_id" case code case codeHash = "code_hash" case originalSize = "original_size" case optimizedSize = "optimized_size" case sizeReduction = "size_reduction" case metadata, abi case estimatedDeployGas = "estimated_deploy_gas" case validation, warnings } public var sizeStats: String { String(format: "Original: %d bytes, Optimized: %d bytes, Reduction: %.1f%%", originalSize, optimizedSize, sizeReduction) } public func decodeCode() -> Data? { Data(base64Encoded: code) } } public struct SizeBreakdown: Codable { public let code: Int public let data: Int public let types: Int public let functions: Int public let memory: Int public let table: Int public let exports: Int public let imports: Int public let custom: Int public let total: Int } public struct FunctionAnalysis: Codable { public let name: String public let size: Int public let instructionCount: Int public let localCount: Int public let exported: Bool public let estimatedGas: Int64 private enum CodingKeys: String, CodingKey { case name, size, exported case instructionCount = "instruction_count" case localCount = "local_count" case estimatedGas = "estimated_gas" } } public struct ImportAnalysis: Codable { public let module: String public let name: String public let kind: String public let signature: String? } public struct SecurityIssue: Codable { public let severity: String public let type: String public let description: String public let location: String? } public struct SecurityAnalysis: Codable { public let score: Int public let issues: [SecurityIssue] public let recommendations: [String] } public struct GasAnalysis: Codable { public let deploymentGas: Int64 public let functionGas: [String: Int64] public let memoryInitGas: Int64 public let dataSectionGas: Int64 private enum CodingKeys: String, CodingKey { case deploymentGas = "deployment_gas" case functionGas = "function_gas" case memoryInitGas = "memory_init_gas" case dataSectionGas = "data_section_gas" } } public struct ContractAnalysis: Codable { public let sizeBreakdown: SizeBreakdown public let functions: [FunctionAnalysis] public let imports: [ImportAnalysis] public let security: SecurityAnalysis? public let gasAnalysis: GasAnalysis? private enum CodingKeys: String, CodingKey { case sizeBreakdown = "size_breakdown" case functions, imports, security case gasAnalysis = "gas_analysis" } } public struct CompilerError: Error { public let message: String public let code: String? public let httpStatus: Int? public init(_ message: String, code: String? = nil, httpStatus: Int? = nil) { self.message = message self.code = code self.httpStatus = httpStatus } } // MARK: - Client public actor SynorCompiler { private let config: CompilerConfig private let session: URLSession private let decoder: JSONDecoder private let encoder: JSONEncoder private var closed = false public let contracts: ContractsClient public let abi: AbiClient public let analysis: AnalysisClient public let validation: ValidationClient public init(config: CompilerConfig) { self.config = config let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = config.timeout self.session = URLSession(configuration: configuration) self.decoder = JSONDecoder() self.encoder = JSONEncoder() self.encoder.keyEncodingStrategy = .convertToSnakeCase self.contracts = ContractsClient() self.abi = AbiClient() self.analysis = AnalysisClient() self.validation = ValidationClient() Task { await self.contracts.setCompiler(self) } Task { await self.abi.setCompiler(self) } Task { await self.analysis.setCompiler(self) } Task { await self.validation.setCompiler(self) } } public var defaultOptimizationLevel: OptimizationLevel { config.optimizationLevel } public func healthCheck() async -> Bool { do { let result: [String: String] = try await get("/health") return result["status"] == "healthy" } catch { return false } } public func getInfo() async throws -> [String: Any] { try await get("/info") } public func close() { closed = true } public var isClosed: Bool { closed } public func compile( wasm: Data, optimizationLevel: OptimizationLevel? = nil, stripOptions: StripOptions? = nil, useWasmOpt: Bool? = nil, validate: Bool? = nil, extractMetadata: Bool? = nil, generateAbi: Bool? = nil ) async throws -> CompilationResult { let wasmBase64 = wasm.base64EncodedString() var body: [String: Any] = [ "wasm": wasmBase64, "optimization_level": (optimizationLevel ?? config.optimizationLevel).rawValue, "use_wasm_opt": useWasmOpt ?? config.useWasmOpt, "validate": validate ?? config.validate, "extract_metadata": extractMetadata ?? config.extractMetadata, "generate_abi": generateAbi ?? config.generateAbi ] if let options = stripOptions { body["strip_options"] = try encoder.encode(options) } return try await post("/compile", body: body) } func get(_ path: String) async throws -> T { guard !closed else { throw CompilerError("Client has been closed", code: "CLIENT_CLOSED") } var request = URLRequest(url: URL(string: config.endpoint + path)!) request.httpMethod = "GET" request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization") request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version") let (data, response) = try await session.data(for: request) return try handleResponse(data: data, response: response) } func post(_ path: String, body: [String: Any]) async throws -> T { guard !closed else { throw CompilerError("Client has been closed", code: "CLIENT_CLOSED") } var request = URLRequest(url: URL(string: config.endpoint + path)!) request.httpMethod = "POST" request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization") request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version") request.httpBody = try JSONSerialization.data(withJSONObject: body) let (data, response) = try await session.data(for: request) return try handleResponse(data: data, response: response) } private func handleResponse(data: Data, response: URLResponse) throws -> T { guard let httpResponse = response as? HTTPURLResponse else { throw CompilerError("Invalid response") } if httpResponse.statusCode >= 400 { if let error = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { throw CompilerError( error["message"] as? String ?? "HTTP \(httpResponse.statusCode)", code: error["code"] as? String, httpStatus: httpResponse.statusCode ) } throw CompilerError("HTTP \(httpResponse.statusCode)", httpStatus: httpResponse.statusCode) } return try decoder.decode(T.self, from: data) } } // MARK: - Sub-Clients public actor ContractsClient { private var compiler: SynorCompiler? func setCompiler(_ compiler: SynorCompiler) { self.compiler = compiler } public func compile(_ wasm: Data) async throws -> CompilationResult { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.compile(wasm: wasm) } public func compileDev(_ wasm: Data) async throws -> CompilationResult { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.compile(wasm: wasm, optimizationLevel: .none, useWasmOpt: false, validate: true) } public func compileProduction(_ wasm: Data) async throws -> CompilationResult { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.compile(wasm: wasm, optimizationLevel: .aggressive, useWasmOpt: true, validate: true, extractMetadata: true, generateAbi: true) } public func get(_ contractId: String) async throws -> CompilationResult { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.get("/contracts/\(contractId)") } } public actor AbiClient { private var compiler: SynorCompiler? func setCompiler(_ compiler: SynorCompiler) { self.compiler = compiler } public func extract(_ wasm: Data) async throws -> ContractAbi { guard let compiler = compiler else { throw CompilerError("Compiler not set") } let wasmBase64 = wasm.base64EncodedString() return try await compiler.post("/abi/extract", body: ["wasm": wasmBase64]) } public func get(_ contractId: String) async throws -> ContractAbi { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.get("/contracts/\(contractId)/abi") } } public actor AnalysisClient { private var compiler: SynorCompiler? func setCompiler(_ compiler: SynorCompiler) { self.compiler = compiler } public func analyze(_ wasm: Data) async throws -> ContractAnalysis { guard let compiler = compiler else { throw CompilerError("Compiler not set") } let wasmBase64 = wasm.base64EncodedString() return try await compiler.post("/analysis", body: ["wasm": wasmBase64]) } public func get(_ contractId: String) async throws -> ContractAnalysis { guard let compiler = compiler else { throw CompilerError("Compiler not set") } return try await compiler.get("/contracts/\(contractId)/analysis") } public func extractMetadata(_ wasm: Data) async throws -> ContractMetadata { guard let compiler = compiler else { throw CompilerError("Compiler not set") } let wasmBase64 = wasm.base64EncodedString() return try await compiler.post("/analysis/metadata", body: ["wasm": wasmBase64]) } } public actor ValidationClient { private var compiler: SynorCompiler? func setCompiler(_ compiler: SynorCompiler) { self.compiler = compiler } public func validate(_ wasm: Data) async throws -> ValidationResult { guard let compiler = compiler else { throw CompilerError("Compiler not set") } let wasmBase64 = wasm.base64EncodedString() return try await compiler.post("/validate", body: ["wasm": wasmBase64]) } public func isValid(_ wasm: Data) async throws -> Bool { try await validate(wasm).valid } public func getErrors(_ wasm: Data) async throws -> [String] { try await validate(wasm).errors.map { $0.message } } } // MARK: - Constants public let MAX_CONTRACT_SIZE = 256 * 1024 public let MAX_MEMORY_PAGES = 16