synor/sdk/swift/Sources/SynorCompiler/SynorCompiler.swift
Gulshan Yadav 2c534a18bb feat(sdk): Add Compiler SDK for all 12 languages
Implements WASM smart contract compilation, optimization, and ABI generation
across JavaScript/TypeScript, Python, Go, Rust, Flutter/Dart, Java, Kotlin,
Swift, C, C++, C#/.NET, and Ruby.

Features:
- Optimization levels: None, Basic, Size, Aggressive
- WASM section stripping with customizable options
- Contract validation against VM requirements
- ABI extraction and generation
- Contract metadata handling
- Gas estimation for deployment
- Security analysis and recommendations
- Size breakdown analysis

Sub-clients: Contracts, ABI, Analysis, Validation
2026-01-28 13:28:18 +05:30

590 lines
20 KiB
Swift

/**
* 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<T: Decodable>(_ 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<T: Decodable>(_ 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<T: Decodable>(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