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
This commit is contained in:
Gulshan Yadav 2026-01-28 13:28:18 +05:30
parent eab599767c
commit 2c534a18bb
21 changed files with 9362 additions and 0 deletions

View file

@ -0,0 +1,520 @@
/**
* Synor Compiler SDK for C
*
* Smart contract compilation, optimization, and ABI generation.
*/
#ifndef SYNOR_COMPILER_H
#define SYNOR_COMPILER_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ============================================================================
* Opaque Types
* ============================================================================ */
typedef struct synor_compiler_client synor_compiler_client_t;
typedef struct synor_compilation_result synor_compilation_result_t;
typedef struct synor_contract_abi synor_contract_abi_t;
typedef struct synor_contract_analysis synor_contract_analysis_t;
typedef struct synor_validation_result synor_validation_result_t;
/* ============================================================================
* Enumerations
* ============================================================================ */
/** Optimization level */
typedef enum {
SYNOR_COMPILER_OPT_NONE = 0, /**< No optimization */
SYNOR_COMPILER_OPT_BASIC = 1, /**< Basic optimizations (stripping only) */
SYNOR_COMPILER_OPT_SIZE = 2, /**< Optimize for size (-Os equivalent) */
SYNOR_COMPILER_OPT_AGGRESSIVE = 3 /**< Aggressive optimization (-O3 -Os equivalent) */
} synor_compiler_optimization_level_t;
/** Error codes */
typedef enum {
SYNOR_COMPILER_OK = 0,
SYNOR_COMPILER_ERROR_INVALID_ARGUMENT = 1,
SYNOR_COMPILER_ERROR_NETWORK = 2,
SYNOR_COMPILER_ERROR_AUTH = 3,
SYNOR_COMPILER_ERROR_NOT_FOUND = 4,
SYNOR_COMPILER_ERROR_TIMEOUT = 5,
SYNOR_COMPILER_ERROR_INTERNAL = 6,
SYNOR_COMPILER_ERROR_CLIENT_CLOSED = 7,
SYNOR_COMPILER_ERROR_PARSE = 8,
SYNOR_COMPILER_ERROR_VALIDATION = 9,
SYNOR_COMPILER_ERROR_OPTIMIZATION = 10,
SYNOR_COMPILER_ERROR_CONTRACT_TOO_LARGE = 11
} synor_compiler_error_t;
/* ============================================================================
* Structures
* ============================================================================ */
/** Strip options */
typedef struct {
bool strip_debug; /**< Strip debug sections */
bool strip_producers; /**< Strip producer sections */
bool strip_names; /**< Strip name sections */
bool strip_custom; /**< Strip custom sections */
const char** preserve_sections; /**< Sections to preserve */
size_t preserve_sections_count;
bool strip_unused; /**< Strip unused functions */
} synor_compiler_strip_options_t;
/** Compiler configuration */
typedef struct {
const char* api_key;
const char* endpoint; /**< Default: "https://compiler.synor.io/v1" */
uint32_t timeout_ms; /**< Default: 60000 */
uint32_t retries; /**< Default: 3 */
synor_compiler_optimization_level_t optimization_level; /**< Default: SIZE */
synor_compiler_strip_options_t* strip_options; /**< Optional */
size_t max_contract_size; /**< Default: 262144 (256 KB) */
bool use_wasm_opt; /**< Default: true */
bool validate; /**< Default: true */
bool extract_metadata; /**< Default: true */
bool generate_abi; /**< Default: true */
bool debug; /**< Default: false */
} synor_compiler_config_t;
/** Compilation request */
typedef struct {
synor_compiler_optimization_level_t* optimization_level; /**< Optional */
synor_compiler_strip_options_t* strip_options; /**< Optional */
bool* use_wasm_opt; /**< Optional */
bool* validate; /**< Optional */
bool* extract_metadata; /**< Optional */
bool* generate_abi; /**< Optional */
} synor_compiler_request_t;
/** Validation error */
typedef struct {
char code[64];
char message[256];
char* location; /**< Optional */
} synor_compiler_validation_error_t;
/** Contract metadata */
typedef struct {
char* name; /**< Optional */
char* version; /**< Optional */
char** authors;
size_t authors_count;
char* description; /**< Optional */
char* license; /**< Optional */
char* repository; /**< Optional */
uint64_t build_timestamp;
char* rust_version; /**< Optional */
char* sdk_version; /**< Optional */
} synor_compiler_metadata_t;
/** Compilation result info */
typedef struct {
char contract_id[64];
char code_hash[66];
size_t original_size;
size_t optimized_size;
double size_reduction;
uint64_t estimated_deploy_gas;
bool validation_valid;
size_t warning_count;
} synor_compiler_result_info_t;
/** Function ABI */
typedef struct {
char name[128];
char selector[10]; /* 4 bytes hex + null */
size_t input_count;
size_t output_count;
bool view;
bool payable;
char* doc;
} synor_compiler_function_abi_t;
/** ABI info */
typedef struct {
char name[128];
char version[32];
size_t function_count;
size_t event_count;
size_t error_count;
} synor_compiler_abi_info_t;
/** Size breakdown */
typedef struct {
size_t code;
size_t data;
size_t types;
size_t functions;
size_t memory;
size_t table;
size_t exports;
size_t imports;
size_t custom;
size_t total;
} synor_compiler_size_breakdown_t;
/** Security issue */
typedef struct {
char severity[16]; /* low, medium, high, critical */
char type[64];
char description[256];
char* location;
} synor_compiler_security_issue_t;
/** Security analysis */
typedef struct {
int score; /* 0-100 */
synor_compiler_security_issue_t* issues;
size_t issue_count;
char** recommendations;
size_t recommendation_count;
} synor_compiler_security_analysis_t;
/* ============================================================================
* Client Lifecycle
* ============================================================================ */
/**
* Create a new compiler client
*
* @param config Configuration options
* @return New client handle, or NULL on error
*/
synor_compiler_client_t* synor_compiler_client_new(const synor_compiler_config_t* config);
/**
* Close and free a client
*
* @param client Client to close
*/
void synor_compiler_client_free(synor_compiler_client_t* client);
/**
* Check if client is closed
*
* @param client Client handle
* @return true if closed
*/
bool synor_compiler_client_is_closed(const synor_compiler_client_t* client);
/**
* Health check
*
* @param client Client handle
* @param healthy Output parameter for health status
* @return Error code
*/
synor_compiler_error_t synor_compiler_health_check(synor_compiler_client_t* client, bool* healthy);
/* ============================================================================
* Compilation Operations
* ============================================================================ */
/**
* Compile WASM bytecode
*
* @param client Client handle
* @param wasm WASM bytecode
* @param wasm_len Length of WASM bytecode
* @param request Optional compilation request parameters
* @param result Output parameter for compilation result
* @return Error code
*/
synor_compiler_error_t synor_compiler_compile(
synor_compiler_client_t* client,
const uint8_t* wasm,
size_t wasm_len,
const synor_compiler_request_t* request,
synor_compilation_result_t** result
);
/**
* Get compilation result info
*
* @param result Compilation result handle
* @param info Output parameter for result info
* @return Error code
*/
synor_compiler_error_t synor_compiler_result_get_info(
const synor_compilation_result_t* result,
synor_compiler_result_info_t* info
);
/**
* Get optimized bytecode from result
*
* @param result Compilation result handle
* @param code Output buffer for bytecode
* @param code_len Input: buffer size, Output: actual bytecode length
* @return Error code
*/
synor_compiler_error_t synor_compiler_result_get_code(
const synor_compilation_result_t* result,
uint8_t* code,
size_t* code_len
);
/**
* Get metadata from result
*
* @param result Compilation result handle
* @param metadata Output parameter for metadata
* @return Error code
*/
synor_compiler_error_t synor_compiler_result_get_metadata(
const synor_compilation_result_t* result,
synor_compiler_metadata_t* metadata
);
/**
* Free compilation result
*
* @param result Result to free
*/
void synor_compiler_result_free(synor_compilation_result_t* result);
/* ============================================================================
* ABI Operations
* ============================================================================ */
/**
* Extract ABI from WASM bytecode
*
* @param client Client handle
* @param wasm WASM bytecode
* @param wasm_len Length of WASM bytecode
* @param abi Output parameter for ABI
* @return Error code
*/
synor_compiler_error_t synor_compiler_abi_extract(
synor_compiler_client_t* client,
const uint8_t* wasm,
size_t wasm_len,
synor_contract_abi_t** abi
);
/**
* Get ABI info
*
* @param abi ABI handle
* @param info Output parameter for ABI info
* @return Error code
*/
synor_compiler_error_t synor_compiler_abi_get_info(
const synor_contract_abi_t* abi,
synor_compiler_abi_info_t* info
);
/**
* Get function from ABI by index
*
* @param abi ABI handle
* @param index Function index
* @param func Output parameter for function
* @return Error code
*/
synor_compiler_error_t synor_compiler_abi_get_function(
const synor_contract_abi_t* abi,
size_t index,
synor_compiler_function_abi_t* func
);
/**
* Find function by name
*
* @param abi ABI handle
* @param name Function name
* @param func Output parameter for function (NULL if not found)
* @return Error code
*/
synor_compiler_error_t synor_compiler_abi_find_function(
const synor_contract_abi_t* abi,
const char* name,
synor_compiler_function_abi_t* func
);
/**
* Free ABI
*
* @param abi ABI to free
*/
void synor_compiler_abi_free(synor_contract_abi_t* abi);
/* ============================================================================
* Analysis Operations
* ============================================================================ */
/**
* Analyze WASM bytecode
*
* @param client Client handle
* @param wasm WASM bytecode
* @param wasm_len Length of WASM bytecode
* @param analysis Output parameter for analysis
* @return Error code
*/
synor_compiler_error_t synor_compiler_analyze(
synor_compiler_client_t* client,
const uint8_t* wasm,
size_t wasm_len,
synor_contract_analysis_t** analysis
);
/**
* Get size breakdown from analysis
*
* @param analysis Analysis handle
* @param breakdown Output parameter for size breakdown
* @return Error code
*/
synor_compiler_error_t synor_compiler_analysis_get_size_breakdown(
const synor_contract_analysis_t* analysis,
synor_compiler_size_breakdown_t* breakdown
);
/**
* Get security analysis
*
* @param analysis Analysis handle
* @param security Output parameter for security analysis
* @return Error code
*/
synor_compiler_error_t synor_compiler_analysis_get_security(
const synor_contract_analysis_t* analysis,
synor_compiler_security_analysis_t* security
);
/**
* Estimate deployment gas
*
* @param client Client handle
* @param wasm WASM bytecode
* @param wasm_len Length of WASM bytecode
* @param gas Output parameter for gas estimate
* @return Error code
*/
synor_compiler_error_t synor_compiler_estimate_deploy_gas(
synor_compiler_client_t* client,
const uint8_t* wasm,
size_t wasm_len,
uint64_t* gas
);
/**
* Free analysis
*
* @param analysis Analysis to free
*/
void synor_compiler_analysis_free(synor_contract_analysis_t* analysis);
/* ============================================================================
* Validation Operations
* ============================================================================ */
/**
* Validate WASM bytecode
*
* @param client Client handle
* @param wasm WASM bytecode
* @param wasm_len Length of WASM bytecode
* @param result Output parameter for validation result
* @return Error code
*/
synor_compiler_error_t synor_compiler_validate(
synor_compiler_client_t* client,
const uint8_t* wasm,
size_t wasm_len,
synor_validation_result_t** result
);
/**
* Check if validation passed
*
* @param result Validation result handle
* @param valid Output parameter for validity
* @return Error code
*/
synor_compiler_error_t synor_compiler_validation_is_valid(
const synor_validation_result_t* result,
bool* valid
);
/**
* Get validation error count
*
* @param result Validation result handle
* @param count Output parameter for error count
* @return Error code
*/
synor_compiler_error_t synor_compiler_validation_get_error_count(
const synor_validation_result_t* result,
size_t* count
);
/**
* Get validation error by index
*
* @param result Validation result handle
* @param index Error index
* @param error Output parameter for error
* @return Error code
*/
synor_compiler_error_t synor_compiler_validation_get_error(
const synor_validation_result_t* result,
size_t index,
synor_compiler_validation_error_t* error
);
/**
* Free validation result
*
* @param result Result to free
*/
void synor_compiler_validation_free(synor_validation_result_t* result);
/* ============================================================================
* Utility Functions
* ============================================================================ */
/**
* Get error message
*
* @param error Error code
* @return Error message string
*/
const char* synor_compiler_error_message(synor_compiler_error_t error);
/**
* Free metadata
*
* @param metadata Metadata to free
*/
void synor_compiler_metadata_free(synor_compiler_metadata_t* metadata);
/**
* Free security analysis
*
* @param security Security analysis to free
*/
void synor_compiler_security_free(synor_compiler_security_analysis_t* security);
/* ============================================================================
* Constants
* ============================================================================ */
#define SYNOR_COMPILER_MAX_CONTRACT_SIZE 262144 /* 256 KB */
#define SYNOR_COMPILER_MAX_MEMORY_PAGES 16
#ifdef __cplusplus
}
#endif
#endif /* SYNOR_COMPILER_H */

View file

@ -0,0 +1,423 @@
/**
* Synor Compiler SDK for C++
*
* Smart contract compilation, optimization, and ABI generation.
*/
#ifndef SYNOR_COMPILER_HPP
#define SYNOR_COMPILER_HPP
#include <string>
#include <vector>
#include <memory>
#include <optional>
#include <chrono>
#include <future>
#include <stdexcept>
#include <map>
#include <cstdint>
namespace synor {
namespace compiler {
/* ============================================================================
* Enumerations
* ============================================================================ */
/** Optimization level */
enum class OptimizationLevel {
None, ///< No optimization
Basic, ///< Basic optimizations (stripping only)
Size, ///< Optimize for size (-Os equivalent)
Aggressive ///< Aggressive optimization (-O3 -Os equivalent)
};
/* ============================================================================
* Exception
* ============================================================================ */
class CompilerError : public std::runtime_error {
public:
CompilerError(const std::string& message,
const std::optional<std::string>& code = std::nullopt,
const std::optional<int>& status = std::nullopt)
: std::runtime_error(message), code_(code), status_(status) {}
const std::optional<std::string>& code() const { return code_; }
const std::optional<int>& status() const { return status_; }
private:
std::optional<std::string> code_;
std::optional<int> status_;
};
/* ============================================================================
* Data Types
* ============================================================================ */
/** Strip options */
struct StripOptions {
bool strip_debug = true;
bool strip_producers = true;
bool strip_names = true;
bool strip_custom = true;
std::vector<std::string> preserve_sections;
bool strip_unused = true;
};
/** Compiler configuration */
struct CompilerConfig {
std::string api_key;
std::string endpoint = "https://compiler.synor.io/v1";
std::chrono::milliseconds timeout{60000};
int retries = 3;
bool debug = false;
OptimizationLevel optimization_level = OptimizationLevel::Size;
std::optional<StripOptions> strip_options;
size_t max_contract_size = 256 * 1024;
bool use_wasm_opt = true;
bool validate = true;
bool extract_metadata = true;
bool generate_abi = true;
};
/** Validation error */
struct ValidationError {
std::string code;
std::string message;
std::optional<std::string> location;
};
/** Validation result */
struct ValidationResult {
bool valid;
std::vector<ValidationError> errors;
std::vector<std::string> warnings;
size_t export_count = 0;
size_t import_count = 0;
size_t function_count = 0;
std::pair<uint32_t, std::optional<uint32_t>> memory_pages = {0, std::nullopt};
};
/** Contract metadata */
struct ContractMetadata {
std::optional<std::string> name;
std::optional<std::string> version;
std::vector<std::string> authors;
std::optional<std::string> description;
std::optional<std::string> license;
std::optional<std::string> repository;
std::optional<uint64_t> build_timestamp;
std::optional<std::string> rust_version;
std::optional<std::string> sdk_version;
std::map<std::string, std::string> custom;
};
/** Type information */
struct TypeInfo {
std::string kind;
std::optional<size_t> size;
std::unique_ptr<TypeInfo> element;
std::unique_ptr<TypeInfo> inner;
std::vector<TypeInfo> elements;
std::optional<std::string> name;
std::vector<std::pair<std::string, TypeInfo>> fields;
std::string type_name() const;
};
/** Parameter ABI */
struct ParamAbi {
std::string name;
TypeInfo type;
bool indexed = false;
};
/** Function ABI */
struct FunctionAbi {
std::string name;
std::string selector;
std::vector<ParamAbi> inputs;
std::vector<ParamAbi> outputs;
bool view = false;
bool payable = false;
std::optional<std::string> doc;
};
/** Event ABI */
struct EventAbi {
std::string name;
std::string topic;
std::vector<ParamAbi> params;
std::optional<std::string> doc;
};
/** Error ABI */
struct ErrorAbi {
std::string name;
std::string selector;
std::vector<ParamAbi> params;
std::optional<std::string> doc;
};
/** Contract ABI */
struct ContractAbi {
std::string name;
std::string version;
std::vector<FunctionAbi> functions;
std::vector<EventAbi> events;
std::vector<ErrorAbi> errors;
const FunctionAbi* find_function(const std::string& name) const;
const FunctionAbi* find_by_selector(const std::string& selector) const;
std::string to_json() const;
};
/** Compilation result */
struct CompilationResult {
std::string contract_id;
std::vector<uint8_t> code;
std::string code_hash;
size_t original_size;
size_t optimized_size;
double size_reduction;
std::optional<ContractMetadata> metadata;
std::optional<ContractAbi> abi;
uint64_t estimated_deploy_gas = 0;
std::optional<ValidationResult> validation;
std::vector<std::string> warnings;
std::string size_stats() const;
};
/** Size breakdown */
struct SizeBreakdown {
size_t code = 0;
size_t data = 0;
size_t types = 0;
size_t functions = 0;
size_t memory = 0;
size_t table = 0;
size_t exports = 0;
size_t imports = 0;
size_t custom = 0;
size_t total = 0;
};
/** Function analysis */
struct FunctionAnalysis {
std::string name;
size_t size;
size_t instruction_count;
size_t local_count;
bool exported;
uint64_t estimated_gas;
};
/** Import analysis */
struct ImportAnalysis {
std::string module;
std::string name;
std::string kind;
std::optional<std::string> signature;
};
/** Security issue */
struct SecurityIssue {
std::string severity;
std::string type;
std::string description;
std::optional<std::string> location;
};
/** Security analysis */
struct SecurityAnalysis {
int score = 0;
std::vector<SecurityIssue> issues;
std::vector<std::string> recommendations;
};
/** Gas analysis */
struct GasAnalysis {
uint64_t deployment_gas = 0;
std::map<std::string, uint64_t> function_gas;
uint64_t memory_init_gas = 0;
uint64_t data_section_gas = 0;
};
/** Contract analysis */
struct ContractAnalysis {
SizeBreakdown size_breakdown;
std::vector<FunctionAnalysis> functions;
std::vector<ImportAnalysis> imports;
std::optional<SecurityAnalysis> security;
std::optional<GasAnalysis> gas_analysis;
};
/** Compilation request */
struct CompilationRequest {
std::optional<OptimizationLevel> optimization_level;
std::optional<StripOptions> strip_options;
std::optional<bool> use_wasm_opt;
std::optional<bool> validate;
std::optional<bool> extract_metadata;
std::optional<bool> generate_abi;
};
/* ============================================================================
* Client Classes
* ============================================================================ */
class SynorCompiler;
/** Contracts sub-client */
class ContractsClient {
public:
explicit ContractsClient(SynorCompiler& compiler) : compiler_(compiler) {}
std::future<CompilationResult> compile(const std::vector<uint8_t>& wasm,
const std::optional<CompilationRequest>& request = std::nullopt);
std::future<CompilationResult> compile_dev(const std::vector<uint8_t>& wasm);
std::future<CompilationResult> compile_production(const std::vector<uint8_t>& wasm);
std::future<CompilationResult> get(const std::string& contract_id);
std::future<std::vector<CompilationResult>> list(std::optional<int> limit = std::nullopt,
std::optional<int> offset = std::nullopt);
std::future<std::vector<uint8_t>> get_code(const std::string& contract_id);
private:
SynorCompiler& compiler_;
};
/** ABI sub-client */
class AbiClient {
public:
explicit AbiClient(SynorCompiler& compiler) : compiler_(compiler) {}
std::future<ContractAbi> extract(const std::vector<uint8_t>& wasm);
std::future<ContractAbi> get(const std::string& contract_id);
std::future<std::string> encode_call(const FunctionAbi& func, const std::vector<std::string>& args);
std::future<std::vector<std::string>> decode_result(const FunctionAbi& func, const std::string& data);
private:
SynorCompiler& compiler_;
};
/** Analysis sub-client */
class AnalysisClient {
public:
explicit AnalysisClient(SynorCompiler& compiler) : compiler_(compiler) {}
std::future<ContractAnalysis> analyze(const std::vector<uint8_t>& wasm);
std::future<ContractAnalysis> get(const std::string& contract_id);
std::future<ContractMetadata> extract_metadata(const std::vector<uint8_t>& wasm);
std::future<uint64_t> estimate_deploy_gas(const std::vector<uint8_t>& wasm);
std::future<SecurityAnalysis> security_scan(const std::vector<uint8_t>& wasm);
private:
SynorCompiler& compiler_;
};
/** Validation sub-client */
class ValidationClient {
public:
explicit ValidationClient(SynorCompiler& compiler) : compiler_(compiler) {}
std::future<ValidationResult> validate(const std::vector<uint8_t>& wasm);
std::future<bool> is_valid(const std::vector<uint8_t>& wasm);
std::future<std::vector<std::string>> get_errors(const std::vector<uint8_t>& wasm);
std::future<bool> validate_exports(const std::vector<uint8_t>& wasm,
const std::vector<std::string>& required_exports);
std::future<bool> validate_memory(const std::vector<uint8_t>& wasm, uint32_t max_pages);
private:
SynorCompiler& compiler_;
};
/**
* Synor Compiler SDK Client
*
* Smart contract compilation, optimization, and ABI generation.
*
* Example:
* @code
* synor::compiler::CompilerConfig config;
* config.api_key = "your-api-key";
* synor::compiler::SynorCompiler compiler(config);
*
* auto result = compiler.compile(wasm_bytes).get();
* std::cout << "Optimized size: " << result.optimized_size << " bytes" << std::endl;
* @endcode
*/
class SynorCompiler {
public:
explicit SynorCompiler(const CompilerConfig& config);
~SynorCompiler();
// Non-copyable
SynorCompiler(const SynorCompiler&) = delete;
SynorCompiler& operator=(const SynorCompiler&) = delete;
// Movable
SynorCompiler(SynorCompiler&&) noexcept;
SynorCompiler& operator=(SynorCompiler&&) noexcept;
/** Get the default optimization level */
OptimizationLevel default_optimization_level() const { return config_.optimization_level; }
/** Health check */
std::future<bool> health_check();
/** Get service info */
std::future<std::map<std::string, std::string>> get_info();
/** Close the client */
void close();
/** Check if client is closed */
bool is_closed() const { return closed_; }
/** Compile WASM bytecode */
std::future<CompilationResult> compile(const std::vector<uint8_t>& wasm,
const std::optional<CompilationRequest>& request = std::nullopt);
/** Sub-clients */
ContractsClient& contracts() { return contracts_; }
AbiClient& abi() { return abi_; }
AnalysisClient& analysis() { return analysis_; }
ValidationClient& validation() { return validation_; }
// Internal HTTP methods (public for sub-clients)
std::future<std::map<std::string, std::string>> get(const std::string& path);
std::future<std::map<std::string, std::string>> post(const std::string& path,
const std::map<std::string, std::string>& body);
private:
CompilerConfig config_;
bool closed_ = false;
ContractsClient contracts_;
AbiClient abi_;
AnalysisClient analysis_;
ValidationClient validation_;
};
/* ============================================================================
* Utility Functions
* ============================================================================ */
/** Convert optimization level to string */
std::string optimization_level_to_string(OptimizationLevel level);
/** Parse optimization level from string */
OptimizationLevel optimization_level_from_string(const std::string& str);
/* ============================================================================
* Constants
* ============================================================================ */
constexpr size_t MAX_CONTRACT_SIZE = 256 * 1024;
constexpr uint32_t MAX_MEMORY_PAGES = 16;
} // namespace compiler
} // namespace synor
#endif // SYNOR_COMPILER_HPP

View file

@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace Synor.Compiler;
/// <summary>
/// Synor Compiler SDK for C#/.NET
///
/// Smart contract compilation, optimization, and ABI generation.
/// </summary>
public class SynorCompiler : IDisposable
{
private readonly CompilerConfig _config;
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonOptions;
private bool _closed;
public ContractsClient Contracts { get; }
public AbiClient Abi { get; }
public AnalysisClient Analysis { get; }
public ValidationClient Validation { get; }
public SynorCompiler(CompilerConfig config)
{
_config = config;
_httpClient = new HttpClient
{
BaseAddress = new Uri(config.Endpoint),
Timeout = TimeSpan.FromMilliseconds(config.Timeout)
};
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {config.ApiKey}");
_httpClient.DefaultRequestHeaders.Add("X-SDK-Version", "csharp/0.1.0");
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower) }
};
Contracts = new ContractsClient(this);
Abi = new AbiClient(this);
Analysis = new AnalysisClient(this);
Validation = new ValidationClient(this);
}
public OptimizationLevel DefaultOptimizationLevel => _config.DefaultOptimizationLevel;
public async Task<bool> HealthCheckAsync(CancellationToken ct = default)
{
try
{
var result = await GetAsync<Dictionary<string, object>>("/health", ct);
return result.TryGetValue("status", out var status) && status?.ToString() == "healthy";
}
catch
{
return false;
}
}
public Task<Dictionary<string, object>> GetInfoAsync(CancellationToken ct = default) =>
GetAsync<Dictionary<string, object>>("/info", ct);
public void Close()
{
_closed = true;
_httpClient.Dispose();
}
public bool IsClosed => _closed;
public void Dispose()
{
Close();
GC.SuppressFinalize(this);
}
public async Task<CompilationResult> CompileAsync(
byte[] wasm,
CompilationRequest? request = null,
CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
var body = new Dictionary<string, object>
{
["wasm"] = wasmBase64,
["optimization_level"] = (request?.OptimizationLevel ?? _config.DefaultOptimizationLevel).ToApiString(),
["use_wasm_opt"] = request?.UseWasmOpt ?? _config.UseWasmOpt,
["validate"] = request?.Validate ?? _config.Validate,
["extract_metadata"] = request?.ExtractMetadata ?? _config.ExtractMetadata,
["generate_abi"] = request?.GenerateAbi ?? _config.GenerateAbi
};
if (request?.StripOptions != null)
{
body["strip_options"] = request.StripOptions;
}
return await PostAsync<CompilationResult>("/compile", body, ct);
}
internal async Task<T> GetAsync<T>(string path, CancellationToken ct = default)
{
CheckClosed();
var response = await _httpClient.GetAsync(path, ct);
return await HandleResponseAsync<T>(response);
}
internal async Task<T> PostAsync<T>(string path, object? body = null, CancellationToken ct = default)
{
CheckClosed();
var content = body != null
? JsonContent.Create(body, options: _jsonOptions)
: JsonContent.Create(new { });
var response = await _httpClient.PostAsync(path, content, ct);
return await HandleResponseAsync<T>(response);
}
private async Task<T> HandleResponseAsync<T>(HttpResponseMessage response)
{
var json = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
var error = JsonSerializer.Deserialize<Dictionary<string, object>>(json, _jsonOptions) ?? new();
throw new CompilerException(
error.TryGetValue("message", out var msg) ? msg?.ToString() ?? $"HTTP {(int)response.StatusCode}" : $"HTTP {(int)response.StatusCode}",
error.TryGetValue("code", out var code) ? code?.ToString() : null,
(int)response.StatusCode
);
}
return JsonSerializer.Deserialize<T>(json, _jsonOptions)!;
}
private void CheckClosed()
{
if (_closed) throw new CompilerException("Client has been closed", "CLIENT_CLOSED");
}
}
/// <summary>Contracts sub-client</summary>
public class ContractsClient
{
private readonly SynorCompiler _compiler;
internal ContractsClient(SynorCompiler compiler) => _compiler = compiler;
public Task<CompilationResult> CompileAsync(byte[] wasm, CompilationRequest? request = null, CancellationToken ct = default) =>
_compiler.CompileAsync(wasm, request, ct);
public Task<CompilationResult> CompileDevAsync(byte[] wasm, CancellationToken ct = default) =>
_compiler.CompileAsync(wasm, new CompilationRequest
{
OptimizationLevel = OptimizationLevel.None,
UseWasmOpt = false,
Validate = true
}, ct);
public Task<CompilationResult> CompileProductionAsync(byte[] wasm, CancellationToken ct = default) =>
_compiler.CompileAsync(wasm, new CompilationRequest
{
OptimizationLevel = OptimizationLevel.Aggressive,
UseWasmOpt = true,
Validate = true,
ExtractMetadata = true,
GenerateAbi = true
}, ct);
public Task<CompilationResult> GetAsync(string contractId, CancellationToken ct = default) =>
_compiler.GetAsync<CompilationResult>($"/contracts/{contractId}", ct);
public async Task<List<CompilationResult>> ListAsync(int? limit = null, int? offset = null, CancellationToken ct = default)
{
var path = "/contracts";
var qs = new List<string>();
if (limit.HasValue) qs.Add($"limit={limit}");
if (offset.HasValue) qs.Add($"offset={offset}");
if (qs.Count > 0) path += "?" + string.Join("&", qs);
var result = await _compiler.GetAsync<ContractsListResponse>(path, ct);
return result.Contracts ?? new List<CompilationResult>();
}
public async Task<byte[]> GetCodeAsync(string contractId, CancellationToken ct = default)
{
var result = await _compiler.GetAsync<CodeResponse>($"/contracts/{contractId}/code", ct);
return Convert.FromBase64String(result.Code);
}
}
/// <summary>ABI sub-client</summary>
public class AbiClient
{
private readonly SynorCompiler _compiler;
internal AbiClient(SynorCompiler compiler) => _compiler = compiler;
public async Task<ContractAbi> ExtractAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
return await _compiler.PostAsync<ContractAbi>("/abi/extract", new { wasm = wasmBase64 }, ct);
}
public Task<ContractAbi> GetAsync(string contractId, CancellationToken ct = default) =>
_compiler.GetAsync<ContractAbi>($"/contracts/{contractId}/abi", ct);
public async Task<string> EncodeCallAsync(FunctionAbi functionAbi, object[] args, CancellationToken ct = default)
{
var result = await _compiler.PostAsync<DataResponse>("/abi/encode", new
{
function = functionAbi,
args
}, ct);
return result.Data;
}
public async Task<object[]> DecodeResultAsync(FunctionAbi functionAbi, string data, CancellationToken ct = default)
{
var result = await _compiler.PostAsync<ValuesResponse>("/abi/decode", new
{
function = functionAbi,
data
}, ct);
return result.Values ?? Array.Empty<object>();
}
}
/// <summary>Analysis sub-client</summary>
public class AnalysisClient
{
private readonly SynorCompiler _compiler;
internal AnalysisClient(SynorCompiler compiler) => _compiler = compiler;
public async Task<ContractAnalysis> AnalyzeAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
return await _compiler.PostAsync<ContractAnalysis>("/analysis", new { wasm = wasmBase64 }, ct);
}
public Task<ContractAnalysis> GetAsync(string contractId, CancellationToken ct = default) =>
_compiler.GetAsync<ContractAnalysis>($"/contracts/{contractId}/analysis", ct);
public async Task<ContractMetadata> ExtractMetadataAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
return await _compiler.PostAsync<ContractMetadata>("/analysis/metadata", new { wasm = wasmBase64 }, ct);
}
public async Task<long> EstimateDeployGasAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
var result = await _compiler.PostAsync<GasResponse>("/analysis/estimate-gas", new { wasm = wasmBase64 }, ct);
return result.Gas;
}
public async Task<SecurityAnalysis> SecurityScanAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
var result = await _compiler.PostAsync<SecurityResponse>("/analysis/security", new { wasm = wasmBase64 }, ct);
return result.Security!;
}
}
/// <summary>Validation sub-client</summary>
public class ValidationClient
{
private readonly SynorCompiler _compiler;
internal ValidationClient(SynorCompiler compiler) => _compiler = compiler;
public async Task<ValidationResult> ValidateAsync(byte[] wasm, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
return await _compiler.PostAsync<ValidationResult>("/validate", new { wasm = wasmBase64 }, ct);
}
public async Task<bool> IsValidAsync(byte[] wasm, CancellationToken ct = default)
{
var result = await ValidateAsync(wasm, ct);
return result.Valid;
}
public async Task<List<string>> GetErrorsAsync(byte[] wasm, CancellationToken ct = default)
{
var result = await ValidateAsync(wasm, ct);
return result.Errors?.ConvertAll(e => e.Message) ?? new List<string>();
}
public async Task<bool> ValidateExportsAsync(byte[] wasm, List<string> requiredExports, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
var result = await _compiler.PostAsync<ValidResponse>("/validate/exports", new
{
wasm = wasmBase64,
required_exports = requiredExports
}, ct);
return result.Valid;
}
public async Task<bool> ValidateMemoryAsync(byte[] wasm, int maxPages, CancellationToken ct = default)
{
var wasmBase64 = Convert.ToBase64String(wasm);
var result = await _compiler.PostAsync<ValidResponse>("/validate/memory", new
{
wasm = wasmBase64,
max_pages = maxPages
}, ct);
return result.Valid;
}
}
// Response helper types
internal record ContractsListResponse(List<CompilationResult>? Contracts);
internal record CodeResponse(string Code);
internal record DataResponse(string Data);
internal record ValuesResponse(object[]? Values);
internal record GasResponse(long Gas);
internal record SecurityResponse(SecurityAnalysis? Security);
internal record ValidResponse(bool Valid);

View file

@ -0,0 +1,345 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Synor.Compiler;
// ============================================================================
// Enumerations
// ============================================================================
/// <summary>Optimization level for WASM compilation.</summary>
public enum OptimizationLevel
{
/// <summary>No optimization.</summary>
None,
/// <summary>Basic optimizations (stripping only).</summary>
Basic,
/// <summary>Optimize for size (-Os equivalent).</summary>
Size,
/// <summary>Aggressive optimization (-O3 -Os equivalent).</summary>
Aggressive
}
public static class OptimizationLevelExtensions
{
public static string ToApiString(this OptimizationLevel level) => level switch
{
OptimizationLevel.None => "none",
OptimizationLevel.Basic => "basic",
OptimizationLevel.Size => "size",
OptimizationLevel.Aggressive => "aggressive",
_ => "size"
};
public static OptimizationLevel FromApiString(string value) => value switch
{
"none" => OptimizationLevel.None,
"basic" => OptimizationLevel.Basic,
"size" => OptimizationLevel.Size,
"aggressive" => OptimizationLevel.Aggressive,
_ => OptimizationLevel.Size
};
}
// ============================================================================
// Configuration
// ============================================================================
/// <summary>Strip options for WASM sections.</summary>
public record StripOptions
{
public bool StripDebug { get; init; } = true;
public bool StripProducers { get; init; } = true;
public bool StripNames { get; init; } = true;
public bool StripCustom { get; init; } = true;
public List<string> PreserveSections { get; init; } = new();
public bool StripUnused { get; init; } = true;
}
/// <summary>Compiler configuration.</summary>
public record CompilerConfig
{
public required string ApiKey { get; init; }
public string Endpoint { get; init; } = "https://compiler.synor.io/v1";
public int Timeout { get; init; } = 60000;
public int Retries { get; init; } = 3;
public bool Debug { get; init; } = false;
public OptimizationLevel DefaultOptimizationLevel { get; init; } = OptimizationLevel.Size;
public StripOptions? StripOptions { get; init; }
public int MaxContractSize { get; init; } = 256 * 1024;
public bool UseWasmOpt { get; init; } = true;
public bool Validate { get; init; } = true;
public bool ExtractMetadata { get; init; } = true;
public bool GenerateAbi { get; init; } = true;
}
/// <summary>Compilation request.</summary>
public record CompilationRequest
{
public OptimizationLevel? OptimizationLevel { get; init; }
public StripOptions? StripOptions { get; init; }
public bool? UseWasmOpt { get; init; }
public bool? Validate { get; init; }
public bool? ExtractMetadata { get; init; }
public bool? GenerateAbi { get; init; }
}
// ============================================================================
// Validation Types
// ============================================================================
/// <summary>Validation error.</summary>
public record ValidationError
{
public string Code { get; init; } = "";
public string Message { get; init; } = "";
public string? Location { get; init; }
}
/// <summary>Validation result.</summary>
public record ValidationResult
{
public bool Valid { get; init; }
public List<ValidationError>? Errors { get; init; }
public List<string>? Warnings { get; init; }
public int ExportCount { get; init; }
public int ImportCount { get; init; }
public int FunctionCount { get; init; }
public int[]? MemoryPages { get; init; }
}
// ============================================================================
// Metadata Types
// ============================================================================
/// <summary>Contract metadata.</summary>
public record ContractMetadata
{
public string? Name { get; init; }
public string? Version { get; init; }
public List<string>? Authors { get; init; }
public string? Description { get; init; }
public string? License { get; init; }
public string? Repository { get; init; }
public long? BuildTimestamp { get; init; }
public string? RustVersion { get; init; }
public string? SdkVersion { get; init; }
public Dictionary<string, string>? Custom { get; init; }
}
// ============================================================================
// ABI Types
// ============================================================================
/// <summary>Type information.</summary>
public record TypeInfo
{
public string Kind { get; init; } = "unknown";
public int? Size { get; init; }
public TypeInfo? Element { get; init; }
public TypeInfo? Inner { get; init; }
public List<TypeInfo>? Elements { get; init; }
public string? Name { get; init; }
public List<TypeField>? Fields { get; init; }
public string TypeName => Kind switch
{
"u8" or "u16" or "u32" or "u64" or "u128" or
"i8" or "i16" or "i32" or "i64" or "i128" or
"bool" or "string" or "bytes" or "address" or "hash256" => Kind,
"fixedBytes" => $"bytes{Size ?? 0}",
"array" => $"{Element?.TypeName ?? ""}[]",
"fixedArray" => $"{Element?.TypeName ?? ""}[{Size ?? 0}]",
"option" => $"{Inner?.TypeName ?? ""}?",
"tuple" => $"({string.Join(", ", Elements?.ConvertAll(e => e.TypeName) ?? new())})",
"struct" => Name ?? "struct",
_ => Name ?? "unknown"
};
}
/// <summary>Type field for structs.</summary>
public record TypeField(string Name, TypeInfo Type);
/// <summary>Parameter ABI.</summary>
public record ParamAbi
{
public string Name { get; init; } = "";
public TypeInfo Type { get; init; } = new();
public bool Indexed { get; init; }
}
/// <summary>Function ABI.</summary>
public record FunctionAbi
{
public string Name { get; init; } = "";
public string Selector { get; init; } = "";
public List<ParamAbi>? Inputs { get; init; }
public List<ParamAbi>? Outputs { get; init; }
public bool View { get; init; }
public bool Payable { get; init; }
public string? Doc { get; init; }
}
/// <summary>Event ABI.</summary>
public record EventAbi
{
public string Name { get; init; } = "";
public string Topic { get; init; } = "";
public List<ParamAbi>? Params { get; init; }
public string? Doc { get; init; }
}
/// <summary>Error ABI.</summary>
public record ErrorAbi
{
public string Name { get; init; } = "";
public string Selector { get; init; } = "";
public List<ParamAbi>? Params { get; init; }
public string? Doc { get; init; }
}
/// <summary>Contract ABI (Application Binary Interface).</summary>
public record ContractAbi
{
public string Name { get; init; } = "";
public string Version { get; init; } = "1.0.0";
public List<FunctionAbi>? Functions { get; init; }
public List<EventAbi>? Events { get; init; }
public List<ErrorAbi>? Errors { get; init; }
public FunctionAbi? FindFunction(string name) =>
Functions?.Find(f => f.Name == name);
public FunctionAbi? FindBySelector(string selector) =>
Functions?.Find(f => f.Selector == selector);
}
// ============================================================================
// Compilation Types
// ============================================================================
/// <summary>Compilation result.</summary>
public record CompilationResult
{
public string ContractId { get; init; } = "";
public string Code { get; init; } = "";
public string CodeHash { get; init; } = "";
public int OriginalSize { get; init; }
public int OptimizedSize { get; init; }
public double SizeReduction { get; init; }
public ContractMetadata? Metadata { get; init; }
public ContractAbi? Abi { get; init; }
public long EstimatedDeployGas { get; init; }
public ValidationResult? Validation { get; init; }
public List<string>? Warnings { get; init; }
public string SizeStats =>
$"Original: {OriginalSize} bytes, Optimized: {OptimizedSize} bytes, Reduction: {SizeReduction:F1}%";
public byte[] DecodeCode() => Convert.FromBase64String(Code);
}
// ============================================================================
// Analysis Types
// ============================================================================
/// <summary>Size breakdown.</summary>
public record SizeBreakdown
{
public int Code { get; init; }
public int Data { get; init; }
public int Types { get; init; }
public int Functions { get; init; }
public int Memory { get; init; }
public int Table { get; init; }
public int Exports { get; init; }
public int Imports { get; init; }
public int Custom { get; init; }
public int Total { get; init; }
}
/// <summary>Function analysis.</summary>
public record FunctionAnalysis
{
public string Name { get; init; } = "";
public int Size { get; init; }
public int InstructionCount { get; init; }
public int LocalCount { get; init; }
public bool Exported { get; init; }
public long EstimatedGas { get; init; }
}
/// <summary>Import analysis.</summary>
public record ImportAnalysis
{
public string Module { get; init; } = "";
public string Name { get; init; } = "";
public string Kind { get; init; } = "function";
public string? Signature { get; init; }
}
/// <summary>Security issue.</summary>
public record SecurityIssue
{
public string Severity { get; init; } = "low";
public string Type { get; init; } = "";
public string Description { get; init; } = "";
public string? Location { get; init; }
}
/// <summary>Security analysis.</summary>
public record SecurityAnalysis
{
public int Score { get; init; }
public List<SecurityIssue>? Issues { get; init; }
public List<string>? Recommendations { get; init; }
}
/// <summary>Gas analysis.</summary>
public record GasAnalysis
{
public long DeploymentGas { get; init; }
public Dictionary<string, long>? FunctionGas { get; init; }
public long MemoryInitGas { get; init; }
public long DataSectionGas { get; init; }
}
/// <summary>Contract analysis result.</summary>
public record ContractAnalysis
{
public SizeBreakdown? SizeBreakdown { get; init; }
public List<FunctionAnalysis>? Functions { get; init; }
public List<ImportAnalysis>? Imports { get; init; }
public SecurityAnalysis? Security { get; init; }
public GasAnalysis? GasAnalysis { get; init; }
}
// ============================================================================
// Error Types
// ============================================================================
/// <summary>Compiler exception.</summary>
public class CompilerException : Exception
{
public string? Code { get; }
public int? HttpStatus { get; }
public CompilerException(string message, string? code = null, int? httpStatus = null)
: base(message)
{
Code = code;
HttpStatus = httpStatus;
}
}
// ============================================================================
// Constants
// ============================================================================
/// <summary>Compiler constants.</summary>
public static class CompilerConstants
{
public const int MaxContractSize = 256 * 1024;
public const int MaxMemoryPages = 16;
}

View file

@ -0,0 +1,390 @@
/// Synor Compiler SDK Client
///
/// Smart contract compilation, optimization, and ABI generation.
library synor_compiler_client;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'types.dart';
export 'types.dart';
/// Synor Compiler SDK Client.
///
/// Smart contract compilation, optimization, and ABI generation.
///
/// Example:
/// ```dart
/// final compiler = SynorCompiler(CompilerConfig(apiKey: 'your-api-key'));
///
/// final result = await compiler.compile(wasmBytes);
/// print('Optimized size: ${result.optimizedSize} bytes');
/// ```
class SynorCompiler {
final CompilerConfig _config;
final http.Client _httpClient;
bool _closed = false;
late final ContractsClient contracts;
late final AbiClient abi;
late final AnalysisClient analysis;
late final ValidationClient validation;
SynorCompiler(this._config, {http.Client? httpClient})
: _httpClient = httpClient ?? http.Client() {
contracts = ContractsClient(this);
abi = AbiClient(this);
analysis = AnalysisClient(this);
validation = ValidationClient(this);
}
/// Get the default optimization level.
OptimizationLevel get defaultOptimizationLevel => _config.optimizationLevel;
/// Check service health.
Future<bool> healthCheck() async {
try {
final result = await _get('/health');
return result['status'] == 'healthy';
} catch (_) {
return false;
}
}
/// Get service info.
Future<Map<String, dynamic>> getInfo() async {
return await _get('/info');
}
/// Close the client.
void close() {
_closed = true;
_httpClient.close();
}
/// Check if client is closed.
bool get isClosed => _closed;
/// Compile WASM bytecode.
Future<CompilationResult> compile(
List<int> wasm, {
OptimizationLevel? optimizationLevel,
StripOptions? stripOptions,
bool? useWasmOpt,
bool? validate,
bool? extractMetadata,
bool? generateAbi,
}) async {
final wasmBase64 = base64Encode(wasm);
final body = {
'wasm': wasmBase64,
'optimization_level':
(optimizationLevel ?? _config.optimizationLevel).toApiString(),
'strip_options':
(stripOptions ?? _config.stripOptions ?? const StripOptions())
.toJson(),
'use_wasm_opt': useWasmOpt ?? _config.useWasmOpt,
'validate': validate ?? _config.validate,
'extract_metadata': extractMetadata ?? _config.extractMetadata,
'generate_abi': generateAbi ?? _config.generateAbi,
};
final result = await _post('/compile', body);
return CompilationResult.fromJson(result);
}
Future<Map<String, dynamic>> _get(String path) async {
_checkClosed();
final response = await _httpClient.get(
Uri.parse('${_config.endpoint}$path'),
headers: _headers,
);
return _handleResponse(response);
}
Future<Map<String, dynamic>> _post(
String path, Map<String, dynamic> body) async {
_checkClosed();
final response = await _httpClient.post(
Uri.parse('${_config.endpoint}$path'),
headers: _headers,
body: jsonEncode(body),
);
return _handleResponse(response);
}
Map<String, String> get _headers => {
'Authorization': 'Bearer ${_config.apiKey}',
'Content-Type': 'application/json',
'X-SDK-Version': 'flutter/0.1.0',
};
Map<String, dynamic> _handleResponse(http.Response response) {
final body = response.body;
if (response.statusCode >= 400) {
Map<String, dynamic> error = {};
try {
error = jsonDecode(body);
} catch (_) {
error = {'message': body.isEmpty ? 'HTTP ${response.statusCode}' : body};
}
throw CompilerError(
error['message'] ?? 'HTTP ${response.statusCode}',
code: error['code'],
httpStatus: response.statusCode,
);
}
return jsonDecode(body);
}
void _checkClosed() {
if (_closed) {
throw const CompilerError('Client has been closed', code: 'CLIENT_CLOSED');
}
}
}
/// Contracts sub-client.
class ContractsClient {
final SynorCompiler _compiler;
ContractsClient(this._compiler);
/// Compile WASM bytecode with default settings.
Future<CompilationResult> compile(
List<int> wasm, {
OptimizationLevel? optimizationLevel,
StripOptions? stripOptions,
bool? useWasmOpt,
bool? validate,
bool? extractMetadata,
bool? generateAbi,
}) {
return _compiler.compile(
wasm,
optimizationLevel: optimizationLevel,
stripOptions: stripOptions,
useWasmOpt: useWasmOpt,
validate: validate,
extractMetadata: extractMetadata,
generateAbi: generateAbi,
);
}
/// Compile with development settings.
Future<CompilationResult> compileDev(List<int> wasm) {
return _compiler.compile(
wasm,
optimizationLevel: OptimizationLevel.none,
useWasmOpt: false,
validate: true,
);
}
/// Compile with production settings.
Future<CompilationResult> compileProduction(List<int> wasm) {
return _compiler.compile(
wasm,
optimizationLevel: OptimizationLevel.aggressive,
useWasmOpt: true,
validate: true,
extractMetadata: true,
generateAbi: true,
);
}
/// Get a compiled contract by ID.
Future<CompilationResult> get(String contractId) async {
final result = await _compiler._get('/contracts/$contractId');
return CompilationResult.fromJson(result);
}
/// List compiled contracts.
Future<List<CompilationResult>> list({int? limit, int? offset}) async {
var path = '/contracts';
final params = <String>[];
if (limit != null) params.add('limit=$limit');
if (offset != null) params.add('offset=$offset');
if (params.isNotEmpty) path += '?${params.join('&')}';
final result = await _compiler._get(path);
return (result['contracts'] as List<dynamic>?)
?.map((c) => CompilationResult.fromJson(c))
.toList() ??
[];
}
/// Get optimized bytecode for a contract.
Future<List<int>> getCode(String contractId) async {
final result = await _compiler._get('/contracts/$contractId/code');
return base64Decode(result['code'] ?? '');
}
}
/// ABI sub-client.
class AbiClient {
final SynorCompiler _compiler;
AbiClient(this._compiler);
/// Extract ABI from WASM bytecode.
Future<ContractAbi> extract(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/abi/extract', {'wasm': wasmBase64});
return ContractAbi.fromJson(result);
}
/// Get ABI for a compiled contract.
Future<ContractAbi> get(String contractId) async {
final result = await _compiler._get('/contracts/$contractId/abi');
return ContractAbi.fromJson(result);
}
/// Encode function call.
Future<String> encodeCall(FunctionAbi functionAbi, List<dynamic> args) async {
final result = await _compiler._post('/abi/encode', {
'function': {
'name': functionAbi.name,
'selector': functionAbi.selector,
'inputs': functionAbi.inputs
.map((i) => {'name': i.name, 'type': {'kind': i.type.kind}})
.toList(),
'outputs': functionAbi.outputs
.map((o) => {'name': o.name, 'type': {'kind': o.type.kind}})
.toList(),
},
'args': args,
});
return result['data'] ?? '';
}
/// Decode function result.
Future<List<dynamic>> decodeResult(
FunctionAbi functionAbi, String data) async {
final result = await _compiler._post('/abi/decode', {
'function': {
'name': functionAbi.name,
'selector': functionAbi.selector,
'inputs': functionAbi.inputs
.map((i) => {'name': i.name, 'type': {'kind': i.type.kind}})
.toList(),
'outputs': functionAbi.outputs
.map((o) => {'name': o.name, 'type': {'kind': o.type.kind}})
.toList(),
},
'data': data,
});
return result['values'] ?? [];
}
}
/// Analysis sub-client.
class AnalysisClient {
final SynorCompiler _compiler;
AnalysisClient(this._compiler);
/// Analyze WASM bytecode.
Future<ContractAnalysis> analyze(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/analysis', {'wasm': wasmBase64});
return ContractAnalysis.fromJson(result);
}
/// Get analysis for a compiled contract.
Future<ContractAnalysis> get(String contractId) async {
final result = await _compiler._get('/contracts/$contractId/analysis');
return ContractAnalysis.fromJson(result);
}
/// Extract metadata from WASM bytecode.
Future<ContractMetadata> extractMetadata(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/analysis/metadata', {'wasm': wasmBase64});
return ContractMetadata.fromJson(result);
}
/// Extract source map from WASM bytecode.
Future<SourceMap?> extractSourceMap(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/analysis/source-map', {'wasm': wasmBase64});
final sourceMap = result['source_map'];
return sourceMap != null ? SourceMap.fromJson(sourceMap) : null;
}
/// Estimate gas for contract deployment.
Future<int> estimateDeployGas(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/analysis/estimate-gas', {'wasm': wasmBase64});
return result['gas'] ?? 0;
}
/// Get security analysis.
Future<SecurityAnalysis> securityScan(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/analysis/security', {'wasm': wasmBase64});
return SecurityAnalysis.fromJson(result['security'] ?? {});
}
}
/// Validation sub-client.
class ValidationClient {
final SynorCompiler _compiler;
ValidationClient(this._compiler);
/// Validate WASM bytecode against VM requirements.
Future<ValidationResult> validate(List<int> wasm) async {
final wasmBase64 = base64Encode(wasm);
final result =
await _compiler._post('/validate', {'wasm': wasmBase64});
return ValidationResult.fromJson(result);
}
/// Check if WASM is valid.
Future<bool> isValid(List<int> wasm) async {
final result = await validate(wasm);
return result.valid;
}
/// Get validation errors.
Future<List<String>> getErrors(List<int> wasm) async {
final result = await validate(wasm);
return result.errors.map((e) => e.message).toList();
}
/// Validate contract exports.
Future<bool> validateExports(
List<int> wasm, List<String> requiredExports) async {
final wasmBase64 = base64Encode(wasm);
final result = await _compiler._post('/validate/exports', {
'wasm': wasmBase64,
'required_exports': requiredExports,
});
return result['valid'] ?? false;
}
/// Validate memory limits.
Future<bool> validateMemory(List<int> wasm, int maxPages) async {
final wasmBase64 = base64Encode(wasm);
final result = await _compiler._post('/validate/memory', {
'wasm': wasmBase64,
'max_pages': maxPages,
});
return result['valid'] ?? false;
}
}

View file

@ -0,0 +1,788 @@
/// Synor Compiler SDK Types
///
/// Smart contract compilation, optimization, and ABI generation.
library synor_compiler_types;
/// Optimization level for WASM compilation.
enum OptimizationLevel {
/// No optimization.
none,
/// Basic optimizations (stripping only).
basic,
/// Optimize for size (-Os equivalent).
size,
/// Aggressive optimization (-O3 -Os equivalent).
aggressive,
}
extension OptimizationLevelExtension on OptimizationLevel {
String toApiString() {
switch (this) {
case OptimizationLevel.none:
return 'none';
case OptimizationLevel.basic:
return 'basic';
case OptimizationLevel.size:
return 'size';
case OptimizationLevel.aggressive:
return 'aggressive';
}
}
static OptimizationLevel fromApiString(String value) {
switch (value) {
case 'none':
return OptimizationLevel.none;
case 'basic':
return OptimizationLevel.basic;
case 'size':
return OptimizationLevel.size;
case 'aggressive':
return OptimizationLevel.aggressive;
default:
return OptimizationLevel.size;
}
}
}
/// Options for stripping WASM sections.
class StripOptions {
final bool stripDebug;
final bool stripProducers;
final bool stripNames;
final bool stripCustom;
final List<String> preserveSections;
final bool stripUnused;
const StripOptions({
this.stripDebug = true,
this.stripProducers = true,
this.stripNames = true,
this.stripCustom = true,
this.preserveSections = const [],
this.stripUnused = true,
});
factory StripOptions.fromJson(Map<String, dynamic> json) {
return StripOptions(
stripDebug: json['strip_debug'] ?? true,
stripProducers: json['strip_producers'] ?? true,
stripNames: json['strip_names'] ?? true,
stripCustom: json['strip_custom'] ?? true,
preserveSections: List<String>.from(json['preserve_sections'] ?? []),
stripUnused: json['strip_unused'] ?? true,
);
}
Map<String, dynamic> toJson() => {
'strip_debug': stripDebug,
'strip_producers': stripProducers,
'strip_names': stripNames,
'strip_custom': stripCustom,
'preserve_sections': preserveSections,
'strip_unused': stripUnused,
};
}
/// Compiler configuration.
class CompilerConfig {
final String apiKey;
final String endpoint;
final int timeout;
final int retries;
final bool debug;
final OptimizationLevel optimizationLevel;
final StripOptions? stripOptions;
final int maxContractSize;
final bool useWasmOpt;
final bool validate;
final bool extractMetadata;
final bool generateAbi;
const CompilerConfig({
required this.apiKey,
this.endpoint = 'https://compiler.synor.io/v1',
this.timeout = 60000,
this.retries = 3,
this.debug = false,
this.optimizationLevel = OptimizationLevel.size,
this.stripOptions,
this.maxContractSize = 256 * 1024,
this.useWasmOpt = true,
this.validate = true,
this.extractMetadata = true,
this.generateAbi = true,
});
}
/// Validation error.
class ValidationError {
final String code;
final String message;
final String? location;
const ValidationError({
required this.code,
required this.message,
this.location,
});
factory ValidationError.fromJson(Map<String, dynamic> json) {
return ValidationError(
code: json['code'] ?? '',
message: json['message'] ?? '',
location: json['location'],
);
}
}
/// Validation result.
class ValidationResult {
final bool valid;
final List<ValidationError> errors;
final List<String> warnings;
final int exportCount;
final int importCount;
final int functionCount;
final List<int?> memoryPages;
const ValidationResult({
required this.valid,
this.errors = const [],
this.warnings = const [],
this.exportCount = 0,
this.importCount = 0,
this.functionCount = 0,
this.memoryPages = const [0, null],
});
factory ValidationResult.fromJson(Map<String, dynamic> json) {
return ValidationResult(
valid: json['valid'] ?? false,
errors: (json['errors'] as List<dynamic>?)
?.map((e) => ValidationError.fromJson(e))
.toList() ??
[],
warnings: List<String>.from(json['warnings'] ?? []),
exportCount: json['export_count'] ?? 0,
importCount: json['import_count'] ?? 0,
functionCount: json['function_count'] ?? 0,
memoryPages: List<int?>.from(json['memory_pages'] ?? [0, null]),
);
}
}
/// Contract metadata.
class ContractMetadata {
final String? name;
final String? version;
final List<String> authors;
final String? description;
final String? license;
final String? repository;
final int? buildTimestamp;
final String? rustVersion;
final String? sdkVersion;
final Map<String, String> custom;
const ContractMetadata({
this.name,
this.version,
this.authors = const [],
this.description,
this.license,
this.repository,
this.buildTimestamp,
this.rustVersion,
this.sdkVersion,
this.custom = const {},
});
factory ContractMetadata.fromJson(Map<String, dynamic> json) {
return ContractMetadata(
name: json['name'],
version: json['version'],
authors: List<String>.from(json['authors'] ?? []),
description: json['description'],
license: json['license'],
repository: json['repository'],
buildTimestamp: json['build_timestamp'],
rustVersion: json['rust_version'],
sdkVersion: json['sdk_version'],
custom: Map<String, String>.from(json['custom'] ?? {}),
);
}
}
/// Type information.
class TypeInfo {
final String kind;
final int? size;
final TypeInfo? element;
final TypeInfo? inner;
final List<TypeInfo>? elements;
final String? name;
final List<TypeField>? fields;
const TypeInfo({
required this.kind,
this.size,
this.element,
this.inner,
this.elements,
this.name,
this.fields,
});
factory TypeInfo.fromJson(Map<String, dynamic> json) {
return TypeInfo(
kind: json['kind'] ?? 'unknown',
size: json['size'],
element: json['element'] != null ? TypeInfo.fromJson(json['element']) : null,
inner: json['inner'] != null ? TypeInfo.fromJson(json['inner']) : null,
elements: (json['elements'] as List<dynamic>?)
?.map((e) => TypeInfo.fromJson(e))
.toList(),
name: json['name'],
fields: (json['fields'] as List<dynamic>?)
?.map((f) => TypeField.fromJson(f))
.toList(),
);
}
String get typeName {
switch (kind) {
case 'u8':
case 'u16':
case 'u32':
case 'u64':
case 'u128':
case 'i8':
case 'i16':
case 'i32':
case 'i64':
case 'i128':
case 'bool':
case 'string':
case 'bytes':
case 'address':
case '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':
final names = elements?.map((e) => e.typeName).join(', ') ?? '';
return '($names)';
case 'struct':
return name ?? 'struct';
default:
return name ?? 'unknown';
}
}
}
/// Type field for structs.
class TypeField {
final String name;
final TypeInfo type;
const TypeField({required this.name, required this.type});
factory TypeField.fromJson(Map<String, dynamic> json) {
return TypeField(
name: json['name'] ?? '',
type: TypeInfo.fromJson(json['type'] ?? {'kind': 'unknown'}),
);
}
}
/// Parameter ABI.
class ParamAbi {
final String name;
final TypeInfo type;
final bool indexed;
const ParamAbi({
required this.name,
required this.type,
this.indexed = false,
});
factory ParamAbi.fromJson(Map<String, dynamic> json) {
return ParamAbi(
name: json['name'] ?? '',
type: TypeInfo.fromJson(json['type'] ?? {'kind': 'unknown'}),
indexed: json['indexed'] ?? false,
);
}
}
/// Function ABI.
class FunctionAbi {
final String name;
final String selector;
final List<ParamAbi> inputs;
final List<ParamAbi> outputs;
final bool view;
final bool payable;
final String? doc;
const FunctionAbi({
required this.name,
required this.selector,
this.inputs = const [],
this.outputs = const [],
this.view = false,
this.payable = false,
this.doc,
});
factory FunctionAbi.fromJson(Map<String, dynamic> json) {
return FunctionAbi(
name: json['name'] ?? '',
selector: json['selector'] ?? '',
inputs: (json['inputs'] as List<dynamic>?)
?.map((i) => ParamAbi.fromJson(i))
.toList() ??
[],
outputs: (json['outputs'] as List<dynamic>?)
?.map((o) => ParamAbi.fromJson(o))
.toList() ??
[],
view: json['view'] ?? false,
payable: json['payable'] ?? false,
doc: json['doc'],
);
}
}
/// Event ABI.
class EventAbi {
final String name;
final String topic;
final List<ParamAbi> params;
final String? doc;
const EventAbi({
required this.name,
required this.topic,
this.params = const [],
this.doc,
});
factory EventAbi.fromJson(Map<String, dynamic> json) {
return EventAbi(
name: json['name'] ?? '',
topic: json['topic'] ?? '',
params: (json['params'] as List<dynamic>?)
?.map((p) => ParamAbi.fromJson(p))
.toList() ??
[],
doc: json['doc'],
);
}
}
/// Error ABI.
class ErrorAbi {
final String name;
final String selector;
final List<ParamAbi> params;
final String? doc;
const ErrorAbi({
required this.name,
required this.selector,
this.params = const [],
this.doc,
});
factory ErrorAbi.fromJson(Map<String, dynamic> json) {
return ErrorAbi(
name: json['name'] ?? '',
selector: json['selector'] ?? '',
params: (json['params'] as List<dynamic>?)
?.map((p) => ParamAbi.fromJson(p))
.toList() ??
[],
doc: json['doc'],
);
}
}
/// Contract ABI (Application Binary Interface).
class ContractAbi {
final String name;
final String version;
final List<FunctionAbi> functions;
final List<EventAbi> events;
final List<ErrorAbi> errors;
const ContractAbi({
required this.name,
required this.version,
this.functions = const [],
this.events = const [],
this.errors = const [],
});
factory ContractAbi.fromJson(Map<String, dynamic> json) {
return ContractAbi(
name: json['name'] ?? '',
version: json['version'] ?? '1.0.0',
functions: (json['functions'] as List<dynamic>?)
?.map((f) => FunctionAbi.fromJson(f))
.toList() ??
[],
events: (json['events'] as List<dynamic>?)
?.map((e) => EventAbi.fromJson(e))
.toList() ??
[],
errors: (json['errors'] as List<dynamic>?)
?.map((e) => ErrorAbi.fromJson(e))
.toList() ??
[],
);
}
FunctionAbi? findFunction(String name) {
return functions.cast<FunctionAbi?>().firstWhere(
(f) => f?.name == name,
orElse: () => null,
);
}
FunctionAbi? findBySelector(String selector) {
return functions.cast<FunctionAbi?>().firstWhere(
(f) => f?.selector == selector,
orElse: () => null,
);
}
}
/// Compilation result.
class CompilationResult {
final String contractId;
final String code;
final String codeHash;
final int originalSize;
final int optimizedSize;
final double sizeReduction;
final ContractMetadata? metadata;
final ContractAbi? abi;
final int estimatedDeployGas;
final ValidationResult? validation;
final List<String> warnings;
const CompilationResult({
required this.contractId,
required this.code,
required this.codeHash,
required this.originalSize,
required this.optimizedSize,
required this.sizeReduction,
this.metadata,
this.abi,
this.estimatedDeployGas = 0,
this.validation,
this.warnings = const [],
});
factory CompilationResult.fromJson(Map<String, dynamic> json) {
return CompilationResult(
contractId: json['contract_id'] ?? '',
code: json['code'] ?? '',
codeHash: json['code_hash'] ?? '',
originalSize: json['original_size'] ?? 0,
optimizedSize: json['optimized_size'] ?? 0,
sizeReduction: (json['size_reduction'] ?? 0).toDouble(),
metadata: json['metadata'] != null
? ContractMetadata.fromJson(json['metadata'])
: null,
abi: json['abi'] != null ? ContractAbi.fromJson(json['abi']) : null,
estimatedDeployGas: json['estimated_deploy_gas'] ?? 0,
validation: json['validation'] != null
? ValidationResult.fromJson(json['validation'])
: null,
warnings: List<String>.from(json['warnings'] ?? []),
);
}
String get sizeStats =>
'Original: $originalSize bytes, Optimized: $optimizedSize bytes, Reduction: ${sizeReduction.toStringAsFixed(1)}%';
}
/// Source mapping entry.
class SourceMapping {
final int wasmOffset;
final int line;
final int column;
final String? function;
const SourceMapping({
required this.wasmOffset,
required this.line,
required this.column,
this.function,
});
factory SourceMapping.fromJson(Map<String, dynamic> json) {
return SourceMapping(
wasmOffset: json['wasm_offset'] ?? 0,
line: json['line'] ?? 0,
column: json['column'] ?? 0,
function: json['function'],
);
}
}
/// Source map.
class SourceMap {
final String file;
final List<SourceMapping> mappings;
const SourceMap({
required this.file,
this.mappings = const [],
});
factory SourceMap.fromJson(Map<String, dynamic> json) {
return SourceMap(
file: json['file'] ?? '',
mappings: (json['mappings'] as List<dynamic>?)
?.map((m) => SourceMapping.fromJson(m))
.toList() ??
[],
);
}
}
/// Size breakdown.
class SizeBreakdown {
final int code;
final int data;
final int types;
final int functions;
final int memory;
final int table;
final int exports;
final int imports;
final int custom;
final int total;
const SizeBreakdown({
this.code = 0,
this.data = 0,
this.types = 0,
this.functions = 0,
this.memory = 0,
this.table = 0,
this.exports = 0,
this.imports = 0,
this.custom = 0,
this.total = 0,
});
factory SizeBreakdown.fromJson(Map<String, dynamic> json) {
return SizeBreakdown(
code: json['code'] ?? 0,
data: json['data'] ?? 0,
types: json['types'] ?? 0,
functions: json['functions'] ?? 0,
memory: json['memory'] ?? 0,
table: json['table'] ?? 0,
exports: json['exports'] ?? 0,
imports: json['imports'] ?? 0,
custom: json['custom'] ?? 0,
total: json['total'] ?? 0,
);
}
}
/// Function analysis.
class FunctionAnalysis {
final String name;
final int size;
final int instructionCount;
final int localCount;
final bool exported;
final int estimatedGas;
const FunctionAnalysis({
required this.name,
required this.size,
required this.instructionCount,
required this.localCount,
required this.exported,
required this.estimatedGas,
});
factory FunctionAnalysis.fromJson(Map<String, dynamic> json) {
return FunctionAnalysis(
name: json['name'] ?? '',
size: json['size'] ?? 0,
instructionCount: json['instruction_count'] ?? 0,
localCount: json['local_count'] ?? 0,
exported: json['exported'] ?? false,
estimatedGas: json['estimated_gas'] ?? 0,
);
}
}
/// Import analysis.
class ImportAnalysis {
final String module;
final String name;
final String kind;
final String? signature;
const ImportAnalysis({
required this.module,
required this.name,
required this.kind,
this.signature,
});
factory ImportAnalysis.fromJson(Map<String, dynamic> json) {
return ImportAnalysis(
module: json['module'] ?? '',
name: json['name'] ?? '',
kind: json['kind'] ?? 'function',
signature: json['signature'],
);
}
}
/// Security issue.
class SecurityIssue {
final String severity;
final String type;
final String description;
final String? location;
const SecurityIssue({
required this.severity,
required this.type,
required this.description,
this.location,
});
factory SecurityIssue.fromJson(Map<String, dynamic> json) {
return SecurityIssue(
severity: json['severity'] ?? 'low',
type: json['type'] ?? '',
description: json['description'] ?? '',
location: json['location'],
);
}
}
/// Security analysis.
class SecurityAnalysis {
final int score;
final List<SecurityIssue> issues;
final List<String> recommendations;
const SecurityAnalysis({
required this.score,
this.issues = const [],
this.recommendations = const [],
});
factory SecurityAnalysis.fromJson(Map<String, dynamic> json) {
return SecurityAnalysis(
score: json['score'] ?? 0,
issues: (json['issues'] as List<dynamic>?)
?.map((i) => SecurityIssue.fromJson(i))
.toList() ??
[],
recommendations: List<String>.from(json['recommendations'] ?? []),
);
}
}
/// Gas analysis.
class GasAnalysis {
final int deploymentGas;
final Map<String, int> functionGas;
final int memoryInitGas;
final int dataSectionGas;
const GasAnalysis({
required this.deploymentGas,
this.functionGas = const {},
this.memoryInitGas = 0,
this.dataSectionGas = 0,
});
factory GasAnalysis.fromJson(Map<String, dynamic> json) {
return GasAnalysis(
deploymentGas: json['deployment_gas'] ?? 0,
functionGas: Map<String, int>.from(json['function_gas'] ?? {}),
memoryInitGas: json['memory_init_gas'] ?? 0,
dataSectionGas: json['data_section_gas'] ?? 0,
);
}
}
/// Contract analysis result.
class ContractAnalysis {
final SizeBreakdown sizeBreakdown;
final List<FunctionAnalysis> functions;
final List<ImportAnalysis> imports;
final SecurityAnalysis? security;
final GasAnalysis? gasAnalysis;
const ContractAnalysis({
required this.sizeBreakdown,
this.functions = const [],
this.imports = const [],
this.security,
this.gasAnalysis,
});
factory ContractAnalysis.fromJson(Map<String, dynamic> json) {
return ContractAnalysis(
sizeBreakdown: SizeBreakdown.fromJson(json['size_breakdown'] ?? {}),
functions: (json['functions'] as List<dynamic>?)
?.map((f) => FunctionAnalysis.fromJson(f))
.toList() ??
[],
imports: (json['imports'] as List<dynamic>?)
?.map((i) => ImportAnalysis.fromJson(i))
.toList() ??
[],
security: json['security'] != null
? SecurityAnalysis.fromJson(json['security'])
: null,
gasAnalysis: json['gas_analysis'] != null
? GasAnalysis.fromJson(json['gas_analysis'])
: null,
);
}
}
/// Compiler error.
class CompilerError implements Exception {
final String message;
final String? code;
final int? httpStatus;
const CompilerError(this.message, {this.code, this.httpStatus});
@override
String toString() => code != null ? '$message ($code)' : message;
}
/// Constants
const int maxContractSize = 256 * 1024; // 256 KB
const int maxMemoryPages = 16;

483
sdk/go/compiler/client.go Normal file
View file

@ -0,0 +1,483 @@
package compiler
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"sync/atomic"
"time"
)
// SynorCompiler is the main compiler client.
type SynorCompiler struct {
config CompilerConfig
httpClient *http.Client
closed atomic.Bool
// Sub-clients
Contracts *ContractsClient
Abi *AbiClient
Analysis *AnalysisClient
Validation *ValidationClient
}
// NewSynorCompiler creates a new compiler client.
func NewSynorCompiler(config CompilerConfig) *SynorCompiler {
if config.Endpoint == "" {
config.Endpoint = "https://compiler.synor.io/v1"
}
if config.Timeout == 0 {
config.Timeout = 60000
}
if config.OptimizationLevel == "" {
config.OptimizationLevel = OptimizationSize
}
c := &SynorCompiler{
config: config,
httpClient: &http.Client{
Timeout: time.Duration(config.Timeout) * time.Millisecond,
},
}
c.Contracts = &ContractsClient{compiler: c}
c.Abi = &AbiClient{compiler: c}
c.Analysis = &AnalysisClient{compiler: c}
c.Validation = &ValidationClient{compiler: c}
return c
}
// DefaultOptimizationLevel returns the default optimization level.
func (c *SynorCompiler) DefaultOptimizationLevel() OptimizationLevel {
return c.config.OptimizationLevel
}
// HealthCheck checks service health.
func (c *SynorCompiler) HealthCheck(ctx context.Context) (bool, error) {
var result map[string]interface{}
if err := c.get(ctx, "/health", &result); err != nil {
return false, nil
}
return result["status"] == "healthy", nil
}
// GetInfo returns service info.
func (c *SynorCompiler) GetInfo(ctx context.Context) (map[string]interface{}, error) {
var result map[string]interface{}
if err := c.get(ctx, "/info", &result); err != nil {
return nil, err
}
return result, nil
}
// Close closes the client.
func (c *SynorCompiler) Close() {
c.closed.Store(true)
}
// IsClosed returns whether the client is closed.
func (c *SynorCompiler) IsClosed() bool {
return c.closed.Load()
}
// Compile compiles WASM bytecode.
func (c *SynorCompiler) Compile(ctx context.Context, wasm []byte, opts *CompilationRequest) (*CompilationResult, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
body := map[string]interface{}{
"wasm": wasmBase64,
}
if opts != nil {
if opts.OptimizationLevel != nil {
body["optimization_level"] = *opts.OptimizationLevel
} else {
body["optimization_level"] = c.config.OptimizationLevel
}
if opts.StripOptions != nil {
body["strip_options"] = opts.StripOptions
}
if opts.UseWasmOpt != nil {
body["use_wasm_opt"] = *opts.UseWasmOpt
} else {
body["use_wasm_opt"] = c.config.UseWasmOpt
}
if opts.Validate != nil {
body["validate"] = *opts.Validate
} else {
body["validate"] = c.config.Validate
}
if opts.ExtractMetadata != nil {
body["extract_metadata"] = *opts.ExtractMetadata
} else {
body["extract_metadata"] = c.config.ExtractMetadata
}
if opts.GenerateAbi != nil {
body["generate_abi"] = *opts.GenerateAbi
} else {
body["generate_abi"] = c.config.GenerateAbi
}
} else {
body["optimization_level"] = c.config.OptimizationLevel
body["use_wasm_opt"] = c.config.UseWasmOpt
body["validate"] = c.config.Validate
body["extract_metadata"] = c.config.ExtractMetadata
body["generate_abi"] = c.config.GenerateAbi
}
var result CompilationResult
if err := c.post(ctx, "/compile", body, &result); err != nil {
return nil, err
}
return &result, nil
}
func (c *SynorCompiler) get(ctx context.Context, path string, result interface{}) error {
if c.closed.Load() {
return &CompilerError{Message: "Client has been closed", Code: "CLIENT_CLOSED"}
}
req, err := http.NewRequestWithContext(ctx, "GET", c.config.Endpoint+path, nil)
if err != nil {
return err
}
c.setHeaders(req)
return c.doRequest(req, result)
}
func (c *SynorCompiler) post(ctx context.Context, path string, body interface{}, result interface{}) error {
if c.closed.Load() {
return &CompilerError{Message: "Client has been closed", Code: "CLIENT_CLOSED"}
}
jsonBody, err := json.Marshal(body)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, "POST", c.config.Endpoint+path, bytes.NewReader(jsonBody))
if err != nil {
return err
}
c.setHeaders(req)
return c.doRequest(req, result)
}
func (c *SynorCompiler) setHeaders(req *http.Request) {
req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-SDK-Version", "go/0.1.0")
}
func (c *SynorCompiler) doRequest(req *http.Request, result interface{}) error {
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode >= 400 {
var errResp map[string]interface{}
if json.Unmarshal(respBody, &errResp) == nil {
msg, _ := errResp["message"].(string)
code, _ := errResp["code"].(string)
if msg == "" {
msg = fmt.Sprintf("HTTP %d", resp.StatusCode)
}
return &CompilerError{Message: msg, Code: code, HTTPStatus: resp.StatusCode}
}
return &CompilerError{
Message: fmt.Sprintf("HTTP %d: %s", resp.StatusCode, string(respBody)),
HTTPStatus: resp.StatusCode,
}
}
if result != nil && len(respBody) > 0 {
return json.Unmarshal(respBody, result)
}
return nil
}
// ContractsClient is the contracts sub-client.
type ContractsClient struct {
compiler *SynorCompiler
}
// Compile compiles WASM bytecode with default settings.
func (c *ContractsClient) Compile(ctx context.Context, wasm []byte, opts *CompilationRequest) (*CompilationResult, error) {
return c.compiler.Compile(ctx, wasm, opts)
}
// CompileDev compiles with development settings.
func (c *ContractsClient) CompileDev(ctx context.Context, wasm []byte) (*CompilationResult, error) {
level := OptimizationNone
useWasmOpt := false
validate := true
return c.compiler.Compile(ctx, wasm, &CompilationRequest{
OptimizationLevel: &level,
UseWasmOpt: &useWasmOpt,
Validate: &validate,
})
}
// CompileProduction compiles with production settings.
func (c *ContractsClient) CompileProduction(ctx context.Context, wasm []byte) (*CompilationResult, error) {
level := OptimizationAggressive
useWasmOpt := true
validate := true
extractMetadata := true
generateAbi := true
return c.compiler.Compile(ctx, wasm, &CompilationRequest{
OptimizationLevel: &level,
UseWasmOpt: &useWasmOpt,
Validate: &validate,
ExtractMetadata: &extractMetadata,
GenerateAbi: &generateAbi,
})
}
// Get gets a compiled contract by ID.
func (c *ContractsClient) Get(ctx context.Context, contractID string) (*CompilationResult, error) {
var result CompilationResult
if err := c.compiler.get(ctx, "/contracts/"+contractID, &result); err != nil {
return nil, err
}
return &result, nil
}
// List lists compiled contracts.
func (c *ContractsClient) List(ctx context.Context, limit, offset *int) ([]CompilationResult, error) {
path := "/contracts"
if limit != nil || offset != nil {
path += "?"
if limit != nil {
path += fmt.Sprintf("limit=%d", *limit)
if offset != nil {
path += "&"
}
}
if offset != nil {
path += fmt.Sprintf("offset=%d", *offset)
}
}
var result struct {
Contracts []CompilationResult `json:"contracts"`
}
if err := c.compiler.get(ctx, path, &result); err != nil {
return nil, err
}
return result.Contracts, nil
}
// GetCode gets optimized bytecode for a contract.
func (c *ContractsClient) GetCode(ctx context.Context, contractID string) ([]byte, error) {
var result struct {
Code string `json:"code"`
}
if err := c.compiler.get(ctx, "/contracts/"+contractID+"/code", &result); err != nil {
return nil, err
}
return base64.StdEncoding.DecodeString(result.Code)
}
// AbiClient is the ABI sub-client.
type AbiClient struct {
compiler *SynorCompiler
}
// Extract extracts ABI from WASM bytecode.
func (c *AbiClient) Extract(ctx context.Context, wasm []byte) (*ContractAbi, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result ContractAbi
if err := c.compiler.post(ctx, "/abi/extract", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return &result, nil
}
// Get gets ABI for a compiled contract.
func (c *AbiClient) Get(ctx context.Context, contractID string) (*ContractAbi, error) {
var result ContractAbi
if err := c.compiler.get(ctx, "/contracts/"+contractID+"/abi", &result); err != nil {
return nil, err
}
return &result, nil
}
// EncodeCall encodes a function call.
func (c *AbiClient) EncodeCall(ctx context.Context, functionAbi *FunctionAbi, args []interface{}) (string, error) {
var result struct {
Data string `json:"data"`
}
if err := c.compiler.post(ctx, "/abi/encode", map[string]interface{}{
"function": functionAbi,
"args": args,
}, &result); err != nil {
return "", err
}
return result.Data, nil
}
// DecodeResult decodes a function result.
func (c *AbiClient) DecodeResult(ctx context.Context, functionAbi *FunctionAbi, data string) ([]interface{}, error) {
var result struct {
Values []interface{} `json:"values"`
}
if err := c.compiler.post(ctx, "/abi/decode", map[string]interface{}{
"function": functionAbi,
"data": data,
}, &result); err != nil {
return nil, err
}
return result.Values, nil
}
// AnalysisClient is the analysis sub-client.
type AnalysisClient struct {
compiler *SynorCompiler
}
// Analyze analyzes WASM bytecode.
func (c *AnalysisClient) Analyze(ctx context.Context, wasm []byte) (*ContractAnalysis, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result ContractAnalysis
if err := c.compiler.post(ctx, "/analysis", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return &result, nil
}
// Get gets analysis for a compiled contract.
func (c *AnalysisClient) Get(ctx context.Context, contractID string) (*ContractAnalysis, error) {
var result ContractAnalysis
if err := c.compiler.get(ctx, "/contracts/"+contractID+"/analysis", &result); err != nil {
return nil, err
}
return &result, nil
}
// ExtractMetadata extracts metadata from WASM bytecode.
func (c *AnalysisClient) ExtractMetadata(ctx context.Context, wasm []byte) (*ContractMetadata, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result ContractMetadata
if err := c.compiler.post(ctx, "/analysis/metadata", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return &result, nil
}
// ExtractSourceMap extracts source map from WASM bytecode.
func (c *AnalysisClient) ExtractSourceMap(ctx context.Context, wasm []byte) (*SourceMap, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result struct {
SourceMap *SourceMap `json:"source_map"`
}
if err := c.compiler.post(ctx, "/analysis/source-map", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return result.SourceMap, nil
}
// EstimateDeployGas estimates gas for contract deployment.
func (c *AnalysisClient) EstimateDeployGas(ctx context.Context, wasm []byte) (int64, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result struct {
Gas int64 `json:"gas"`
}
if err := c.compiler.post(ctx, "/analysis/estimate-gas", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return 0, err
}
return result.Gas, nil
}
// SecurityScan performs security analysis.
func (c *AnalysisClient) SecurityScan(ctx context.Context, wasm []byte) (*SecurityAnalysis, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result struct {
Security SecurityAnalysis `json:"security"`
}
if err := c.compiler.post(ctx, "/analysis/security", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return &result.Security, nil
}
// ValidationClient is the validation sub-client.
type ValidationClient struct {
compiler *SynorCompiler
}
// Validate validates WASM bytecode against VM requirements.
func (c *ValidationClient) Validate(ctx context.Context, wasm []byte) (*ValidationResult, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result ValidationResult
if err := c.compiler.post(ctx, "/validate", map[string]string{"wasm": wasmBase64}, &result); err != nil {
return nil, err
}
return &result, nil
}
// IsValid checks if WASM is valid.
func (c *ValidationClient) IsValid(ctx context.Context, wasm []byte) (bool, error) {
result, err := c.Validate(ctx, wasm)
if err != nil {
return false, err
}
return result.Valid, nil
}
// GetErrors gets validation errors.
func (c *ValidationClient) GetErrors(ctx context.Context, wasm []byte) ([]string, error) {
result, err := c.Validate(ctx, wasm)
if err != nil {
return nil, err
}
errors := make([]string, len(result.Errors))
for i, e := range result.Errors {
errors[i] = e.Message
}
return errors, nil
}
// ValidateExports validates contract exports.
func (c *ValidationClient) ValidateExports(ctx context.Context, wasm []byte, requiredExports []string) (bool, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result struct {
Valid bool `json:"valid"`
}
if err := c.compiler.post(ctx, "/validate/exports", map[string]interface{}{
"wasm": wasmBase64,
"required_exports": requiredExports,
}, &result); err != nil {
return false, err
}
return result.Valid, nil
}
// ValidateMemory validates memory limits.
func (c *ValidationClient) ValidateMemory(ctx context.Context, wasm []byte, maxPages int) (bool, error) {
wasmBase64 := base64.StdEncoding.EncodeToString(wasm)
var result struct {
Valid bool `json:"valid"`
}
if err := c.compiler.post(ctx, "/validate/memory", map[string]interface{}{
"wasm": wasmBase64,
"max_pages": maxPages,
}, &result); err != nil {
return false, err
}
return result.Valid, nil
}

382
sdk/go/compiler/types.go Normal file
View file

@ -0,0 +1,382 @@
// Package compiler provides the Synor Compiler SDK for Go.
//
// Smart contract compilation, optimization, and ABI generation.
package compiler
import (
"encoding/json"
"fmt"
)
// OptimizationLevel represents the optimization level for WASM compilation.
type OptimizationLevel string
const (
OptimizationNone OptimizationLevel = "none"
OptimizationBasic OptimizationLevel = "basic"
OptimizationSize OptimizationLevel = "size"
OptimizationAggressive OptimizationLevel = "aggressive"
)
// StripOptions defines options for stripping WASM sections.
type StripOptions struct {
StripDebug bool `json:"strip_debug"`
StripProducers bool `json:"strip_producers"`
StripNames bool `json:"strip_names"`
StripCustom bool `json:"strip_custom"`
PreserveSections []string `json:"preserve_sections"`
StripUnused bool `json:"strip_unused"`
}
// DefaultStripOptions returns the default strip options.
func DefaultStripOptions() StripOptions {
return StripOptions{
StripDebug: true,
StripProducers: true,
StripNames: true,
StripCustom: true,
PreserveSections: []string{},
StripUnused: true,
}
}
// CompilerConfig holds the configuration for the compiler client.
type CompilerConfig struct {
APIKey string `json:"api_key"`
Endpoint string `json:"endpoint"`
Timeout int `json:"timeout"` // milliseconds
Retries int `json:"retries"`
Debug bool `json:"debug"`
OptimizationLevel OptimizationLevel `json:"optimization_level"`
StripOptions *StripOptions `json:"strip_options,omitempty"`
MaxContractSize int `json:"max_contract_size"`
UseWasmOpt bool `json:"use_wasm_opt"`
Validate bool `json:"validate"`
ExtractMetadata bool `json:"extract_metadata"`
GenerateAbi bool `json:"generate_abi"`
}
// DefaultConfig returns a default configuration.
func DefaultConfig(apiKey string) CompilerConfig {
return CompilerConfig{
APIKey: apiKey,
Endpoint: "https://compiler.synor.io/v1",
Timeout: 60000,
Retries: 3,
Debug: false,
OptimizationLevel: OptimizationSize,
StripOptions: nil,
MaxContractSize: 256 * 1024,
UseWasmOpt: true,
Validate: true,
ExtractMetadata: true,
GenerateAbi: true,
}
}
// CompilationRequest represents a compilation request.
type CompilationRequest struct {
Wasm string `json:"wasm"` // Base64 encoded
OptimizationLevel *OptimizationLevel `json:"optimization_level,omitempty"`
StripOptions *StripOptions `json:"strip_options,omitempty"`
UseWasmOpt *bool `json:"use_wasm_opt,omitempty"`
Validate *bool `json:"validate,omitempty"`
ExtractMetadata *bool `json:"extract_metadata,omitempty"`
GenerateAbi *bool `json:"generate_abi,omitempty"`
}
// ValidationError represents a validation error.
type ValidationError struct {
Code string `json:"code"`
Message string `json:"message"`
Location *string `json:"location,omitempty"`
}
// ValidationResult represents the result of validation.
type ValidationResult struct {
Valid bool `json:"valid"`
Errors []ValidationError `json:"errors"`
Warnings []string `json:"warnings"`
ExportCount int `json:"export_count"`
ImportCount int `json:"import_count"`
FunctionCount int `json:"function_count"`
MemoryPages [2]*int `json:"memory_pages"` // [min, max]
}
// ContractMetadata represents contract metadata.
type ContractMetadata struct {
Name *string `json:"name,omitempty"`
Version *string `json:"version,omitempty"`
Authors []string `json:"authors"`
Description *string `json:"description,omitempty"`
License *string `json:"license,omitempty"`
Repository *string `json:"repository,omitempty"`
BuildTimestamp *int64 `json:"build_timestamp,omitempty"`
RustVersion *string `json:"rust_version,omitempty"`
SdkVersion *string `json:"sdk_version,omitempty"`
Custom map[string]string `json:"custom"`
}
// TypeInfo represents type information.
type TypeInfo struct {
Kind string `json:"kind"`
Size *int `json:"size,omitempty"`
Element *TypeInfo `json:"element,omitempty"`
Inner *TypeInfo `json:"inner,omitempty"`
Elements []TypeInfo `json:"elements,omitempty"`
Name *string `json:"name,omitempty"`
Fields []TypeField `json:"fields,omitempty"`
}
// TypeField represents a struct field.
type TypeField struct {
Name string `json:"name"`
Type TypeInfo `json:"type"`
}
// TypeName returns the type name as a string.
func (t TypeInfo) TypeName() string {
switch t.Kind {
case "u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128",
"bool", "string", "bytes", "address", "hash256":
return t.Kind
case "fixedBytes":
if t.Size != nil {
return fmt.Sprintf("bytes%d", *t.Size)
}
return "bytes"
case "array":
if t.Element != nil {
return t.Element.TypeName() + "[]"
}
return "[]"
case "fixedArray":
if t.Element != nil && t.Size != nil {
return fmt.Sprintf("%s[%d]", t.Element.TypeName(), *t.Size)
}
return "[]"
case "option":
if t.Inner != nil {
return t.Inner.TypeName() + "?"
}
return "?"
case "tuple":
names := make([]string, len(t.Elements))
for i, e := range t.Elements {
names[i] = e.TypeName()
}
return "(" + join(names, ", ") + ")"
case "struct":
if t.Name != nil {
return *t.Name
}
return "struct"
default:
if t.Name != nil {
return *t.Name
}
return "unknown"
}
}
func join(strs []string, sep string) string {
if len(strs) == 0 {
return ""
}
result := strs[0]
for i := 1; i < len(strs); i++ {
result += sep + strs[i]
}
return result
}
// ParamAbi represents a parameter ABI.
type ParamAbi struct {
Name string `json:"name"`
Type TypeInfo `json:"type"`
Indexed bool `json:"indexed"`
}
// FunctionAbi represents a function ABI.
type FunctionAbi struct {
Name string `json:"name"`
Selector string `json:"selector"`
Inputs []ParamAbi `json:"inputs"`
Outputs []ParamAbi `json:"outputs"`
View bool `json:"view"`
Payable bool `json:"payable"`
Doc *string `json:"doc,omitempty"`
}
// EventAbi represents an event ABI.
type EventAbi struct {
Name string `json:"name"`
Topic string `json:"topic"`
Params []ParamAbi `json:"params"`
Doc *string `json:"doc,omitempty"`
}
// ErrorAbi represents an error ABI.
type ErrorAbi struct {
Name string `json:"name"`
Selector string `json:"selector"`
Params []ParamAbi `json:"params"`
Doc *string `json:"doc,omitempty"`
}
// ContractAbi represents the contract ABI.
type ContractAbi struct {
Name string `json:"name"`
Version string `json:"version"`
Functions []FunctionAbi `json:"functions"`
Events []EventAbi `json:"events"`
Errors []ErrorAbi `json:"errors"`
}
// FindFunction finds a function by name.
func (a *ContractAbi) FindFunction(name string) *FunctionAbi {
for i := range a.Functions {
if a.Functions[i].Name == name {
return &a.Functions[i]
}
}
return nil
}
// FindBySelector finds a function by selector.
func (a *ContractAbi) FindBySelector(selector string) *FunctionAbi {
for i := range a.Functions {
if a.Functions[i].Selector == selector {
return &a.Functions[i]
}
}
return nil
}
// ToJSON serializes the ABI to JSON.
func (a *ContractAbi) ToJSON() (string, error) {
data, err := json.MarshalIndent(a, "", " ")
if err != nil {
return "", err
}
return string(data), nil
}
// CompilationResult represents the result of compilation.
type CompilationResult struct {
ContractID string `json:"contract_id"`
Code string `json:"code"` // Base64 encoded
CodeHash string `json:"code_hash"`
OriginalSize int `json:"original_size"`
OptimizedSize int `json:"optimized_size"`
SizeReduction float64 `json:"size_reduction"`
Metadata *ContractMetadata `json:"metadata,omitempty"`
Abi *ContractAbi `json:"abi,omitempty"`
EstimatedDeployGas int64 `json:"estimated_deploy_gas"`
Validation *ValidationResult `json:"validation,omitempty"`
Warnings []string `json:"warnings"`
}
// SizeStats returns size statistics as a formatted string.
func (r *CompilationResult) SizeStats() string {
return fmt.Sprintf("Original: %d bytes, Optimized: %d bytes, Reduction: %.1f%%",
r.OriginalSize, r.OptimizedSize, r.SizeReduction)
}
// SourceMapping represents a source mapping entry.
type SourceMapping struct {
WasmOffset int `json:"wasm_offset"`
Line int `json:"line"`
Column int `json:"column"`
Function *string `json:"function,omitempty"`
}
// SourceMap represents a source map.
type SourceMap struct {
File string `json:"file"`
Mappings []SourceMapping `json:"mappings"`
}
// SizeBreakdown represents size breakdown.
type SizeBreakdown struct {
Code int `json:"code"`
Data int `json:"data"`
Types int `json:"types"`
Functions int `json:"functions"`
Memory int `json:"memory"`
Table int `json:"table"`
Exports int `json:"exports"`
Imports int `json:"imports"`
Custom int `json:"custom"`
Total int `json:"total"`
}
// FunctionAnalysis represents function analysis.
type FunctionAnalysis struct {
Name string `json:"name"`
Size int `json:"size"`
InstructionCount int `json:"instruction_count"`
LocalCount int `json:"local_count"`
Exported bool `json:"exported"`
EstimatedGas int64 `json:"estimated_gas"`
}
// ImportAnalysis represents import analysis.
type ImportAnalysis struct {
Module string `json:"module"`
Name string `json:"name"`
Kind string `json:"kind"` // function, memory, table, global
Signature *string `json:"signature,omitempty"`
}
// SecurityIssue represents a security issue.
type SecurityIssue struct {
Severity string `json:"severity"` // low, medium, high, critical
Type string `json:"type"`
Description string `json:"description"`
Location *string `json:"location,omitempty"`
}
// SecurityAnalysis represents security analysis.
type SecurityAnalysis struct {
Score int `json:"score"` // 0-100
Issues []SecurityIssue `json:"issues"`
Recommendations []string `json:"recommendations"`
}
// GasAnalysis represents gas analysis.
type GasAnalysis struct {
DeploymentGas int64 `json:"deployment_gas"`
FunctionGas map[string]int `json:"function_gas"`
MemoryInitGas int64 `json:"memory_init_gas"`
DataSectionGas int64 `json:"data_section_gas"`
}
// ContractAnalysis represents contract analysis result.
type ContractAnalysis struct {
SizeBreakdown SizeBreakdown `json:"size_breakdown"`
Functions []FunctionAnalysis `json:"functions"`
Imports []ImportAnalysis `json:"imports"`
Security *SecurityAnalysis `json:"security,omitempty"`
GasAnalysis *GasAnalysis `json:"gas_analysis,omitempty"`
}
// CompilerError represents a compiler error.
type CompilerError struct {
Message string
Code string
HTTPStatus int
}
func (e *CompilerError) Error() string {
if e.Code != "" {
return fmt.Sprintf("%s (%s)", e.Message, e.Code)
}
return e.Message
}
// Constants
const (
MaxContractSize = 256 * 1024 // 256 KB
MaxMemoryPages = 16
)

View file

@ -0,0 +1,329 @@
package io.synor.compiler;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
/**
* Synor Compiler SDK Client
*
* Smart contract compilation, optimization, and ABI generation.
*
* Example:
* <pre>{@code
* var config = new CompilerConfig("your-api-key");
* var compiler = new SynorCompiler(config);
*
* var result = compiler.compile(wasmBytes).join();
* System.out.println("Optimized size: " + result.getOptimizedSize() + " bytes");
* }</pre>
*/
public class SynorCompiler implements AutoCloseable {
private final CompilerConfig config;
private final HttpClient httpClient;
private final Gson gson;
private final AtomicBoolean closed = new AtomicBoolean(false);
public final ContractsClient contracts;
public final AbiClient abi;
public final AnalysisClient analysis;
public final ValidationClient validation;
public SynorCompiler(CompilerConfig config) {
this.config = config;
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(config.getTimeout()))
.build();
this.gson = new GsonBuilder()
.setFieldNamingPolicy(com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
this.contracts = new ContractsClient(this);
this.abi = new AbiClient(this);
this.analysis = new AnalysisClient(this);
this.validation = new ValidationClient(this);
}
public OptimizationLevel getDefaultOptimizationLevel() {
return config.getOptimizationLevel();
}
public CompletableFuture<Boolean> healthCheck() {
return get("/health", new TypeToken<Map<String, Object>>() {})
.thenApply(result -> "healthy".equals(result.get("status")))
.exceptionally(e -> false);
}
public CompletableFuture<Map<String, Object>> getInfo() {
return get("/info", new TypeToken<Map<String, Object>>() {});
}
@Override
public void close() {
closed.set(true);
}
public boolean isClosed() {
return closed.get();
}
public CompletableFuture<CompilationResult> compile(byte[] wasm) {
return compile(wasm, null);
}
public CompletableFuture<CompilationResult> compile(byte[] wasm, CompilationRequest request) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
Map<String, Object> body = new HashMap<>();
body.put("wasm", wasmBase64);
body.put("optimization_level", (request != null && request.getOptimizationLevel() != null
? request.getOptimizationLevel() : config.getOptimizationLevel()).toApiString());
body.put("use_wasm_opt", request != null && request.getUseWasmOpt() != null
? request.getUseWasmOpt() : config.isUseWasmOpt());
body.put("validate", request != null && request.getValidate() != null
? request.getValidate() : config.isValidate());
body.put("extract_metadata", request != null && request.getExtractMetadata() != null
? request.getExtractMetadata() : config.isExtractMetadata());
body.put("generate_abi", request != null && request.getGenerateAbi() != null
? request.getGenerateAbi() : config.isGenerateAbi());
if (request != null && request.getStripOptions() != null) {
body.put("strip_options", request.getStripOptions().toMap());
}
return post("/compile", body, new TypeToken<CompilationResult>() {});
}
<T> CompletableFuture<T> get(String path, TypeToken<T> typeToken) {
checkClosed();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(config.getEndpoint() + path))
.header("Authorization", "Bearer " + config.getApiKey())
.header("Content-Type", "application/json")
.header("X-SDK-Version", "java/0.1.0")
.GET()
.timeout(Duration.ofMillis(config.getTimeout()))
.build();
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> handleResponse(response, typeToken));
}
<T> CompletableFuture<T> post(String path, Object body, TypeToken<T> typeToken) {
checkClosed();
String jsonBody = gson.toJson(body);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(config.getEndpoint() + path))
.header("Authorization", "Bearer " + config.getApiKey())
.header("Content-Type", "application/json")
.header("X-SDK-Version", "java/0.1.0")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.timeout(Duration.ofMillis(config.getTimeout()))
.build();
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> handleResponse(response, typeToken));
}
private <T> T handleResponse(HttpResponse<String> response, TypeToken<T> typeToken) {
if (response.statusCode() >= 400) {
Map<String, Object> error;
try {
error = gson.fromJson(response.body(), new TypeToken<Map<String, Object>>() {}.getType());
} catch (Exception e) {
error = Map.of("message", response.body().isEmpty() ? "HTTP " + response.statusCode() : response.body());
}
throw new CompilerException(
(String) error.getOrDefault("message", "HTTP " + response.statusCode()),
(String) error.get("code"),
response.statusCode()
);
}
return gson.fromJson(response.body(), typeToken.getType());
}
private void checkClosed() {
if (closed.get()) {
throw new CompilerException("Client has been closed", "CLIENT_CLOSED", null);
}
}
Gson getGson() {
return gson;
}
}
class ContractsClient {
private final SynorCompiler compiler;
ContractsClient(SynorCompiler compiler) {
this.compiler = compiler;
}
public CompletableFuture<CompilationResult> compile(byte[] wasm) {
return compiler.compile(wasm, null);
}
public CompletableFuture<CompilationResult> compile(byte[] wasm, CompilationRequest request) {
return compiler.compile(wasm, request);
}
public CompletableFuture<CompilationResult> compileDev(byte[] wasm) {
CompilationRequest request = new CompilationRequest();
request.setOptimizationLevel(OptimizationLevel.NONE);
request.setUseWasmOpt(false);
request.setValidate(true);
return compiler.compile(wasm, request);
}
public CompletableFuture<CompilationResult> compileProduction(byte[] wasm) {
CompilationRequest request = new CompilationRequest();
request.setOptimizationLevel(OptimizationLevel.AGGRESSIVE);
request.setUseWasmOpt(true);
request.setValidate(true);
request.setExtractMetadata(true);
request.setGenerateAbi(true);
return compiler.compile(wasm, request);
}
public CompletableFuture<CompilationResult> get(String contractId) {
return compiler.get("/contracts/" + contractId, new TypeToken<CompilationResult>() {});
}
public CompletableFuture<List<CompilationResult>> list(Integer limit, Integer offset) {
StringBuilder path = new StringBuilder("/contracts");
List<String> params = new ArrayList<>();
if (limit != null) params.add("limit=" + limit);
if (offset != null) params.add("offset=" + offset);
if (!params.isEmpty()) path.append("?").append(String.join("&", params));
return compiler.get(path.toString(), new TypeToken<Map<String, List<CompilationResult>>>() {})
.thenApply(result -> result.getOrDefault("contracts", List.of()));
}
public CompletableFuture<byte[]> getCode(String contractId) {
return compiler.get("/contracts/" + contractId + "/code", new TypeToken<Map<String, String>>() {})
.thenApply(result -> Base64.getDecoder().decode(result.getOrDefault("code", "")));
}
}
class AbiClient {
private final SynorCompiler compiler;
AbiClient(SynorCompiler compiler) {
this.compiler = compiler;
}
public CompletableFuture<ContractAbi> extract(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/abi/extract", Map.of("wasm", wasmBase64), new TypeToken<ContractAbi>() {});
}
public CompletableFuture<ContractAbi> get(String contractId) {
return compiler.get("/contracts/" + contractId + "/abi", new TypeToken<ContractAbi>() {});
}
public CompletableFuture<String> encodeCall(FunctionAbi functionAbi, List<Object> args) {
Map<String, Object> body = new HashMap<>();
body.put("function", functionAbi);
body.put("args", args);
return compiler.post("/abi/encode", body, new TypeToken<Map<String, String>>() {})
.thenApply(result -> result.getOrDefault("data", ""));
}
public CompletableFuture<List<Object>> decodeResult(FunctionAbi functionAbi, String data) {
Map<String, Object> body = new HashMap<>();
body.put("function", functionAbi);
body.put("data", data);
return compiler.post("/abi/decode", body, new TypeToken<Map<String, List<Object>>>() {})
.thenApply(result -> result.getOrDefault("values", List.of()));
}
}
class AnalysisClient {
private final SynorCompiler compiler;
AnalysisClient(SynorCompiler compiler) {
this.compiler = compiler;
}
public CompletableFuture<ContractAnalysis> analyze(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/analysis", Map.of("wasm", wasmBase64), new TypeToken<ContractAnalysis>() {});
}
public CompletableFuture<ContractAnalysis> get(String contractId) {
return compiler.get("/contracts/" + contractId + "/analysis", new TypeToken<ContractAnalysis>() {});
}
public CompletableFuture<ContractMetadata> extractMetadata(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/analysis/metadata", Map.of("wasm", wasmBase64), new TypeToken<ContractMetadata>() {});
}
public CompletableFuture<Long> estimateDeployGas(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/analysis/estimate-gas", Map.of("wasm", wasmBase64), new TypeToken<Map<String, Long>>() {})
.thenApply(result -> result.getOrDefault("gas", 0L));
}
public CompletableFuture<SecurityAnalysis> securityScan(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/analysis/security", Map.of("wasm", wasmBase64), new TypeToken<Map<String, SecurityAnalysis>>() {})
.thenApply(result -> result.get("security"));
}
}
class ValidationClient {
private final SynorCompiler compiler;
ValidationClient(SynorCompiler compiler) {
this.compiler = compiler;
}
public CompletableFuture<ValidationResult> validate(byte[] wasm) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
return compiler.post("/validate", Map.of("wasm", wasmBase64), new TypeToken<ValidationResult>() {});
}
public CompletableFuture<Boolean> isValid(byte[] wasm) {
return validate(wasm).thenApply(ValidationResult::isValid);
}
public CompletableFuture<List<String>> getErrors(byte[] wasm) {
return validate(wasm).thenApply(result ->
result.getErrors().stream().map(ValidationError::getMessage).toList()
);
}
public CompletableFuture<Boolean> validateExports(byte[] wasm, List<String> requiredExports) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
Map<String, Object> body = new HashMap<>();
body.put("wasm", wasmBase64);
body.put("required_exports", requiredExports);
return compiler.post("/validate/exports", body, new TypeToken<Map<String, Boolean>>() {})
.thenApply(result -> result.getOrDefault("valid", false));
}
public CompletableFuture<Boolean> validateMemory(byte[] wasm, int maxPages) {
String wasmBase64 = Base64.getEncoder().encodeToString(wasm);
Map<String, Object> body = new HashMap<>();
body.put("wasm", wasmBase64);
body.put("max_pages", maxPages);
return compiler.post("/validate/memory", body, new TypeToken<Map<String, Boolean>>() {})
.thenApply(result -> result.getOrDefault("valid", false));
}
}

View file

@ -0,0 +1,494 @@
package io.synor.compiler;
import java.util.*;
/**
* Synor Compiler SDK Types
*/
// Enumerations
enum OptimizationLevel {
NONE("none"),
BASIC("basic"),
SIZE("size"),
AGGRESSIVE("aggressive");
private final String value;
OptimizationLevel(String value) {
this.value = value;
}
public String toApiString() {
return value;
}
public static OptimizationLevel fromApiString(String value) {
for (OptimizationLevel level : values()) {
if (level.value.equals(value)) {
return level;
}
}
return SIZE;
}
}
// Configuration
class StripOptions {
private boolean stripDebug = true;
private boolean stripProducers = true;
private boolean stripNames = true;
private boolean stripCustom = true;
private List<String> preserveSections = new ArrayList<>();
private boolean stripUnused = true;
public boolean isStripDebug() { return stripDebug; }
public void setStripDebug(boolean stripDebug) { this.stripDebug = stripDebug; }
public boolean isStripProducers() { return stripProducers; }
public void setStripProducers(boolean stripProducers) { this.stripProducers = stripProducers; }
public boolean isStripNames() { return stripNames; }
public void setStripNames(boolean stripNames) { this.stripNames = stripNames; }
public boolean isStripCustom() { return stripCustom; }
public void setStripCustom(boolean stripCustom) { this.stripCustom = stripCustom; }
public List<String> getPreserveSections() { return preserveSections; }
public void setPreserveSections(List<String> preserveSections) { this.preserveSections = preserveSections; }
public boolean isStripUnused() { return stripUnused; }
public void setStripUnused(boolean stripUnused) { this.stripUnused = stripUnused; }
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("strip_debug", stripDebug);
map.put("strip_producers", stripProducers);
map.put("strip_names", stripNames);
map.put("strip_custom", stripCustom);
map.put("preserve_sections", preserveSections);
map.put("strip_unused", stripUnused);
return map;
}
}
class CompilerConfig {
private final String apiKey;
private String endpoint = "https://compiler.synor.io/v1";
private long timeout = 60000;
private int retries = 3;
private boolean debug = false;
private OptimizationLevel optimizationLevel = OptimizationLevel.SIZE;
private StripOptions stripOptions;
private int maxContractSize = 256 * 1024;
private boolean useWasmOpt = true;
private boolean validate = true;
private boolean extractMetadata = true;
private boolean generateAbi = true;
public CompilerConfig(String apiKey) {
this.apiKey = apiKey;
}
public String getApiKey() { return apiKey; }
public String getEndpoint() { return endpoint; }
public void setEndpoint(String endpoint) { this.endpoint = endpoint; }
public long getTimeout() { return timeout; }
public void setTimeout(long timeout) { this.timeout = timeout; }
public int getRetries() { return retries; }
public void setRetries(int retries) { this.retries = retries; }
public boolean isDebug() { return debug; }
public void setDebug(boolean debug) { this.debug = debug; }
public OptimizationLevel getOptimizationLevel() { return optimizationLevel; }
public void setOptimizationLevel(OptimizationLevel level) { this.optimizationLevel = level; }
public StripOptions getStripOptions() { return stripOptions; }
public void setStripOptions(StripOptions stripOptions) { this.stripOptions = stripOptions; }
public int getMaxContractSize() { return maxContractSize; }
public void setMaxContractSize(int maxContractSize) { this.maxContractSize = maxContractSize; }
public boolean isUseWasmOpt() { return useWasmOpt; }
public void setUseWasmOpt(boolean useWasmOpt) { this.useWasmOpt = useWasmOpt; }
public boolean isValidate() { return validate; }
public void setValidate(boolean validate) { this.validate = validate; }
public boolean isExtractMetadata() { return extractMetadata; }
public void setExtractMetadata(boolean extractMetadata) { this.extractMetadata = extractMetadata; }
public boolean isGenerateAbi() { return generateAbi; }
public void setGenerateAbi(boolean generateAbi) { this.generateAbi = generateAbi; }
}
class CompilationRequest {
private OptimizationLevel optimizationLevel;
private StripOptions stripOptions;
private Boolean useWasmOpt;
private Boolean validate;
private Boolean extractMetadata;
private Boolean generateAbi;
public OptimizationLevel getOptimizationLevel() { return optimizationLevel; }
public void setOptimizationLevel(OptimizationLevel optimizationLevel) { this.optimizationLevel = optimizationLevel; }
public StripOptions getStripOptions() { return stripOptions; }
public void setStripOptions(StripOptions stripOptions) { this.stripOptions = stripOptions; }
public Boolean getUseWasmOpt() { return useWasmOpt; }
public void setUseWasmOpt(Boolean useWasmOpt) { this.useWasmOpt = useWasmOpt; }
public Boolean getValidate() { return validate; }
public void setValidate(Boolean validate) { this.validate = validate; }
public Boolean getExtractMetadata() { return extractMetadata; }
public void setExtractMetadata(Boolean extractMetadata) { this.extractMetadata = extractMetadata; }
public Boolean getGenerateAbi() { return generateAbi; }
public void setGenerateAbi(Boolean generateAbi) { this.generateAbi = generateAbi; }
}
// Validation
class ValidationError {
private String code;
private String message;
private String location;
public String getCode() { return code; }
public String getMessage() { return message; }
public String getLocation() { return location; }
}
class ValidationResult {
private boolean valid;
private List<ValidationError> errors = new ArrayList<>();
private List<String> warnings = new ArrayList<>();
private int exportCount;
private int importCount;
private int functionCount;
private int[] memoryPages = {0, 0};
public boolean isValid() { return valid; }
public List<ValidationError> getErrors() { return errors; }
public List<String> getWarnings() { return warnings; }
public int getExportCount() { return exportCount; }
public int getImportCount() { return importCount; }
public int getFunctionCount() { return functionCount; }
public int[] getMemoryPages() { return memoryPages; }
}
// Metadata
class ContractMetadata {
private String name;
private String version;
private List<String> authors = new ArrayList<>();
private String description;
private String license;
private String repository;
private Long buildTimestamp;
private String rustVersion;
private String sdkVersion;
private Map<String, String> custom = new HashMap<>();
public String getName() { return name; }
public String getVersion() { return version; }
public List<String> getAuthors() { return authors; }
public String getDescription() { return description; }
public String getLicense() { return license; }
public String getRepository() { return repository; }
public Long getBuildTimestamp() { return buildTimestamp; }
public String getRustVersion() { return rustVersion; }
public String getSdkVersion() { return sdkVersion; }
public Map<String, String> getCustom() { return custom; }
}
// ABI Types
class TypeInfo {
private String kind;
private Integer size;
private TypeInfo element;
private TypeInfo inner;
private List<TypeInfo> elements;
private String name;
private List<TypeField> fields;
public String getKind() { return kind; }
public Integer getSize() { return size; }
public TypeInfo getElement() { return element; }
public TypeInfo getInner() { return inner; }
public List<TypeInfo> getElements() { return elements; }
public String getName() { return name; }
public List<TypeField> getFields() { return fields; }
public String getTypeName() {
switch (kind) {
case "u8": case "u16": case "u32": case "u64": case "u128":
case "i8": case "i16": case "i32": case "i64": case "i128":
case "bool": case "string": case "bytes": case "address": case "hash256":
return kind;
case "fixedBytes":
return "bytes" + (size != null ? size : 0);
case "array":
return (element != null ? element.getTypeName() : "") + "[]";
case "fixedArray":
return (element != null ? element.getTypeName() : "") + "[" + (size != null ? size : 0) + "]";
case "option":
return (inner != null ? inner.getTypeName() : "") + "?";
case "tuple":
if (elements != null) {
StringBuilder sb = new StringBuilder("(");
for (int i = 0; i < elements.size(); i++) {
if (i > 0) sb.append(", ");
sb.append(elements.get(i).getTypeName());
}
sb.append(")");
return sb.toString();
}
return "()";
case "struct":
return name != null ? name : "struct";
default:
return name != null ? name : "unknown";
}
}
}
class TypeField {
private String name;
private TypeInfo type;
public String getName() { return name; }
public TypeInfo getType() { return type; }
}
class ParamAbi {
private String name;
private TypeInfo type;
private boolean indexed;
public String getName() { return name; }
public TypeInfo getType() { return type; }
public boolean isIndexed() { return indexed; }
}
class FunctionAbi {
private String name;
private String selector;
private List<ParamAbi> inputs = new ArrayList<>();
private List<ParamAbi> outputs = new ArrayList<>();
private boolean view;
private boolean payable;
private String doc;
public String getName() { return name; }
public String getSelector() { return selector; }
public List<ParamAbi> getInputs() { return inputs; }
public List<ParamAbi> getOutputs() { return outputs; }
public boolean isView() { return view; }
public boolean isPayable() { return payable; }
public String getDoc() { return doc; }
}
class EventAbi {
private String name;
private String topic;
private List<ParamAbi> params = new ArrayList<>();
private String doc;
public String getName() { return name; }
public String getTopic() { return topic; }
public List<ParamAbi> getParams() { return params; }
public String getDoc() { return doc; }
}
class ErrorAbi {
private String name;
private String selector;
private List<ParamAbi> params = new ArrayList<>();
private String doc;
public String getName() { return name; }
public String getSelector() { return selector; }
public List<ParamAbi> getParams() { return params; }
public String getDoc() { return doc; }
}
class ContractAbi {
private String name;
private String version;
private List<FunctionAbi> functions = new ArrayList<>();
private List<EventAbi> events = new ArrayList<>();
private List<ErrorAbi> errors = new ArrayList<>();
public String getName() { return name; }
public String getVersion() { return version; }
public List<FunctionAbi> getFunctions() { return functions; }
public List<EventAbi> getEvents() { return events; }
public List<ErrorAbi> getErrors() { return errors; }
public FunctionAbi findFunction(String name) {
return functions.stream().filter(f -> f.getName().equals(name)).findFirst().orElse(null);
}
public FunctionAbi findBySelector(String selector) {
return functions.stream().filter(f -> f.getSelector().equals(selector)).findFirst().orElse(null);
}
}
// Compilation Result
class CompilationResult {
private String contractId;
private String code;
private String codeHash;
private int originalSize;
private int optimizedSize;
private double sizeReduction;
private ContractMetadata metadata;
private ContractAbi abi;
private long estimatedDeployGas;
private ValidationResult validation;
private List<String> warnings = new ArrayList<>();
public String getContractId() { return contractId; }
public String getCode() { return code; }
public String getCodeHash() { return codeHash; }
public int getOriginalSize() { return originalSize; }
public int getOptimizedSize() { return optimizedSize; }
public double getSizeReduction() { return sizeReduction; }
public ContractMetadata getMetadata() { return metadata; }
public ContractAbi getAbi() { return abi; }
public long getEstimatedDeployGas() { return estimatedDeployGas; }
public ValidationResult getValidation() { return validation; }
public List<String> getWarnings() { return warnings; }
public String getSizeStats() {
return String.format("Original: %d bytes, Optimized: %d bytes, Reduction: %.1f%%",
originalSize, optimizedSize, sizeReduction);
}
public byte[] decodeCode() {
return Base64.getDecoder().decode(code);
}
}
// Analysis
class SizeBreakdown {
private int code;
private int data;
private int types;
private int functions;
private int memory;
private int table;
private int exports;
private int imports;
private int custom;
private int total;
public int getCode() { return code; }
public int getData() { return data; }
public int getTypes() { return types; }
public int getFunctions() { return functions; }
public int getMemory() { return memory; }
public int getTable() { return table; }
public int getExports() { return exports; }
public int getImports() { return imports; }
public int getCustom() { return custom; }
public int getTotal() { return total; }
}
class FunctionAnalysis {
private String name;
private int size;
private int instructionCount;
private int localCount;
private boolean exported;
private long estimatedGas;
public String getName() { return name; }
public int getSize() { return size; }
public int getInstructionCount() { return instructionCount; }
public int getLocalCount() { return localCount; }
public boolean isExported() { return exported; }
public long getEstimatedGas() { return estimatedGas; }
}
class ImportAnalysis {
private String module;
private String name;
private String kind;
private String signature;
public String getModule() { return module; }
public String getName() { return name; }
public String getKind() { return kind; }
public String getSignature() { return signature; }
}
class SecurityIssue {
private String severity;
private String type;
private String description;
private String location;
public String getSeverity() { return severity; }
public String getType() { return type; }
public String getDescription() { return description; }
public String getLocation() { return location; }
}
class SecurityAnalysis {
private int score;
private List<SecurityIssue> issues = new ArrayList<>();
private List<String> recommendations = new ArrayList<>();
public int getScore() { return score; }
public List<SecurityIssue> getIssues() { return issues; }
public List<String> getRecommendations() { return recommendations; }
}
class GasAnalysis {
private long deploymentGas;
private Map<String, Long> functionGas = new HashMap<>();
private long memoryInitGas;
private long dataSectionGas;
public long getDeploymentGas() { return deploymentGas; }
public Map<String, Long> getFunctionGas() { return functionGas; }
public long getMemoryInitGas() { return memoryInitGas; }
public long getDataSectionGas() { return dataSectionGas; }
}
class ContractAnalysis {
private SizeBreakdown sizeBreakdown;
private List<FunctionAnalysis> functions = new ArrayList<>();
private List<ImportAnalysis> imports = new ArrayList<>();
private SecurityAnalysis security;
private GasAnalysis gasAnalysis;
public SizeBreakdown getSizeBreakdown() { return sizeBreakdown; }
public List<FunctionAnalysis> getFunctions() { return functions; }
public List<ImportAnalysis> getImports() { return imports; }
public SecurityAnalysis getSecurity() { return security; }
public GasAnalysis getGasAnalysis() { return gasAnalysis; }
}
// Error
class CompilerException extends RuntimeException {
private final String code;
private final Integer httpStatus;
public CompilerException(String message, String code, Integer httpStatus) {
super(message);
this.code = code;
this.httpStatus = httpStatus;
}
public String getCode() { return code; }
public Integer getHttpStatus() { return httpStatus; }
}
// Constants
final class CompilerConstants {
public static final int MAX_CONTRACT_SIZE = 256 * 1024;
public static final int MAX_MEMORY_PAGES = 16;
private CompilerConstants() {}
}

View file

@ -0,0 +1,539 @@
/**
* Synor Compiler SDK
*
* Smart contract compilation, optimization, and ABI generation.
*/
import {
CompilerConfig,
CompilerError,
CompilationRequest,
CompilationResult,
ContractAbi,
ContractAnalysis,
ContractMetadata,
FunctionAbi,
OptimizationLevel,
SourceMap,
StripOptions,
ValidationResult,
DEFAULT_COMPILER_CONFIG,
DEFAULT_STRIP_OPTIONS,
} from './types';
export * from './types';
/* ============================================================================
* Main Client
* ============================================================================ */
/**
* Synor Compiler SDK Client
*
* Smart contract compilation, optimization, and ABI generation.
*
* @example
* ```typescript
* const compiler = new SynorCompiler({ apiKey: 'your-api-key' });
*
* // Compile WASM bytecode
* const result = await compiler.compile(wasmBytes, {
* optimizationLevel: OptimizationLevel.Size,
* });
*
* console.log(`Optimized size: ${result.optimizedSize} bytes`);
* console.log(`Size reduction: ${result.sizeReduction.toFixed(1)}%`);
* ```
*/
export class SynorCompiler {
private readonly config: Required<
Omit<CompilerConfig, 'stripOptions'> & { stripOptions: StripOptions }
>;
private closed = false;
/** Contracts sub-client */
public readonly contracts: ContractsClient;
/** ABI sub-client */
public readonly abi: AbiClient;
/** Analysis sub-client */
public readonly analysis: AnalysisClient;
/** Validation sub-client */
public readonly validation: ValidationClient;
constructor(config: CompilerConfig) {
this.config = {
...DEFAULT_COMPILER_CONFIG,
...config,
endpoint: config.endpoint ?? DEFAULT_COMPILER_CONFIG.endpoint!,
timeout: config.timeout ?? DEFAULT_COMPILER_CONFIG.timeout!,
retries: config.retries ?? DEFAULT_COMPILER_CONFIG.retries!,
debug: config.debug ?? DEFAULT_COMPILER_CONFIG.debug!,
optimizationLevel: config.optimizationLevel ?? DEFAULT_COMPILER_CONFIG.optimizationLevel!,
useWasmOpt: config.useWasmOpt ?? DEFAULT_COMPILER_CONFIG.useWasmOpt!,
validate: config.validate ?? DEFAULT_COMPILER_CONFIG.validate!,
extractMetadata: config.extractMetadata ?? DEFAULT_COMPILER_CONFIG.extractMetadata!,
generateAbi: config.generateAbi ?? DEFAULT_COMPILER_CONFIG.generateAbi!,
maxContractSize: config.maxContractSize ?? 256 * 1024,
stripOptions: {
...DEFAULT_STRIP_OPTIONS,
...config.stripOptions,
},
};
this.contracts = new ContractsClient(this);
this.abi = new AbiClient(this);
this.analysis = new AnalysisClient(this);
this.validation = new ValidationClient(this);
}
/** Get the default optimization level */
get defaultOptimizationLevel(): OptimizationLevel {
return this.config.optimizationLevel;
}
/** Check service health */
async healthCheck(): Promise<boolean> {
try {
const result = await this.get<{ status: string }>('/health');
return result.status === 'healthy';
} catch {
return false;
}
}
/** Get service info */
async getInfo(): Promise<Record<string, unknown>> {
return this.get('/info');
}
/** Close the client */
close(): void {
this.closed = true;
}
/** Check if client is closed */
get isClosed(): boolean {
return this.closed;
}
/**
* Compile WASM bytecode
*
* @param wasm - WASM bytecode as Uint8Array or base64 string
* @param options - Compilation options
* @returns Compilation result
*/
async compile(
wasm: Uint8Array | string,
options?: Partial<CompilationRequest>
): Promise<CompilationResult> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
return this.post<CompilationResult>('/compile', {
wasm: wasmBase64,
optimization_level: options?.optimizationLevel ?? this.config.optimizationLevel,
strip_options: options?.stripOptions ?? this.config.stripOptions,
use_wasm_opt: options?.useWasmOpt ?? this.config.useWasmOpt,
validate: options?.validate ?? this.config.validate,
extract_metadata: options?.extractMetadata ?? this.config.extractMetadata,
generate_abi: options?.generateAbi ?? this.config.generateAbi,
});
}
/** Internal GET request */
async get<T>(path: string): Promise<T> {
this.checkClosed();
const response = await this.request('GET', path);
return this.handleResponse<T>(response);
}
/** Internal POST request */
async post<T>(path: string, body?: unknown): Promise<T> {
this.checkClosed();
const response = await this.request('POST', path, body);
return this.handleResponse<T>(response);
}
private async request(method: string, path: string, body?: unknown): Promise<Response> {
const url = `${this.config.endpoint}${path}`;
const headers: Record<string, string> = {
Authorization: `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
'X-SDK-Version': 'js/0.1.0',
};
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
try {
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
return response;
} finally {
clearTimeout(timeoutId);
}
}
private async handleResponse<T>(response: Response): Promise<T> {
const text = await response.text();
if (!response.ok) {
let error: { message?: string; code?: string } = {};
try {
error = JSON.parse(text);
} catch {
error = { message: text || `HTTP ${response.status}` };
}
throw new CompilerError(
error.message ?? `HTTP ${response.status}`,
error.code,
response.status
);
}
return JSON.parse(text, this.reviver) as T;
}
private reviver(_key: string, value: unknown): unknown {
if (typeof value === 'string') {
// Convert snake_case to camelCase for keys handled during JSON parse
return value;
}
if (value && typeof value === 'object' && !Array.isArray(value)) {
const converted: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value)) {
const camelKey = k.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
converted[camelKey] = v;
}
return converted;
}
return value;
}
private checkClosed(): void {
if (this.closed) {
throw new CompilerError('Client has been closed', 'CLIENT_CLOSED');
}
}
private toBase64(data: Uint8Array): string {
if (typeof Buffer !== 'undefined') {
return Buffer.from(data).toString('base64');
}
return btoa(String.fromCharCode(...data));
}
}
/* ============================================================================
* Contracts Sub-Client
* ============================================================================ */
/** Contracts sub-client for contract compilation */
export class ContractsClient {
constructor(private readonly compiler: SynorCompiler) {}
/**
* Compile WASM bytecode with default settings
*/
async compile(
wasm: Uint8Array | string,
options?: Partial<CompilationRequest>
): Promise<CompilationResult> {
return this.compiler.compile(wasm, options);
}
/**
* Compile with development settings (fast, minimal optimization)
*/
async compileDev(wasm: Uint8Array | string): Promise<CompilationResult> {
return this.compiler.compile(wasm, {
optimizationLevel: OptimizationLevel.None,
useWasmOpt: false,
validate: true,
});
}
/**
* Compile with production settings (aggressive optimization)
*/
async compileProduction(wasm: Uint8Array | string): Promise<CompilationResult> {
return this.compiler.compile(wasm, {
optimizationLevel: OptimizationLevel.Aggressive,
useWasmOpt: true,
validate: true,
extractMetadata: true,
generateAbi: true,
});
}
/**
* Get a compiled contract by ID
*/
async get(contractId: string): Promise<CompilationResult> {
return this.compiler.get<CompilationResult>(`/contracts/${contractId}`);
}
/**
* List compiled contracts
*/
async list(limit?: number, offset?: number): Promise<CompilationResult[]> {
const params = new URLSearchParams();
if (limit !== undefined) params.set('limit', limit.toString());
if (offset !== undefined) params.set('offset', offset.toString());
const query = params.toString() ? `?${params}` : '';
const result = await this.compiler.get<{ contracts: CompilationResult[] }>(`/contracts${query}`);
return result.contracts ?? [];
}
/**
* Get optimized bytecode for a contract
*/
async getCode(contractId: string): Promise<Uint8Array> {
const result = await this.compiler.get<{ code: string }>(`/contracts/${contractId}/code`);
return this.fromBase64(result.code);
}
private fromBase64(data: string): Uint8Array {
if (typeof Buffer !== 'undefined') {
return new Uint8Array(Buffer.from(data, 'base64'));
}
return new Uint8Array(
atob(data)
.split('')
.map((c) => c.charCodeAt(0))
);
}
}
/* ============================================================================
* ABI Sub-Client
* ============================================================================ */
/** ABI sub-client for ABI generation and encoding */
export class AbiClient {
constructor(private readonly compiler: SynorCompiler) {}
/**
* Extract ABI from WASM bytecode
*/
async extract(wasm: Uint8Array | string): Promise<ContractAbi> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
return this.compiler.post<ContractAbi>('/abi/extract', { wasm: wasmBase64 });
}
/**
* Get ABI for a compiled contract
*/
async get(contractId: string): Promise<ContractAbi> {
return this.compiler.get<ContractAbi>(`/contracts/${contractId}/abi`);
}
/**
* Encode function call
*/
async encodeCall(functionAbi: FunctionAbi, args: unknown[]): Promise<string> {
const result = await this.compiler.post<{ data: string }>('/abi/encode', {
function: functionAbi,
args,
});
return result.data;
}
/**
* Decode function result
*/
async decodeResult(functionAbi: FunctionAbi, data: string): Promise<unknown[]> {
const result = await this.compiler.post<{ values: unknown[] }>('/abi/decode', {
function: functionAbi,
data,
});
return result.values;
}
/**
* Compute function selector
*/
computeSelector(functionName: string): string {
// Use blake3 hash and take first 4 bytes
// This is a simplified version - actual implementation would use proper hashing
let hash = 0;
for (let i = 0; i < functionName.length; i++) {
const char = functionName.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
const bytes = new Uint8Array(4);
bytes[0] = (hash >> 24) & 0xff;
bytes[1] = (hash >> 16) & 0xff;
bytes[2] = (hash >> 8) & 0xff;
bytes[3] = hash & 0xff;
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Generate TypeScript types from ABI
*/
async generateTypes(abi: ContractAbi): Promise<string> {
const result = await this.compiler.post<{ typescript: string }>('/abi/generate-types', {
abi,
language: 'typescript',
});
return result.typescript;
}
private toBase64(data: Uint8Array): string {
if (typeof Buffer !== 'undefined') {
return Buffer.from(data).toString('base64');
}
return btoa(String.fromCharCode(...data));
}
}
/* ============================================================================
* Analysis Sub-Client
* ============================================================================ */
/** Analysis sub-client for contract analysis */
export class AnalysisClient {
constructor(private readonly compiler: SynorCompiler) {}
/**
* Analyze WASM bytecode
*/
async analyze(wasm: Uint8Array | string): Promise<ContractAnalysis> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
return this.compiler.post<ContractAnalysis>('/analysis', { wasm: wasmBase64 });
}
/**
* Get analysis for a compiled contract
*/
async get(contractId: string): Promise<ContractAnalysis> {
return this.compiler.get<ContractAnalysis>(`/contracts/${contractId}/analysis`);
}
/**
* Extract metadata from WASM bytecode
*/
async extractMetadata(wasm: Uint8Array | string): Promise<ContractMetadata> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
return this.compiler.post<ContractMetadata>('/analysis/metadata', { wasm: wasmBase64 });
}
/**
* Extract source map from WASM bytecode
*/
async extractSourceMap(wasm: Uint8Array | string): Promise<SourceMap | null> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
const result = await this.compiler.post<{ sourceMap: SourceMap | null }>(
'/analysis/source-map',
{
wasm: wasmBase64,
}
);
return result.sourceMap;
}
/**
* Estimate gas for contract deployment
*/
async estimateDeployGas(wasm: Uint8Array | string): Promise<number> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
const result = await this.compiler.post<{ gas: number }>('/analysis/estimate-gas', {
wasm: wasmBase64,
});
return result.gas;
}
/**
* Get security analysis
*/
async securityScan(wasm: Uint8Array | string): Promise<ContractAnalysis['security']> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
const result = await this.compiler.post<{ security: ContractAnalysis['security'] }>(
'/analysis/security',
{
wasm: wasmBase64,
}
);
return result.security;
}
private toBase64(data: Uint8Array): string {
if (typeof Buffer !== 'undefined') {
return Buffer.from(data).toString('base64');
}
return btoa(String.fromCharCode(...data));
}
}
/* ============================================================================
* Validation Sub-Client
* ============================================================================ */
/** Validation sub-client for WASM validation */
export class ValidationClient {
constructor(private readonly compiler: SynorCompiler) {}
/**
* Validate WASM bytecode against VM requirements
*/
async validate(wasm: Uint8Array | string): Promise<ValidationResult> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
return this.compiler.post<ValidationResult>('/validate', { wasm: wasmBase64 });
}
/**
* Check if WASM is valid
*/
async isValid(wasm: Uint8Array | string): Promise<boolean> {
const result = await this.validate(wasm);
return result.valid;
}
/**
* Get validation errors
*/
async getErrors(wasm: Uint8Array | string): Promise<string[]> {
const result = await this.validate(wasm);
return result.errors.map((e) => e.message);
}
/**
* Validate contract exports
*/
async validateExports(wasm: Uint8Array | string, requiredExports: string[]): Promise<boolean> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
const result = await this.compiler.post<{ valid: boolean }>('/validate/exports', {
wasm: wasmBase64,
required_exports: requiredExports,
});
return result.valid;
}
/**
* Validate memory limits
*/
async validateMemory(wasm: Uint8Array | string, maxPages: number): Promise<boolean> {
const wasmBase64 = typeof wasm === 'string' ? wasm : this.toBase64(wasm);
const result = await this.compiler.post<{ valid: boolean }>('/validate/memory', {
wasm: wasmBase64,
max_pages: maxPages,
});
return result.valid;
}
private toBase64(data: Uint8Array): string {
if (typeof Buffer !== 'undefined') {
return Buffer.from(data).toString('base64');
}
return btoa(String.fromCharCode(...data));
}
}

View file

@ -0,0 +1,514 @@
/**
* Synor Compiler SDK Types
*
* Smart contract compilation, optimization, and ABI generation.
*/
/* ============================================================================
* Enumerations
* ============================================================================ */
/** Optimization level for WASM compilation */
export enum OptimizationLevel {
/** No optimization */
None = 'none',
/** Basic optimizations (stripping only) */
Basic = 'basic',
/** Optimize for size (-Os equivalent) */
Size = 'size',
/** Aggressive optimization (-O3 -Os equivalent) */
Aggressive = 'aggressive',
}
/* ============================================================================
* Configuration Types
* ============================================================================ */
/** Options for stripping WASM sections */
export interface StripOptions {
/** Strip debug sections (.debug_*) */
stripDebug: boolean;
/** Strip producer sections */
stripProducers: boolean;
/** Strip name sections */
stripNames: boolean;
/** Strip custom sections (all non-standard sections) */
stripCustom: boolean;
/** Specific custom sections to preserve */
preserveSections: string[];
/** Strip unused functions */
stripUnused: boolean;
}
/** Compiler configuration */
export interface CompilerConfig {
/** API key for authentication */
apiKey: string;
/** Compiler service endpoint */
endpoint?: string;
/** Request timeout in milliseconds */
timeout?: number;
/** Maximum retry attempts */
retries?: number;
/** Enable debug logging */
debug?: boolean;
/** Optimization level */
optimizationLevel?: OptimizationLevel;
/** Strip options */
stripOptions?: Partial<StripOptions>;
/** Maximum contract size in bytes */
maxContractSize?: number;
/** Whether to use wasm-opt */
useWasmOpt?: boolean;
/** Whether to validate against VM requirements */
validate?: boolean;
/** Whether to extract metadata */
extractMetadata?: boolean;
/** Whether to generate ABI */
generateAbi?: boolean;
}
/* ============================================================================
* Compilation Types
* ============================================================================ */
/** Compilation request */
export interface CompilationRequest {
/** WASM bytecode (base64 encoded) */
wasm: string;
/** Optimization level */
optimizationLevel?: OptimizationLevel;
/** Strip options */
stripOptions?: Partial<StripOptions>;
/** Whether to use wasm-opt */
useWasmOpt?: boolean;
/** Whether to validate */
validate?: boolean;
/** Whether to extract metadata */
extractMetadata?: boolean;
/** Whether to generate ABI */
generateAbi?: boolean;
}
/** Compilation result */
export interface CompilationResult {
/** Contract ID (hash of optimized code) */
contractId: string;
/** Optimized WASM bytecode (base64 encoded) */
code: string;
/** Code hash */
codeHash: string;
/** Original code size in bytes */
originalSize: number;
/** Optimized code size in bytes */
optimizedSize: number;
/** Size reduction percentage */
sizeReduction: number;
/** Contract metadata */
metadata?: ContractMetadata;
/** Contract ABI */
abi?: ContractAbi;
/** Estimated deployment gas */
estimatedDeployGas: number;
/** Validation result */
validation: ValidationResult;
/** Compilation warnings */
warnings: string[];
}
/** Validation result */
export interface ValidationResult {
/** Whether validation passed */
valid: boolean;
/** Validation errors */
errors: ValidationError[];
/** Validation warnings */
warnings: string[];
/** Number of exports */
exportCount: number;
/** Number of imports */
importCount: number;
/** Number of functions */
functionCount: number;
/** Memory pages (min, max) */
memoryPages: [number, number | null];
}
/** Validation error */
export interface ValidationError {
/** Error code */
code: string;
/** Error message */
message: string;
/** Location in WASM (if applicable) */
location?: string;
}
/* ============================================================================
* Metadata Types
* ============================================================================ */
/** Contract metadata */
export interface ContractMetadata {
/** Contract name */
name?: string;
/** Contract version */
version?: string;
/** Contract authors */
authors: string[];
/** Contract description */
description?: string;
/** Contract license */
license?: string;
/** Repository URL */
repository?: string;
/** Build timestamp */
buildTimestamp?: number;
/** Rust version used */
rustVersion?: string;
/** Synor SDK version */
sdkVersion?: string;
/** Custom metadata */
custom: Record<string, string>;
}
/* ============================================================================
* ABI Types
* ============================================================================ */
/** Contract ABI (Application Binary Interface) */
export interface ContractAbi {
/** Contract name */
name: string;
/** ABI version */
version: string;
/** Functions */
functions: FunctionAbi[];
/** Events */
events: EventAbi[];
/** Errors */
errors: ErrorAbi[];
}
/** Function ABI */
export interface FunctionAbi {
/** Function name */
name: string;
/** 4-byte selector (hex) */
selector: string;
/** Input parameters */
inputs: ParamAbi[];
/** Output parameters */
outputs: ParamAbi[];
/** Whether function is read-only (view) */
view: boolean;
/** Whether function is payable */
payable: boolean;
/** Function documentation */
doc?: string;
}
/** Event ABI */
export interface EventAbi {
/** Event name */
name: string;
/** Event topic (hex) */
topic: string;
/** Event parameters */
params: ParamAbi[];
/** Event documentation */
doc?: string;
}
/** Error ABI */
export interface ErrorAbi {
/** Error name */
name: string;
/** Error selector (hex) */
selector: string;
/** Error parameters */
params: ParamAbi[];
/** Error documentation */
doc?: string;
}
/** Parameter ABI */
export interface ParamAbi {
/** Parameter name */
name: string;
/** Parameter type */
type: TypeInfo;
/** Whether parameter is indexed (for events) */
indexed: boolean;
}
/** Type information */
export type TypeInfo =
| { kind: 'u8' }
| { kind: 'u16' }
| { kind: 'u32' }
| { kind: 'u64' }
| { kind: 'u128' }
| { kind: 'i8' }
| { kind: 'i16' }
| { kind: 'i32' }
| { kind: 'i64' }
| { kind: 'i128' }
| { kind: 'bool' }
| { kind: 'string' }
| { kind: 'bytes' }
| { kind: 'fixedBytes'; size: number }
| { kind: 'address' }
| { kind: 'hash256' }
| { kind: 'array'; element: TypeInfo }
| { kind: 'fixedArray'; element: TypeInfo; size: number }
| { kind: 'option'; inner: TypeInfo }
| { kind: 'tuple'; elements: TypeInfo[] }
| { kind: 'struct'; name: string; fields: Array<{ name: string; type: TypeInfo }> }
| { kind: 'unknown'; name: string };
/* ============================================================================
* Source Map Types
* ============================================================================ */
/** Source map for debugging */
export interface SourceMap {
/** Source file name */
file: string;
/** Mappings */
mappings: SourceMapping[];
}
/** Source mapping entry */
export interface SourceMapping {
/** WASM offset */
wasmOffset: number;
/** Source line */
line: number;
/** Source column */
column: number;
/** Function name */
function?: string;
}
/* ============================================================================
* Analysis Types
* ============================================================================ */
/** Contract analysis result */
export interface ContractAnalysis {
/** Code size breakdown */
sizeBreakdown: SizeBreakdown;
/** Function analysis */
functions: FunctionAnalysis[];
/** Import analysis */
imports: ImportAnalysis[];
/** Security analysis */
security: SecurityAnalysis;
/** Gas analysis */
gasAnalysis: GasAnalysis;
}
/** Size breakdown */
export interface SizeBreakdown {
/** Code section size */
code: number;
/** Data section size */
data: number;
/** Type section size */
types: number;
/** Function section size */
functions: number;
/** Memory section size */
memory: number;
/** Table section size */
table: number;
/** Export section size */
exports: number;
/** Import section size */
imports: number;
/** Custom sections size */
custom: number;
/** Total size */
total: number;
}
/** Function analysis */
export interface FunctionAnalysis {
/** Function name or index */
name: string;
/** Function size in bytes */
size: number;
/** Instruction count */
instructionCount: number;
/** Local variable count */
localCount: number;
/** Is exported */
exported: boolean;
/** Estimated gas cost */
estimatedGas: number;
}
/** Import analysis */
export interface ImportAnalysis {
/** Module name */
module: string;
/** Import name */
name: string;
/** Import kind */
kind: 'function' | 'memory' | 'table' | 'global';
/** Type signature (for functions) */
signature?: string;
}
/** Security analysis */
export interface SecurityAnalysis {
/** Security score (0-100) */
score: number;
/** Issues found */
issues: SecurityIssue[];
/** Recommendations */
recommendations: string[];
}
/** Security issue */
export interface SecurityIssue {
/** Severity level */
severity: 'low' | 'medium' | 'high' | 'critical';
/** Issue type */
type: string;
/** Issue description */
description: string;
/** Location in code */
location?: string;
}
/** Gas analysis */
export interface GasAnalysis {
/** Base deployment gas */
deploymentGas: number;
/** Per-function gas estimates */
functionGas: Record<string, number>;
/** Memory initialization gas */
memoryInitGas: number;
/** Data section gas */
dataSectionGas: number;
}
/* ============================================================================
* Error Types
* ============================================================================ */
/** Compiler error codes */
export enum CompilerErrorCode {
/** WASM parsing error */
ParseError = 'PARSE_ERROR',
/** Validation error */
ValidationError = 'VALIDATION_ERROR',
/** Optimization error */
OptimizationError = 'OPTIMIZATION_ERROR',
/** Contract too large */
ContractTooLarge = 'CONTRACT_TOO_LARGE',
/** Missing required export */
MissingExport = 'MISSING_EXPORT',
/** Invalid export signature */
InvalidExportSignature = 'INVALID_EXPORT_SIGNATURE',
/** IO error */
IoError = 'IO_ERROR',
/** Encoding error */
EncodingError = 'ENCODING_ERROR',
/** External tool error */
ExternalToolError = 'EXTERNAL_TOOL_ERROR',
/** Network error */
NetworkError = 'NETWORK_ERROR',
/** Authentication error */
AuthError = 'AUTH_ERROR',
/** Client closed */
ClientClosed = 'CLIENT_CLOSED',
}
/** Compiler error */
export class CompilerError extends Error {
constructor(
message: string,
public readonly code?: string,
public readonly httpStatus?: number,
public readonly details?: Record<string, unknown>
) {
super(message);
this.name = 'CompilerError';
}
}
/* ============================================================================
* Constants
* ============================================================================ */
/** Maximum contract size (256 KB) */
export const MAX_CONTRACT_SIZE = 256 * 1024;
/** Maximum memory pages */
export const MAX_MEMORY_PAGES = 16;
/** Default strip options */
export const DEFAULT_STRIP_OPTIONS: StripOptions = {
stripDebug: true,
stripProducers: true,
stripNames: true,
stripCustom: true,
preserveSections: [],
stripUnused: true,
};
/** Default compiler config */
export const DEFAULT_COMPILER_CONFIG: Partial<CompilerConfig> = {
endpoint: 'https://compiler.synor.io/v1',
timeout: 60000,
retries: 3,
debug: false,
optimizationLevel: OptimizationLevel.Size,
useWasmOpt: true,
validate: true,
extractMetadata: true,
generateAbi: true,
};
/* ============================================================================
* Utility Types
* ============================================================================ */
/** Get type name from TypeInfo */
export function getTypeName(type: TypeInfo): string {
switch (type.kind) {
case 'u8':
case 'u16':
case 'u32':
case 'u64':
case 'u128':
case 'i8':
case 'i16':
case 'i32':
case 'i64':
case 'i128':
case 'bool':
case 'string':
case 'bytes':
case 'address':
case 'hash256':
return type.kind;
case 'fixedBytes':
return `bytes${type.size}`;
case 'array':
return `${getTypeName(type.element)}[]`;
case 'fixedArray':
return `${getTypeName(type.element)}[${type.size}]`;
case 'option':
return `${getTypeName(type.inner)}?`;
case 'tuple':
return `(${type.elements.map(getTypeName).join(', ')})`;
case 'struct':
return type.name;
case 'unknown':
return type.name;
}
}

View file

@ -0,0 +1,500 @@
/**
* Synor Compiler SDK for Kotlin
*
* Smart contract compilation, optimization, and ABI generation.
*/
package io.synor.compiler
import kotlinx.coroutines.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
// ============================================================================
// Enumerations
// ============================================================================
@Serializable
enum class OptimizationLevel {
@SerialName("none") NONE,
@SerialName("basic") BASIC,
@SerialName("size") SIZE,
@SerialName("aggressive") AGGRESSIVE;
fun toApiString(): String = when (this) {
NONE -> "none"
BASIC -> "basic"
SIZE -> "size"
AGGRESSIVE -> "aggressive"
}
companion object {
fun fromApiString(value: String): OptimizationLevel = when (value) {
"none" -> NONE
"basic" -> BASIC
"size" -> SIZE
"aggressive" -> AGGRESSIVE
else -> SIZE
}
}
}
// ============================================================================
// Configuration
// ============================================================================
@Serializable
data class StripOptions(
val stripDebug: Boolean = true,
val stripProducers: Boolean = true,
val stripNames: Boolean = true,
val stripCustom: Boolean = true,
val preserveSections: List<String> = emptyList(),
val stripUnused: Boolean = true
)
data class CompilerConfig(
val apiKey: String,
val endpoint: String = "https://compiler.synor.io/v1",
val timeout: Long = 60000,
val retries: Int = 3,
val debug: Boolean = false,
val optimizationLevel: OptimizationLevel = OptimizationLevel.SIZE,
val stripOptions: StripOptions? = null,
val maxContractSize: Int = 256 * 1024,
val useWasmOpt: Boolean = true,
val validate: Boolean = true,
val extractMetadata: Boolean = true,
val generateAbi: Boolean = true
)
// ============================================================================
// Types
// ============================================================================
@Serializable
data class ValidationError(
val code: String,
val message: String,
val location: String? = null
)
@Serializable
data class ValidationResult(
val valid: Boolean,
val errors: List<ValidationError> = emptyList(),
val warnings: List<String> = emptyList(),
val exportCount: Int = 0,
val importCount: Int = 0,
val functionCount: Int = 0,
val memoryPages: List<Int?> = listOf(0, null)
)
@Serializable
data class ContractMetadata(
val name: String? = null,
val version: String? = null,
val authors: List<String> = emptyList(),
val description: String? = null,
val license: String? = null,
val repository: String? = null,
val buildTimestamp: Long? = null,
val rustVersion: String? = null,
val sdkVersion: String? = null,
val custom: Map<String, String> = emptyMap()
)
@Serializable
data class TypeInfo(
val kind: String,
val size: Int? = null,
val element: TypeInfo? = null,
val inner: TypeInfo? = null,
val elements: List<TypeInfo>? = null,
val name: String? = null,
val fields: List<TypeField>? = null
) {
val typeName: String get() = when (kind) {
"u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128",
"bool", "string", "bytes", "address", "hash256" -> kind
"fixedBytes" -> "bytes${size ?: 0}"
"array" -> "${element?.typeName ?: ""}[]"
"fixedArray" -> "${element?.typeName ?: ""}[${size ?: 0}]"
"option" -> "${inner?.typeName ?: ""}?"
"tuple" -> "(${elements?.joinToString(", ") { it.typeName } ?: ""})"
"struct" -> name ?: "struct"
else -> name ?: "unknown"
}
}
@Serializable
data class TypeField(
val name: String,
val type: TypeInfo
)
@Serializable
data class ParamAbi(
val name: String,
val type: TypeInfo,
val indexed: Boolean = false
)
@Serializable
data class FunctionAbi(
val name: String,
val selector: String,
val inputs: List<ParamAbi> = emptyList(),
val outputs: List<ParamAbi> = emptyList(),
val view: Boolean = false,
val payable: Boolean = false,
val doc: String? = null
)
@Serializable
data class EventAbi(
val name: String,
val topic: String,
val params: List<ParamAbi> = emptyList(),
val doc: String? = null
)
@Serializable
data class ErrorAbi(
val name: String,
val selector: String,
val params: List<ParamAbi> = emptyList(),
val doc: String? = null
)
@Serializable
data class ContractAbi(
val name: String,
val version: String,
val functions: List<FunctionAbi> = emptyList(),
val events: List<EventAbi> = emptyList(),
val errors: List<ErrorAbi> = emptyList()
) {
fun findFunction(name: String): FunctionAbi? = functions.find { it.name == name }
fun findBySelector(selector: String): FunctionAbi? = functions.find { it.selector == selector }
}
@Serializable
data class CompilationResult(
val contractId: String,
val code: String,
val codeHash: String,
val originalSize: Int,
val optimizedSize: Int,
val sizeReduction: Double,
val metadata: ContractMetadata? = null,
val abi: ContractAbi? = null,
val estimatedDeployGas: Long = 0,
val validation: ValidationResult? = null,
val warnings: List<String> = emptyList()
) {
val sizeStats: String get() =
"Original: $originalSize bytes, Optimized: $optimizedSize bytes, Reduction: ${"%.1f".format(sizeReduction)}%"
fun decodeCode(): ByteArray = Base64.getDecoder().decode(code)
}
@Serializable
data class SizeBreakdown(
val code: Int = 0,
val data: Int = 0,
val types: Int = 0,
val functions: Int = 0,
val memory: Int = 0,
val table: Int = 0,
val exports: Int = 0,
val imports: Int = 0,
val custom: Int = 0,
val total: Int = 0
)
@Serializable
data class FunctionAnalysis(
val name: String,
val size: Int,
val instructionCount: Int,
val localCount: Int,
val exported: Boolean,
val estimatedGas: Long
)
@Serializable
data class ImportAnalysis(
val module: String,
val name: String,
val kind: String,
val signature: String? = null
)
@Serializable
data class SecurityIssue(
val severity: String,
val type: String,
val description: String,
val location: String? = null
)
@Serializable
data class SecurityAnalysis(
val score: Int,
val issues: List<SecurityIssue> = emptyList(),
val recommendations: List<String> = emptyList()
)
@Serializable
data class GasAnalysis(
val deploymentGas: Long,
val functionGas: Map<String, Long> = emptyMap(),
val memoryInitGas: Long = 0,
val dataSectionGas: Long = 0
)
@Serializable
data class ContractAnalysis(
val sizeBreakdown: SizeBreakdown,
val functions: List<FunctionAnalysis> = emptyList(),
val imports: List<ImportAnalysis> = emptyList(),
val security: SecurityAnalysis? = null,
val gasAnalysis: GasAnalysis? = null
)
class CompilerException(
message: String,
val code: String? = null,
val httpStatus: Int? = null
) : RuntimeException(message)
// ============================================================================
// Client
// ============================================================================
class SynorCompiler(private val config: CompilerConfig) : AutoCloseable {
private val httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(config.timeout))
.build()
private val json = Json { ignoreUnknownKeys = true; isLenient = true }
private val closed = AtomicBoolean(false)
val contracts = ContractsClient(this)
val abi = AbiClient(this)
val analysis = AnalysisClient(this)
val validation = ValidationClient(this)
val defaultOptimizationLevel: OptimizationLevel get() = config.optimizationLevel
suspend fun healthCheck(): Boolean = try {
val result = get<Map<String, String>>("/health")
result["status"] == "healthy"
} catch (e: Exception) {
false
}
suspend fun getInfo(): Map<String, Any> = get("/info")
override fun close() {
closed.set(true)
}
val isClosed: Boolean get() = closed.get()
suspend fun compile(
wasm: ByteArray,
optimizationLevel: OptimizationLevel? = null,
stripOptions: StripOptions? = null,
useWasmOpt: Boolean? = null,
validate: Boolean? = null,
extractMetadata: Boolean? = null,
generateAbi: Boolean? = null
): CompilationResult {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
val body = buildJsonObject {
put("wasm", wasmBase64)
put("optimization_level", (optimizationLevel ?: config.optimizationLevel).toApiString())
put("use_wasm_opt", useWasmOpt ?: config.useWasmOpt)
put("validate", validate ?: config.validate)
put("extract_metadata", extractMetadata ?: config.extractMetadata)
put("generate_abi", generateAbi ?: config.generateAbi)
stripOptions?.let {
put("strip_options", json.encodeToJsonElement(it))
}
}
return post("/compile", body)
}
internal suspend inline fun <reified T> get(path: String): T {
checkClosed()
val request = HttpRequest.newBuilder()
.uri(URI.create("${config.endpoint}$path"))
.header("Authorization", "Bearer ${config.apiKey}")
.header("Content-Type", "application/json")
.header("X-SDK-Version", "kotlin/0.1.0")
.GET()
.timeout(Duration.ofMillis(config.timeout))
.build()
return withContext(Dispatchers.IO) {
val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
handleResponse(response)
}
}
internal suspend inline fun <reified T> post(path: String, body: JsonElement): T {
checkClosed()
val request = HttpRequest.newBuilder()
.uri(URI.create("${config.endpoint}$path"))
.header("Authorization", "Bearer ${config.apiKey}")
.header("Content-Type", "application/json")
.header("X-SDK-Version", "kotlin/0.1.0")
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.timeout(Duration.ofMillis(config.timeout))
.build()
return withContext(Dispatchers.IO) {
val response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
handleResponse(response)
}
}
private inline fun <reified T> handleResponse(response: HttpResponse<String>): T {
if (response.statusCode() >= 400) {
val error = try {
json.decodeFromString<Map<String, String>>(response.body())
} catch (e: Exception) {
mapOf("message" to if (response.body().isEmpty()) "HTTP ${response.statusCode()}" else response.body())
}
throw CompilerException(
error["message"] ?: "HTTP ${response.statusCode()}",
error["code"],
response.statusCode()
)
}
return json.decodeFromString(response.body())
}
private fun checkClosed() {
if (closed.get()) {
throw CompilerException("Client has been closed", "CLIENT_CLOSED")
}
}
}
class ContractsClient(private val compiler: SynorCompiler) {
suspend fun compile(wasm: ByteArray, vararg opts: Pair<String, Any?>): CompilationResult =
compiler.compile(wasm)
suspend fun compileDev(wasm: ByteArray): CompilationResult =
compiler.compile(wasm, OptimizationLevel.NONE, null, false, true)
suspend fun compileProduction(wasm: ByteArray): CompilationResult =
compiler.compile(wasm, OptimizationLevel.AGGRESSIVE, null, true, true, true, true)
suspend fun get(contractId: String): CompilationResult =
compiler.get("/contracts/$contractId")
suspend fun list(limit: Int? = null, offset: Int? = null): List<CompilationResult> {
val params = mutableListOf<String>()
limit?.let { params.add("limit=$it") }
offset?.let { params.add("offset=$it") }
val query = if (params.isNotEmpty()) "?${params.joinToString("&")}" else ""
val result: Map<String, List<CompilationResult>> = compiler.get("/contracts$query")
return result["contracts"] ?: emptyList()
}
suspend fun getCode(contractId: String): ByteArray {
val result: Map<String, String> = compiler.get("/contracts/$contractId/code")
return Base64.getDecoder().decode(result["code"] ?: "")
}
}
class AbiClient(private val compiler: SynorCompiler) {
suspend fun extract(wasm: ByteArray): ContractAbi {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
return compiler.post("/abi/extract", buildJsonObject { put("wasm", wasmBase64) })
}
suspend fun get(contractId: String): ContractAbi =
compiler.get("/contracts/$contractId/abi")
suspend fun encodeCall(functionAbi: FunctionAbi, args: List<Any>): String {
val body = buildJsonObject {
put("function", Json.encodeToJsonElement(functionAbi))
put("args", Json.encodeToJsonElement(args))
}
val result: Map<String, String> = compiler.post("/abi/encode", body)
return result["data"] ?: ""
}
}
class AnalysisClient(private val compiler: SynorCompiler) {
suspend fun analyze(wasm: ByteArray): ContractAnalysis {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
return compiler.post("/analysis", buildJsonObject { put("wasm", wasmBase64) })
}
suspend fun get(contractId: String): ContractAnalysis =
compiler.get("/contracts/$contractId/analysis")
suspend fun extractMetadata(wasm: ByteArray): ContractMetadata {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
return compiler.post("/analysis/metadata", buildJsonObject { put("wasm", wasmBase64) })
}
suspend fun estimateDeployGas(wasm: ByteArray): Long {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
val result: Map<String, Long> = compiler.post("/analysis/estimate-gas", buildJsonObject { put("wasm", wasmBase64) })
return result["gas"] ?: 0
}
suspend fun securityScan(wasm: ByteArray): SecurityAnalysis {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
val result: Map<String, SecurityAnalysis> = compiler.post("/analysis/security", buildJsonObject { put("wasm", wasmBase64) })
return result["security"] ?: SecurityAnalysis(0)
}
}
class ValidationClient(private val compiler: SynorCompiler) {
suspend fun validate(wasm: ByteArray): ValidationResult {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
return compiler.post("/validate", buildJsonObject { put("wasm", wasmBase64) })
}
suspend fun isValid(wasm: ByteArray): Boolean = validate(wasm).valid
suspend fun getErrors(wasm: ByteArray): List<String> =
validate(wasm).errors.map { it.message }
suspend fun validateExports(wasm: ByteArray, requiredExports: List<String>): Boolean {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
val body = buildJsonObject {
put("wasm", wasmBase64)
put("required_exports", Json.encodeToJsonElement(requiredExports))
}
val result: Map<String, Boolean> = compiler.post("/validate/exports", body)
return result["valid"] ?: false
}
suspend fun validateMemory(wasm: ByteArray, maxPages: Int): Boolean {
val wasmBase64 = Base64.getEncoder().encodeToString(wasm)
val body = buildJsonObject {
put("wasm", wasmBase64)
put("max_pages", maxPages)
}
val result: Map<String, Boolean> = compiler.post("/validate/memory", body)
return result["valid"] ?: false
}
}
// Constants
const val MAX_CONTRACT_SIZE = 256 * 1024
const val MAX_MEMORY_PAGES = 16

View file

@ -0,0 +1,70 @@
"""
Synor Compiler SDK for Python
Smart contract compilation, optimization, and ABI generation.
"""
from .types import (
OptimizationLevel,
StripOptions,
CompilerConfig,
CompilationRequest,
CompilationResult,
ValidationResult,
ValidationError,
ContractMetadata,
ContractAbi,
FunctionAbi,
EventAbi,
ErrorAbi,
ParamAbi,
TypeInfo,
SourceMap,
SourceMapping,
ContractAnalysis,
SizeBreakdown,
FunctionAnalysis,
ImportAnalysis,
SecurityAnalysis,
SecurityIssue,
GasAnalysis,
CompilerError,
DEFAULT_STRIP_OPTIONS,
DEFAULT_CONFIG,
MAX_CONTRACT_SIZE,
MAX_MEMORY_PAGES,
)
from .client import SynorCompiler
__version__ = "0.1.0"
__all__ = [
"SynorCompiler",
"OptimizationLevel",
"StripOptions",
"CompilerConfig",
"CompilationRequest",
"CompilationResult",
"ValidationResult",
"ValidationError",
"ContractMetadata",
"ContractAbi",
"FunctionAbi",
"EventAbi",
"ErrorAbi",
"ParamAbi",
"TypeInfo",
"SourceMap",
"SourceMapping",
"ContractAnalysis",
"SizeBreakdown",
"FunctionAnalysis",
"ImportAnalysis",
"SecurityAnalysis",
"SecurityIssue",
"GasAnalysis",
"CompilerError",
"DEFAULT_STRIP_OPTIONS",
"DEFAULT_CONFIG",
"MAX_CONTRACT_SIZE",
"MAX_MEMORY_PAGES",
]

View file

@ -0,0 +1,378 @@
"""
Synor Compiler SDK Client
Smart contract compilation, optimization, and ABI generation.
"""
import base64
from typing import Any, Dict, List, Optional, Union
import httpx
from .types import (
OptimizationLevel,
StripOptions,
CompilerConfig,
CompilationResult,
ValidationResult,
ContractMetadata,
ContractAbi,
FunctionAbi,
SourceMap,
ContractAnalysis,
SecurityAnalysis,
CompilerError,
DEFAULT_STRIP_OPTIONS,
)
class SynorCompiler:
"""
Synor Compiler SDK Client
Smart contract compilation, optimization, and ABI generation.
Example:
>>> compiler = SynorCompiler(CompilerConfig(api_key="your-api-key"))
>>> result = await compiler.compile(wasm_bytes)
>>> print(f"Optimized size: {result.optimized_size} bytes")
"""
def __init__(self, config: CompilerConfig):
self._config = config
self._closed = False
self._client = httpx.AsyncClient(
base_url=config.endpoint,
timeout=config.timeout / 1000, # Convert ms to seconds
headers={
"Authorization": f"Bearer {config.api_key}",
"Content-Type": "application/json",
"X-SDK-Version": "python/0.1.0",
},
)
# Sub-clients
self.contracts = ContractsClient(self)
self.abi = AbiClient(self)
self.analysis = AnalysisClient(self)
self.validation = ValidationClient(self)
@property
def default_optimization_level(self) -> OptimizationLevel:
"""Get the default optimization level."""
return self._config.optimization_level
async def health_check(self) -> bool:
"""Check service health."""
try:
result = await self._get("/health")
return result.get("status") == "healthy"
except Exception:
return False
async def get_info(self) -> Dict[str, Any]:
"""Get service info."""
return await self._get("/info")
async def close(self) -> None:
"""Close the client."""
self._closed = True
await self._client.aclose()
@property
def is_closed(self) -> bool:
"""Check if client is closed."""
return self._closed
async def compile(
self,
wasm: Union[bytes, str],
optimization_level: Optional[OptimizationLevel] = None,
strip_options: Optional[StripOptions] = None,
use_wasm_opt: Optional[bool] = None,
validate: Optional[bool] = None,
extract_metadata: Optional[bool] = None,
generate_abi: Optional[bool] = None,
) -> CompilationResult:
"""
Compile WASM bytecode.
Args:
wasm: WASM bytecode as bytes or base64 string
optimization_level: Optimization level
strip_options: Strip options
use_wasm_opt: Whether to use wasm-opt
validate: Whether to validate
extract_metadata: Whether to extract metadata
generate_abi: Whether to generate ABI
Returns:
Compilation result
"""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
body = {
"wasm": wasm_base64,
"optimization_level": (optimization_level or self._config.optimization_level).value,
"strip_options": (strip_options or self._config.strip_options or DEFAULT_STRIP_OPTIONS).to_dict(),
"use_wasm_opt": use_wasm_opt if use_wasm_opt is not None else self._config.use_wasm_opt,
"validate": validate if validate is not None else self._config.validate,
"extract_metadata": extract_metadata if extract_metadata is not None else self._config.extract_metadata,
"generate_abi": generate_abi if generate_abi is not None else self._config.generate_abi,
}
result = await self._post("/compile", body)
return CompilationResult.from_dict(result)
async def _get(self, path: str) -> Dict[str, Any]:
"""Internal GET request."""
self._check_closed()
response = await self._client.get(path)
return self._handle_response(response)
async def _post(self, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Internal POST request."""
self._check_closed()
response = await self._client.post(path, json=body or {})
return self._handle_response(response)
def _handle_response(self, response: httpx.Response) -> Dict[str, Any]:
"""Handle HTTP response."""
if not response.is_success:
try:
error = response.json()
except Exception:
error = {"message": response.text or f"HTTP {response.status_code}"}
raise CompilerError(
message=error.get("message", f"HTTP {response.status_code}"),
code=error.get("code"),
http_status=response.status_code,
)
return response.json()
def _check_closed(self) -> None:
"""Check if client is closed."""
if self._closed:
raise CompilerError("Client has been closed", code="CLIENT_CLOSED")
async def __aenter__(self) -> "SynorCompiler":
return self
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
await self.close()
class ContractsClient:
"""Contracts sub-client for contract compilation."""
def __init__(self, compiler: SynorCompiler):
self._compiler = compiler
async def compile(
self,
wasm: Union[bytes, str],
**kwargs,
) -> CompilationResult:
"""Compile WASM bytecode with default settings."""
return await self._compiler.compile(wasm, **kwargs)
async def compile_dev(self, wasm: Union[bytes, str]) -> CompilationResult:
"""Compile with development settings (fast, minimal optimization)."""
return await self._compiler.compile(
wasm,
optimization_level=OptimizationLevel.NONE,
use_wasm_opt=False,
validate=True,
)
async def compile_production(self, wasm: Union[bytes, str]) -> CompilationResult:
"""Compile with production settings (aggressive optimization)."""
return await self._compiler.compile(
wasm,
optimization_level=OptimizationLevel.AGGRESSIVE,
use_wasm_opt=True,
validate=True,
extract_metadata=True,
generate_abi=True,
)
async def get(self, contract_id: str) -> CompilationResult:
"""Get a compiled contract by ID."""
result = await self._compiler._get(f"/contracts/{contract_id}")
return CompilationResult.from_dict(result)
async def list(
self,
limit: Optional[int] = None,
offset: Optional[int] = None,
) -> List[CompilationResult]:
"""List compiled contracts."""
params = []
if limit is not None:
params.append(f"limit={limit}")
if offset is not None:
params.append(f"offset={offset}")
query = "?" + "&".join(params) if params else ""
result = await self._compiler._get(f"/contracts{query}")
return [CompilationResult.from_dict(c) for c in result.get("contracts", [])]
async def get_code(self, contract_id: str) -> bytes:
"""Get optimized bytecode for a contract."""
result = await self._compiler._get(f"/contracts/{contract_id}/code")
return base64.b64decode(result.get("code", ""))
class AbiClient:
"""ABI sub-client for ABI generation and encoding."""
def __init__(self, compiler: SynorCompiler):
self._compiler = compiler
async def extract(self, wasm: Union[bytes, str]) -> ContractAbi:
"""Extract ABI from WASM bytecode."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/abi/extract", {"wasm": wasm_base64})
return ContractAbi.from_dict(result)
async def get(self, contract_id: str) -> ContractAbi:
"""Get ABI for a compiled contract."""
result = await self._compiler._get(f"/contracts/{contract_id}/abi")
return ContractAbi.from_dict(result)
async def encode_call(self, function_abi: FunctionAbi, args: List[Any]) -> str:
"""Encode function call."""
result = await self._compiler._post("/abi/encode", {
"function": {
"name": function_abi.name,
"selector": function_abi.selector,
"inputs": [{"name": i.name, "type": {"kind": i.type.kind}} for i in function_abi.inputs],
"outputs": [{"name": o.name, "type": {"kind": o.type.kind}} for o in function_abi.outputs],
},
"args": args,
})
return result.get("data", "")
async def decode_result(self, function_abi: FunctionAbi, data: str) -> List[Any]:
"""Decode function result."""
result = await self._compiler._post("/abi/decode", {
"function": {
"name": function_abi.name,
"selector": function_abi.selector,
"inputs": [{"name": i.name, "type": {"kind": i.type.kind}} for i in function_abi.inputs],
"outputs": [{"name": o.name, "type": {"kind": o.type.kind}} for o in function_abi.outputs],
},
"data": data,
})
return result.get("values", [])
def compute_selector(self, function_name: str) -> str:
"""Compute function selector."""
import hashlib
h = hashlib.blake2b(function_name.encode(), digest_size=4)
return h.hexdigest()
async def generate_types(self, abi: ContractAbi, language: str = "python") -> str:
"""Generate types from ABI."""
result = await self._compiler._post("/abi/generate-types", {
"abi": {
"name": abi.name,
"version": abi.version,
"functions": [{"name": f.name, "selector": f.selector} for f in abi.functions],
},
"language": language,
})
return result.get(language, "")
class AnalysisClient:
"""Analysis sub-client for contract analysis."""
def __init__(self, compiler: SynorCompiler):
self._compiler = compiler
async def analyze(self, wasm: Union[bytes, str]) -> ContractAnalysis:
"""Analyze WASM bytecode."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/analysis", {"wasm": wasm_base64})
return ContractAnalysis.from_dict(result)
async def get(self, contract_id: str) -> ContractAnalysis:
"""Get analysis for a compiled contract."""
result = await self._compiler._get(f"/contracts/{contract_id}/analysis")
return ContractAnalysis.from_dict(result)
async def extract_metadata(self, wasm: Union[bytes, str]) -> ContractMetadata:
"""Extract metadata from WASM bytecode."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/analysis/metadata", {"wasm": wasm_base64})
return ContractMetadata.from_dict(result)
async def extract_source_map(self, wasm: Union[bytes, str]) -> Optional[SourceMap]:
"""Extract source map from WASM bytecode."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/analysis/source-map", {"wasm": wasm_base64})
source_map = result.get("source_map")
return SourceMap.from_dict(source_map) if source_map else None
async def estimate_deploy_gas(self, wasm: Union[bytes, str]) -> int:
"""Estimate gas for contract deployment."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/analysis/estimate-gas", {"wasm": wasm_base64})
return result.get("gas", 0)
async def security_scan(self, wasm: Union[bytes, str]) -> SecurityAnalysis:
"""Get security analysis."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/analysis/security", {"wasm": wasm_base64})
return SecurityAnalysis.from_dict(result.get("security", {}))
class ValidationClient:
"""Validation sub-client for WASM validation."""
def __init__(self, compiler: SynorCompiler):
self._compiler = compiler
async def validate(self, wasm: Union[bytes, str]) -> ValidationResult:
"""Validate WASM bytecode against VM requirements."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/validate", {"wasm": wasm_base64})
return ValidationResult.from_dict(result)
async def is_valid(self, wasm: Union[bytes, str]) -> bool:
"""Check if WASM is valid."""
result = await self.validate(wasm)
return result.valid
async def get_errors(self, wasm: Union[bytes, str]) -> List[str]:
"""Get validation errors."""
result = await self.validate(wasm)
return [e.message for e in result.errors]
async def validate_exports(
self,
wasm: Union[bytes, str],
required_exports: List[str],
) -> bool:
"""Validate contract exports."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/validate/exports", {
"wasm": wasm_base64,
"required_exports": required_exports,
})
return result.get("valid", False)
async def validate_memory(
self,
wasm: Union[bytes, str],
max_pages: int,
) -> bool:
"""Validate memory limits."""
wasm_base64 = wasm if isinstance(wasm, str) else base64.b64encode(wasm).decode()
result = await self._compiler._post("/validate/memory", {
"wasm": wasm_base64,
"max_pages": max_pages,
})
return result.get("valid", False)

View file

@ -0,0 +1,588 @@
"""
Synor Compiler SDK Types
Smart contract compilation, optimization, and ABI generation.
"""
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional, Tuple, Union
# ============================================================================
# Enumerations
# ============================================================================
class OptimizationLevel(Enum):
"""Optimization level for WASM compilation."""
NONE = "none"
BASIC = "basic"
SIZE = "size"
AGGRESSIVE = "aggressive"
# ============================================================================
# Configuration Types
# ============================================================================
@dataclass
class StripOptions:
"""Options for stripping WASM sections."""
strip_debug: bool = True
strip_producers: bool = True
strip_names: bool = True
strip_custom: bool = True
preserve_sections: List[str] = field(default_factory=list)
strip_unused: bool = True
@staticmethod
def from_dict(data: Dict[str, Any]) -> "StripOptions":
return StripOptions(
strip_debug=data.get("strip_debug", True),
strip_producers=data.get("strip_producers", True),
strip_names=data.get("strip_names", True),
strip_custom=data.get("strip_custom", True),
preserve_sections=data.get("preserve_sections", []),
strip_unused=data.get("strip_unused", True),
)
def to_dict(self) -> Dict[str, Any]:
return {
"strip_debug": self.strip_debug,
"strip_producers": self.strip_producers,
"strip_names": self.strip_names,
"strip_custom": self.strip_custom,
"preserve_sections": self.preserve_sections,
"strip_unused": self.strip_unused,
}
@dataclass
class CompilerConfig:
"""Compiler configuration."""
api_key: str
endpoint: str = "https://compiler.synor.io/v1"
timeout: int = 60000
retries: int = 3
debug: bool = False
optimization_level: OptimizationLevel = OptimizationLevel.SIZE
strip_options: Optional[StripOptions] = None
max_contract_size: int = 256 * 1024
use_wasm_opt: bool = True
validate: bool = True
extract_metadata: bool = True
generate_abi: bool = True
# ============================================================================
# Compilation Types
# ============================================================================
@dataclass
class CompilationRequest:
"""Compilation request."""
wasm: str # Base64 encoded
optimization_level: Optional[OptimizationLevel] = None
strip_options: Optional[StripOptions] = None
use_wasm_opt: Optional[bool] = None
validate: Optional[bool] = None
extract_metadata: Optional[bool] = None
generate_abi: Optional[bool] = None
def to_dict(self) -> Dict[str, Any]:
result: Dict[str, Any] = {"wasm": self.wasm}
if self.optimization_level:
result["optimization_level"] = self.optimization_level.value
if self.strip_options:
result["strip_options"] = self.strip_options.to_dict()
if self.use_wasm_opt is not None:
result["use_wasm_opt"] = self.use_wasm_opt
if self.validate is not None:
result["validate"] = self.validate
if self.extract_metadata is not None:
result["extract_metadata"] = self.extract_metadata
if self.generate_abi is not None:
result["generate_abi"] = self.generate_abi
return result
@dataclass
class ValidationError:
"""Validation error."""
code: str
message: str
location: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ValidationError":
return ValidationError(
code=data.get("code", ""),
message=data.get("message", ""),
location=data.get("location"),
)
@dataclass
class ValidationResult:
"""Validation result."""
valid: bool
errors: List[ValidationError] = field(default_factory=list)
warnings: List[str] = field(default_factory=list)
export_count: int = 0
import_count: int = 0
function_count: int = 0
memory_pages: Tuple[int, Optional[int]] = (0, None)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ValidationResult":
return ValidationResult(
valid=data.get("valid", False),
errors=[ValidationError.from_dict(e) for e in data.get("errors", [])],
warnings=data.get("warnings", []),
export_count=data.get("export_count", 0),
import_count=data.get("import_count", 0),
function_count=data.get("function_count", 0),
memory_pages=tuple(data.get("memory_pages", [0, None])),
)
@dataclass
class ContractMetadata:
"""Contract metadata."""
name: Optional[str] = None
version: Optional[str] = None
authors: List[str] = field(default_factory=list)
description: Optional[str] = None
license: Optional[str] = None
repository: Optional[str] = None
build_timestamp: Optional[int] = None
rust_version: Optional[str] = None
sdk_version: Optional[str] = None
custom: Dict[str, str] = field(default_factory=dict)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ContractMetadata":
return ContractMetadata(
name=data.get("name"),
version=data.get("version"),
authors=data.get("authors", []),
description=data.get("description"),
license=data.get("license"),
repository=data.get("repository"),
build_timestamp=data.get("build_timestamp"),
rust_version=data.get("rust_version"),
sdk_version=data.get("sdk_version"),
custom=data.get("custom", {}),
)
# ============================================================================
# ABI Types
# ============================================================================
@dataclass
class TypeInfo:
"""Type information."""
kind: str
size: Optional[int] = None
element: Optional["TypeInfo"] = None
inner: Optional["TypeInfo"] = None
elements: Optional[List["TypeInfo"]] = None
name: Optional[str] = None
fields: Optional[List[Tuple[str, "TypeInfo"]]] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "TypeInfo":
kind = data.get("kind", "unknown")
return TypeInfo(
kind=kind,
size=data.get("size"),
element=TypeInfo.from_dict(data["element"]) if "element" in data else None,
inner=TypeInfo.from_dict(data["inner"]) if "inner" in data else None,
elements=[TypeInfo.from_dict(e) for e in data.get("elements", [])] if "elements" in data else None,
name=data.get("name"),
fields=[(f[0], TypeInfo.from_dict(f[1])) for f in data.get("fields", [])] if "fields" in data else None,
)
def type_name(self) -> str:
"""Get the type name as a string."""
if self.kind in ("u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128",
"bool", "string", "bytes", "address", "hash256"):
return self.kind
if self.kind == "fixedBytes":
return f"bytes{self.size}"
if self.kind == "array" and self.element:
return f"{self.element.type_name()}[]"
if self.kind == "fixedArray" and self.element:
return f"{self.element.type_name()}[{self.size}]"
if self.kind == "option" and self.inner:
return f"{self.inner.type_name()}?"
if self.kind == "tuple" and self.elements:
return f"({', '.join(e.type_name() for e in self.elements)})"
if self.kind == "struct" and self.name:
return self.name
return self.name or "unknown"
@dataclass
class ParamAbi:
"""Parameter ABI."""
name: str
type: TypeInfo
indexed: bool = False
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ParamAbi":
return ParamAbi(
name=data.get("name", ""),
type=TypeInfo.from_dict(data.get("type", {"kind": "unknown"})),
indexed=data.get("indexed", False),
)
@dataclass
class FunctionAbi:
"""Function ABI."""
name: str
selector: str
inputs: List[ParamAbi] = field(default_factory=list)
outputs: List[ParamAbi] = field(default_factory=list)
view: bool = False
payable: bool = False
doc: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "FunctionAbi":
return FunctionAbi(
name=data.get("name", ""),
selector=data.get("selector", ""),
inputs=[ParamAbi.from_dict(i) for i in data.get("inputs", [])],
outputs=[ParamAbi.from_dict(o) for o in data.get("outputs", [])],
view=data.get("view", False),
payable=data.get("payable", False),
doc=data.get("doc"),
)
@dataclass
class EventAbi:
"""Event ABI."""
name: str
topic: str
params: List[ParamAbi] = field(default_factory=list)
doc: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "EventAbi":
return EventAbi(
name=data.get("name", ""),
topic=data.get("topic", ""),
params=[ParamAbi.from_dict(p) for p in data.get("params", [])],
doc=data.get("doc"),
)
@dataclass
class ErrorAbi:
"""Error ABI."""
name: str
selector: str
params: List[ParamAbi] = field(default_factory=list)
doc: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ErrorAbi":
return ErrorAbi(
name=data.get("name", ""),
selector=data.get("selector", ""),
params=[ParamAbi.from_dict(p) for p in data.get("params", [])],
doc=data.get("doc"),
)
@dataclass
class ContractAbi:
"""Contract ABI (Application Binary Interface)."""
name: str
version: str
functions: List[FunctionAbi] = field(default_factory=list)
events: List[EventAbi] = field(default_factory=list)
errors: List[ErrorAbi] = field(default_factory=list)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ContractAbi":
return ContractAbi(
name=data.get("name", ""),
version=data.get("version", "1.0.0"),
functions=[FunctionAbi.from_dict(f) for f in data.get("functions", [])],
events=[EventAbi.from_dict(e) for e in data.get("events", [])],
errors=[ErrorAbi.from_dict(e) for e in data.get("errors", [])],
)
def find_function(self, name: str) -> Optional[FunctionAbi]:
"""Find a function by name."""
return next((f for f in self.functions if f.name == name), None)
def find_by_selector(self, selector: str) -> Optional[FunctionAbi]:
"""Find a function by selector."""
return next((f for f in self.functions if f.selector == selector), None)
@dataclass
class CompilationResult:
"""Compilation result."""
contract_id: str
code: str # Base64 encoded
code_hash: str
original_size: int
optimized_size: int
size_reduction: float
metadata: Optional[ContractMetadata] = None
abi: Optional[ContractAbi] = None
estimated_deploy_gas: int = 0
validation: Optional[ValidationResult] = None
warnings: List[str] = field(default_factory=list)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "CompilationResult":
return CompilationResult(
contract_id=data.get("contract_id", ""),
code=data.get("code", ""),
code_hash=data.get("code_hash", ""),
original_size=data.get("original_size", 0),
optimized_size=data.get("optimized_size", 0),
size_reduction=data.get("size_reduction", 0.0),
metadata=ContractMetadata.from_dict(data["metadata"]) if "metadata" in data and data["metadata"] else None,
abi=ContractAbi.from_dict(data["abi"]) if "abi" in data and data["abi"] else None,
estimated_deploy_gas=data.get("estimated_deploy_gas", 0),
validation=ValidationResult.from_dict(data["validation"]) if "validation" in data and data["validation"] else None,
warnings=data.get("warnings", []),
)
def size_stats(self) -> str:
"""Returns size statistics as a formatted string."""
return f"Original: {self.original_size} bytes, Optimized: {self.optimized_size} bytes, Reduction: {self.size_reduction:.1f}%"
# ============================================================================
# Source Map Types
# ============================================================================
@dataclass
class SourceMapping:
"""Source mapping entry."""
wasm_offset: int
line: int
column: int
function: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "SourceMapping":
return SourceMapping(
wasm_offset=data.get("wasm_offset", 0),
line=data.get("line", 0),
column=data.get("column", 0),
function=data.get("function"),
)
@dataclass
class SourceMap:
"""Source map for debugging."""
file: str
mappings: List[SourceMapping] = field(default_factory=list)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "SourceMap":
return SourceMap(
file=data.get("file", ""),
mappings=[SourceMapping.from_dict(m) for m in data.get("mappings", [])],
)
# ============================================================================
# Analysis Types
# ============================================================================
@dataclass
class SizeBreakdown:
"""Size breakdown."""
code: int = 0
data: int = 0
types: int = 0
functions: int = 0
memory: int = 0
table: int = 0
exports: int = 0
imports: int = 0
custom: int = 0
total: int = 0
@staticmethod
def from_dict(data: Dict[str, Any]) -> "SizeBreakdown":
return SizeBreakdown(
code=data.get("code", 0),
data=data.get("data", 0),
types=data.get("types", 0),
functions=data.get("functions", 0),
memory=data.get("memory", 0),
table=data.get("table", 0),
exports=data.get("exports", 0),
imports=data.get("imports", 0),
custom=data.get("custom", 0),
total=data.get("total", 0),
)
@dataclass
class FunctionAnalysis:
"""Function analysis."""
name: str
size: int
instruction_count: int
local_count: int
exported: bool
estimated_gas: int
@staticmethod
def from_dict(data: Dict[str, Any]) -> "FunctionAnalysis":
return FunctionAnalysis(
name=data.get("name", ""),
size=data.get("size", 0),
instruction_count=data.get("instruction_count", 0),
local_count=data.get("local_count", 0),
exported=data.get("exported", False),
estimated_gas=data.get("estimated_gas", 0),
)
@dataclass
class ImportAnalysis:
"""Import analysis."""
module: str
name: str
kind: str # function, memory, table, global
signature: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ImportAnalysis":
return ImportAnalysis(
module=data.get("module", ""),
name=data.get("name", ""),
kind=data.get("kind", "function"),
signature=data.get("signature"),
)
@dataclass
class SecurityIssue:
"""Security issue."""
severity: str # low, medium, high, critical
type: str
description: str
location: Optional[str] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "SecurityIssue":
return SecurityIssue(
severity=data.get("severity", "low"),
type=data.get("type", ""),
description=data.get("description", ""),
location=data.get("location"),
)
@dataclass
class SecurityAnalysis:
"""Security analysis."""
score: int # 0-100
issues: List[SecurityIssue] = field(default_factory=list)
recommendations: List[str] = field(default_factory=list)
@staticmethod
def from_dict(data: Dict[str, Any]) -> "SecurityAnalysis":
return SecurityAnalysis(
score=data.get("score", 0),
issues=[SecurityIssue.from_dict(i) for i in data.get("issues", [])],
recommendations=data.get("recommendations", []),
)
@dataclass
class GasAnalysis:
"""Gas analysis."""
deployment_gas: int
function_gas: Dict[str, int] = field(default_factory=dict)
memory_init_gas: int = 0
data_section_gas: int = 0
@staticmethod
def from_dict(data: Dict[str, Any]) -> "GasAnalysis":
return GasAnalysis(
deployment_gas=data.get("deployment_gas", 0),
function_gas=data.get("function_gas", {}),
memory_init_gas=data.get("memory_init_gas", 0),
data_section_gas=data.get("data_section_gas", 0),
)
@dataclass
class ContractAnalysis:
"""Contract analysis result."""
size_breakdown: SizeBreakdown
functions: List[FunctionAnalysis] = field(default_factory=list)
imports: List[ImportAnalysis] = field(default_factory=list)
security: Optional[SecurityAnalysis] = None
gas_analysis: Optional[GasAnalysis] = None
@staticmethod
def from_dict(data: Dict[str, Any]) -> "ContractAnalysis":
return ContractAnalysis(
size_breakdown=SizeBreakdown.from_dict(data.get("size_breakdown", {})),
functions=[FunctionAnalysis.from_dict(f) for f in data.get("functions", [])],
imports=[ImportAnalysis.from_dict(i) for i in data.get("imports", [])],
security=SecurityAnalysis.from_dict(data["security"]) if "security" in data and data["security"] else None,
gas_analysis=GasAnalysis.from_dict(data["gas_analysis"]) if "gas_analysis" in data and data["gas_analysis"] else None,
)
# ============================================================================
# Error Types
# ============================================================================
class CompilerError(Exception):
"""Compiler error."""
def __init__(
self,
message: str,
code: Optional[str] = None,
http_status: Optional[int] = None,
details: Optional[Dict[str, Any]] = None,
):
super().__init__(message)
self.code = code
self.http_status = http_status
self.details = details or {}
# ============================================================================
# Constants
# ============================================================================
MAX_CONTRACT_SIZE = 256 * 1024 # 256 KB
MAX_MEMORY_PAGES = 16
DEFAULT_STRIP_OPTIONS = StripOptions()
DEFAULT_CONFIG: Dict[str, Any] = {
"endpoint": "https://compiler.synor.io/v1",
"timeout": 60000,
"retries": 3,
"debug": False,
"optimization_level": OptimizationLevel.SIZE,
"use_wasm_opt": True,
"validate": True,
"extract_metadata": True,
"generate_abi": True,
}

View file

@ -0,0 +1,643 @@
# frozen_string_literal: true
require 'net/http'
require 'json'
require 'uri'
require 'base64'
module Synor
module Compiler
# Constants
MAX_CONTRACT_SIZE = 256 * 1024
MAX_MEMORY_PAGES = 16
# Optimization levels
module OptimizationLevel
NONE = 'none'
BASIC = 'basic'
SIZE = 'size'
AGGRESSIVE = 'aggressive'
end
# Strip options for WASM sections
class StripOptions
attr_accessor :strip_debug, :strip_producers, :strip_names,
:strip_custom, :preserve_sections, :strip_unused
def initialize(
strip_debug: true,
strip_producers: true,
strip_names: true,
strip_custom: true,
preserve_sections: [],
strip_unused: true
)
@strip_debug = strip_debug
@strip_producers = strip_producers
@strip_names = strip_names
@strip_custom = strip_custom
@preserve_sections = preserve_sections
@strip_unused = strip_unused
end
def to_h
{
strip_debug: @strip_debug,
strip_producers: @strip_producers,
strip_names: @strip_names,
strip_custom: @strip_custom,
preserve_sections: @preserve_sections,
strip_unused: @strip_unused
}
end
end
# Compiler configuration
class Config
attr_accessor :api_key, :endpoint, :timeout, :retries, :debug,
:optimization_level, :strip_options, :max_contract_size,
:use_wasm_opt, :validate, :extract_metadata, :generate_abi
def initialize(
api_key:,
endpoint: 'https://compiler.synor.io/v1',
timeout: 60,
retries: 3,
debug: false,
optimization_level: OptimizationLevel::SIZE,
strip_options: nil,
max_contract_size: MAX_CONTRACT_SIZE,
use_wasm_opt: true,
validate: true,
extract_metadata: true,
generate_abi: true
)
@api_key = api_key
@endpoint = endpoint
@timeout = timeout
@retries = retries
@debug = debug
@optimization_level = optimization_level
@strip_options = strip_options
@max_contract_size = max_contract_size
@use_wasm_opt = use_wasm_opt
@validate = validate
@extract_metadata = extract_metadata
@generate_abi = generate_abi
end
end
# Compiler error
class CompilerError < StandardError
attr_reader :code, :http_status
def initialize(message, code: nil, http_status: nil)
super(message)
@code = code
@http_status = http_status
end
end
# Type information
class TypeInfo
attr_accessor :kind, :size, :element, :inner, :elements, :name, :fields
def initialize(data)
@kind = data['kind'] || 'unknown'
@size = data['size']
@element = data['element'] ? TypeInfo.new(data['element']) : nil
@inner = data['inner'] ? TypeInfo.new(data['inner']) : nil
@elements = data['elements']&.map { |e| TypeInfo.new(e) }
@name = data['name']
@fields = data['fields']
end
def type_name
case @kind
when 'u8', 'u16', 'u32', 'u64', 'u128', 'i8', 'i16', 'i32', 'i64', 'i128',
'bool', 'string', 'bytes', 'address', 'hash256'
@kind
when 'fixedBytes'
"bytes#{@size || 0}"
when 'array'
"#{@element&.type_name}[]"
when 'fixedArray'
"#{@element&.type_name}[#{@size || 0}]"
when 'option'
"#{@inner&.type_name}?"
when 'tuple'
"(#{@elements&.map(&:type_name)&.join(', ')})"
when 'struct'
@name || 'struct'
else
@name || 'unknown'
end
end
end
# Parameter ABI
class ParamAbi
attr_accessor :name, :type, :indexed
def initialize(data)
@name = data['name'] || ''
@type = TypeInfo.new(data['type'] || { 'kind' => 'unknown' })
@indexed = data['indexed'] || false
end
end
# Function ABI
class FunctionAbi
attr_accessor :name, :selector, :inputs, :outputs, :view, :payable, :doc
def initialize(data)
@name = data['name'] || ''
@selector = data['selector'] || ''
@inputs = (data['inputs'] || []).map { |i| ParamAbi.new(i) }
@outputs = (data['outputs'] || []).map { |o| ParamAbi.new(o) }
@view = data['view'] || false
@payable = data['payable'] || false
@doc = data['doc']
end
end
# Event ABI
class EventAbi
attr_accessor :name, :topic, :params, :doc
def initialize(data)
@name = data['name'] || ''
@topic = data['topic'] || ''
@params = (data['params'] || []).map { |p| ParamAbi.new(p) }
@doc = data['doc']
end
end
# Error ABI
class ErrorAbi
attr_accessor :name, :selector, :params, :doc
def initialize(data)
@name = data['name'] || ''
@selector = data['selector'] || ''
@params = (data['params'] || []).map { |p| ParamAbi.new(p) }
@doc = data['doc']
end
end
# Contract ABI
class ContractAbi
attr_accessor :name, :version, :functions, :events, :errors
def initialize(data)
@name = data['name'] || ''
@version = data['version'] || '1.0.0'
@functions = (data['functions'] || []).map { |f| FunctionAbi.new(f) }
@events = (data['events'] || []).map { |e| EventAbi.new(e) }
@errors = (data['errors'] || []).map { |e| ErrorAbi.new(e) }
end
def find_function(name)
@functions.find { |f| f.name == name }
end
def find_by_selector(selector)
@functions.find { |f| f.selector == selector }
end
end
# Validation error
class ValidationError
attr_accessor :code, :message, :location
def initialize(data)
@code = data['code'] || ''
@message = data['message'] || ''
@location = data['location']
end
end
# Validation result
class ValidationResult
attr_accessor :valid, :errors, :warnings, :export_count,
:import_count, :function_count, :memory_pages
def initialize(data)
@valid = data['valid'] || false
@errors = (data['errors'] || []).map { |e| ValidationError.new(e) }
@warnings = data['warnings'] || []
@export_count = data['export_count'] || 0
@import_count = data['import_count'] || 0
@function_count = data['function_count'] || 0
@memory_pages = data['memory_pages'] || [0, nil]
end
end
# Contract metadata
class ContractMetadata
attr_accessor :name, :version, :authors, :description, :license,
:repository, :build_timestamp, :rust_version, :sdk_version, :custom
def initialize(data)
@name = data['name']
@version = data['version']
@authors = data['authors'] || []
@description = data['description']
@license = data['license']
@repository = data['repository']
@build_timestamp = data['build_timestamp']
@rust_version = data['rust_version']
@sdk_version = data['sdk_version']
@custom = data['custom'] || {}
end
end
# Compilation result
class CompilationResult
attr_accessor :contract_id, :code, :code_hash, :original_size, :optimized_size,
:size_reduction, :metadata, :abi, :estimated_deploy_gas,
:validation, :warnings
def initialize(data)
@contract_id = data['contract_id'] || ''
@code = data['code'] || ''
@code_hash = data['code_hash'] || ''
@original_size = data['original_size'] || 0
@optimized_size = data['optimized_size'] || 0
@size_reduction = data['size_reduction'] || 0.0
@metadata = data['metadata'] ? ContractMetadata.new(data['metadata']) : nil
@abi = data['abi'] ? ContractAbi.new(data['abi']) : nil
@estimated_deploy_gas = data['estimated_deploy_gas'] || 0
@validation = data['validation'] ? ValidationResult.new(data['validation']) : nil
@warnings = data['warnings'] || []
end
def size_stats
"Original: #{@original_size} bytes, Optimized: #{@optimized_size} bytes, Reduction: #{format('%.1f', @size_reduction)}%"
end
def decode_code
Base64.decode64(@code)
end
end
# Size breakdown
class SizeBreakdown
attr_accessor :code, :data, :types, :functions, :memory,
:table, :exports, :imports, :custom, :total
def initialize(data)
@code = data['code'] || 0
@data = data['data'] || 0
@types = data['types'] || 0
@functions = data['functions'] || 0
@memory = data['memory'] || 0
@table = data['table'] || 0
@exports = data['exports'] || 0
@imports = data['imports'] || 0
@custom = data['custom'] || 0
@total = data['total'] || 0
end
end
# Function analysis
class FunctionAnalysis
attr_accessor :name, :size, :instruction_count, :local_count, :exported, :estimated_gas
def initialize(data)
@name = data['name'] || ''
@size = data['size'] || 0
@instruction_count = data['instruction_count'] || 0
@local_count = data['local_count'] || 0
@exported = data['exported'] || false
@estimated_gas = data['estimated_gas'] || 0
end
end
# Import analysis
class ImportAnalysis
attr_accessor :mod, :name, :kind, :signature
def initialize(data)
@mod = data['module'] || ''
@name = data['name'] || ''
@kind = data['kind'] || 'function'
@signature = data['signature']
end
end
# Security issue
class SecurityIssue
attr_accessor :severity, :type, :description, :location
def initialize(data)
@severity = data['severity'] || 'low'
@type = data['type'] || ''
@description = data['description'] || ''
@location = data['location']
end
end
# Security analysis
class SecurityAnalysis
attr_accessor :score, :issues, :recommendations
def initialize(data)
@score = data['score'] || 0
@issues = (data['issues'] || []).map { |i| SecurityIssue.new(i) }
@recommendations = data['recommendations'] || []
end
end
# Gas analysis
class GasAnalysis
attr_accessor :deployment_gas, :function_gas, :memory_init_gas, :data_section_gas
def initialize(data)
@deployment_gas = data['deployment_gas'] || 0
@function_gas = data['function_gas'] || {}
@memory_init_gas = data['memory_init_gas'] || 0
@data_section_gas = data['data_section_gas'] || 0
end
end
# Contract analysis
class ContractAnalysis
attr_accessor :size_breakdown, :functions, :imports, :security, :gas_analysis
def initialize(data)
@size_breakdown = SizeBreakdown.new(data['size_breakdown'] || {})
@functions = (data['functions'] || []).map { |f| FunctionAnalysis.new(f) }
@imports = (data['imports'] || []).map { |i| ImportAnalysis.new(i) }
@security = data['security'] ? SecurityAnalysis.new(data['security']) : nil
@gas_analysis = data['gas_analysis'] ? GasAnalysis.new(data['gas_analysis']) : nil
end
end
# Main compiler client
class Client
attr_reader :contracts, :abi, :analysis, :validation
def initialize(config)
@config = config
@closed = false
@contracts = ContractsClient.new(self)
@abi = AbiClient.new(self)
@analysis = AnalysisClient.new(self)
@validation = ValidationClient.new(self)
end
def default_optimization_level
@config.optimization_level
end
def health_check
result = get('/health')
result['status'] == 'healthy'
rescue StandardError
false
end
def info
get('/info')
end
def close
@closed = true
end
def closed?
@closed
end
def compile(wasm, **options)
wasm_base64 = Base64.strict_encode64(wasm)
body = {
wasm: wasm_base64,
optimization_level: options[:optimization_level] || @config.optimization_level,
use_wasm_opt: options.key?(:use_wasm_opt) ? options[:use_wasm_opt] : @config.use_wasm_opt,
validate: options.key?(:validate) ? options[:validate] : @config.validate,
extract_metadata: options.key?(:extract_metadata) ? options[:extract_metadata] : @config.extract_metadata,
generate_abi: options.key?(:generate_abi) ? options[:generate_abi] : @config.generate_abi
}
body[:strip_options] = options[:strip_options].to_h if options[:strip_options]
result = post('/compile', body)
CompilationResult.new(result)
end
def get(path)
check_closed!
uri = URI("#{@config.endpoint}#{path}")
request = Net::HTTP::Get.new(uri)
execute_request(uri, request)
end
def post(path, body)
check_closed!
uri = URI("#{@config.endpoint}#{path}")
request = Net::HTTP::Post.new(uri)
request.body = body.to_json
execute_request(uri, request)
end
private
def execute_request(uri, request)
request['Authorization'] = "Bearer #{@config.api_key}"
request['Content-Type'] = 'application/json'
request['X-SDK-Version'] = 'ruby/0.1.0'
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = @config.timeout
http.open_timeout = @config.timeout
response = http.request(request)
handle_response(response)
end
def handle_response(response)
body = JSON.parse(response.body)
unless response.is_a?(Net::HTTPSuccess)
raise CompilerError.new(
body['message'] || "HTTP #{response.code}",
code: body['code'],
http_status: response.code.to_i
)
end
body
end
def check_closed!
raise CompilerError.new('Client has been closed', code: 'CLIENT_CLOSED') if @closed
end
end
# Contracts sub-client
class ContractsClient
def initialize(client)
@client = client
end
def compile(wasm, **options)
@client.compile(wasm, **options)
end
def compile_dev(wasm)
@client.compile(wasm, optimization_level: OptimizationLevel::NONE, use_wasm_opt: false, validate: true)
end
def compile_production(wasm)
@client.compile(wasm,
optimization_level: OptimizationLevel::AGGRESSIVE,
use_wasm_opt: true,
validate: true,
extract_metadata: true,
generate_abi: true)
end
def get(contract_id)
result = @client.get("/contracts/#{contract_id}")
CompilationResult.new(result)
end
def list(limit: nil, offset: nil)
params = []
params << "limit=#{limit}" if limit
params << "offset=#{offset}" if offset
path = "/contracts#{params.empty? ? '' : "?#{params.join('&')}"}"
result = @client.get(path)
(result['contracts'] || []).map { |c| CompilationResult.new(c) }
end
def get_code(contract_id)
result = @client.get("/contracts/#{contract_id}/code")
Base64.decode64(result['code'] || '')
end
end
# ABI sub-client
class AbiClient
def initialize(client)
@client = client
end
def extract(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/abi/extract', { wasm: wasm_base64 })
ContractAbi.new(result)
end
def get(contract_id)
result = @client.get("/contracts/#{contract_id}/abi")
ContractAbi.new(result)
end
def encode_call(function_abi, args)
result = @client.post('/abi/encode', {
function: {
name: function_abi.name,
selector: function_abi.selector,
inputs: function_abi.inputs.map { |i| { name: i.name, type: { kind: i.type.kind } } },
outputs: function_abi.outputs.map { |o| { name: o.name, type: { kind: o.type.kind } } }
},
args: args
})
result['data'] || ''
end
def decode_result(function_abi, data)
result = @client.post('/abi/decode', {
function: {
name: function_abi.name,
selector: function_abi.selector
},
data: data
})
result['values'] || []
end
end
# Analysis sub-client
class AnalysisClient
def initialize(client)
@client = client
end
def analyze(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/analysis', { wasm: wasm_base64 })
ContractAnalysis.new(result)
end
def get(contract_id)
result = @client.get("/contracts/#{contract_id}/analysis")
ContractAnalysis.new(result)
end
def extract_metadata(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/analysis/metadata', { wasm: wasm_base64 })
ContractMetadata.new(result)
end
def estimate_deploy_gas(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/analysis/estimate-gas', { wasm: wasm_base64 })
result['gas'] || 0
end
def security_scan(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/analysis/security', { wasm: wasm_base64 })
SecurityAnalysis.new(result['security'] || {})
end
end
# Validation sub-client
class ValidationClient
def initialize(client)
@client = client
end
def validate(wasm)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/validate', { wasm: wasm_base64 })
ValidationResult.new(result)
end
def valid?(wasm)
validate(wasm).valid
end
def errors(wasm)
validate(wasm).errors.map(&:message)
end
def validate_exports(wasm, required_exports)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/validate/exports', {
wasm: wasm_base64,
required_exports: required_exports
})
result['valid'] || false
end
def validate_memory(wasm, max_pages)
wasm_base64 = Base64.strict_encode64(wasm)
result = @client.post('/validate/memory', {
wasm: wasm_base64,
max_pages: max_pages
})
result['valid'] || false
end
end
end
end

View file

@ -0,0 +1,562 @@
//! Synor Compiler SDK Client
//!
//! Smart contract compilation, optimization, and ABI generation.
use base64::Engine;
use reqwest::Client;
use serde::de::DeserializeOwned;
use serde_json::json;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use super::types::*;
/// Synor Compiler SDK Client.
///
/// Smart contract compilation, optimization, and ABI generation.
///
/// # Example
///
/// ```ignore
/// use synor_sdk::compiler::{SynorCompiler, CompilerConfig, OptimizationLevel};
///
/// let config = CompilerConfig::new("your-api-key");
/// let compiler = SynorCompiler::new(config)?;
///
/// let result = compiler.compile(&wasm_bytes, None).await?;
/// println!("Optimized size: {} bytes", result.optimized_size);
/// ```
pub struct SynorCompiler {
config: CompilerConfig,
client: Client,
closed: Arc<AtomicBool>,
}
impl SynorCompiler {
/// Create a new compiler client.
pub fn new(config: CompilerConfig) -> Result<Self, CompilerError> {
let client = Client::builder()
.timeout(Duration::from_millis(config.timeout_ms))
.build()
.map_err(|e| CompilerError {
message: e.to_string(),
code: Some("HTTP_CLIENT_ERROR".to_string()),
http_status: None,
})?;
Ok(SynorCompiler {
config,
client,
closed: Arc::new(AtomicBool::new(false)),
})
}
/// Get the default optimization level.
pub fn default_optimization_level(&self) -> OptimizationLevel {
self.config.optimization_level
}
/// Check service health.
pub async fn health_check(&self) -> bool {
match self.get::<serde_json::Value>("/health").await {
Ok(result) => result.get("status").and_then(|v| v.as_str()) == Some("healthy"),
Err(_) => false,
}
}
/// Get service info.
pub async fn get_info(&self) -> Result<serde_json::Value, CompilerError> {
self.get("/info").await
}
/// Close the client.
pub fn close(&self) {
self.closed.store(true, Ordering::SeqCst);
}
/// Check if client is closed.
pub fn is_closed(&self) -> bool {
self.closed.load(Ordering::SeqCst)
}
/// Compile WASM bytecode.
pub async fn compile(
&self,
wasm: &[u8],
opts: Option<CompilationRequest>,
) -> Result<CompilationResult, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
let body = if let Some(opts) = opts {
json!({
"wasm": wasm_base64,
"optimization_level": opts.optimization_level.unwrap_or(self.config.optimization_level),
"strip_options": opts.strip_options.or(self.config.strip_options.clone()),
"use_wasm_opt": opts.use_wasm_opt.unwrap_or(self.config.use_wasm_opt),
"validate": opts.validate.unwrap_or(self.config.validate),
"extract_metadata": opts.extract_metadata.unwrap_or(self.config.extract_metadata),
"generate_abi": opts.generate_abi.unwrap_or(self.config.generate_abi),
})
} else {
json!({
"wasm": wasm_base64,
"optimization_level": self.config.optimization_level,
"use_wasm_opt": self.config.use_wasm_opt,
"validate": self.config.validate,
"extract_metadata": self.config.extract_metadata,
"generate_abi": self.config.generate_abi,
})
};
self.post("/compile", body).await
}
/// Get sub-client for contracts.
pub fn contracts(&self) -> ContractsClient<'_> {
ContractsClient { compiler: self }
}
/// Get sub-client for ABI operations.
pub fn abi(&self) -> AbiClient<'_> {
AbiClient { compiler: self }
}
/// Get sub-client for analysis.
pub fn analysis(&self) -> AnalysisClient<'_> {
AnalysisClient { compiler: self }
}
/// Get sub-client for validation.
pub fn validation(&self) -> ValidationClient<'_> {
ValidationClient { compiler: self }
}
async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T, CompilerError> {
self.check_closed()?;
let response = self
.client
.get(format!("{}{}", self.config.endpoint, path))
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("Content-Type", "application/json")
.header("X-SDK-Version", "rust/0.1.0")
.send()
.await
.map_err(|e| CompilerError {
message: e.to_string(),
code: Some("NETWORK_ERROR".to_string()),
http_status: None,
})?;
self.handle_response(response).await
}
async fn post<T: DeserializeOwned>(
&self,
path: &str,
body: serde_json::Value,
) -> Result<T, CompilerError> {
self.check_closed()?;
let response = self
.client
.post(format!("{}{}", self.config.endpoint, path))
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("Content-Type", "application/json")
.header("X-SDK-Version", "rust/0.1.0")
.json(&body)
.send()
.await
.map_err(|e| CompilerError {
message: e.to_string(),
code: Some("NETWORK_ERROR".to_string()),
http_status: None,
})?;
self.handle_response(response).await
}
async fn handle_response<T: DeserializeOwned>(
&self,
response: reqwest::Response,
) -> Result<T, CompilerError> {
let status = response.status();
if !status.is_success() {
let text = response.text().await.unwrap_or_default();
let error: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
return Err(CompilerError {
message: error
.get("message")
.and_then(|v| v.as_str())
.unwrap_or(&format!("HTTP {}", status.as_u16()))
.to_string(),
code: error
.get("code")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
http_status: Some(status.as_u16()),
});
}
response.json().await.map_err(|e| CompilerError {
message: e.to_string(),
code: Some("PARSE_ERROR".to_string()),
http_status: None,
})
}
fn check_closed(&self) -> Result<(), CompilerError> {
if self.closed.load(Ordering::SeqCst) {
return Err(CompilerError {
message: "Client has been closed".to_string(),
code: Some("CLIENT_CLOSED".to_string()),
http_status: None,
});
}
Ok(())
}
}
/// Contracts sub-client.
pub struct ContractsClient<'a> {
compiler: &'a SynorCompiler,
}
impl<'a> ContractsClient<'a> {
/// Compile WASM bytecode with default settings.
pub async fn compile(
&self,
wasm: &[u8],
opts: Option<CompilationRequest>,
) -> Result<CompilationResult, CompilerError> {
self.compiler.compile(wasm, opts).await
}
/// Compile with development settings.
pub async fn compile_dev(&self, wasm: &[u8]) -> Result<CompilationResult, CompilerError> {
self.compiler
.compile(
wasm,
Some(CompilationRequest {
wasm: String::new(),
optimization_level: Some(OptimizationLevel::None),
strip_options: None,
use_wasm_opt: Some(false),
validate: Some(true),
extract_metadata: None,
generate_abi: None,
}),
)
.await
}
/// Compile with production settings.
pub async fn compile_production(
&self,
wasm: &[u8],
) -> Result<CompilationResult, CompilerError> {
self.compiler
.compile(
wasm,
Some(CompilationRequest {
wasm: String::new(),
optimization_level: Some(OptimizationLevel::Aggressive),
strip_options: None,
use_wasm_opt: Some(true),
validate: Some(true),
extract_metadata: Some(true),
generate_abi: Some(true),
}),
)
.await
}
/// Get a compiled contract by ID.
pub async fn get(&self, contract_id: &str) -> Result<CompilationResult, CompilerError> {
self.compiler
.get(&format!("/contracts/{}", contract_id))
.await
}
/// List compiled contracts.
pub async fn list(
&self,
limit: Option<u32>,
offset: Option<u32>,
) -> Result<Vec<CompilationResult>, CompilerError> {
let mut path = "/contracts".to_string();
let mut params = Vec::new();
if let Some(l) = limit {
params.push(format!("limit={}", l));
}
if let Some(o) = offset {
params.push(format!("offset={}", o));
}
if !params.is_empty() {
path.push('?');
path.push_str(&params.join("&"));
}
#[derive(serde::Deserialize)]
struct Response {
contracts: Vec<CompilationResult>,
}
let result: Response = self.compiler.get(&path).await?;
Ok(result.contracts)
}
/// Get optimized bytecode for a contract.
pub async fn get_code(&self, contract_id: &str) -> Result<Vec<u8>, CompilerError> {
#[derive(serde::Deserialize)]
struct Response {
code: String,
}
let result: Response = self
.compiler
.get(&format!("/contracts/{}/code", contract_id))
.await?;
base64::engine::general_purpose::STANDARD
.decode(&result.code)
.map_err(|e| CompilerError {
message: e.to_string(),
code: Some("DECODE_ERROR".to_string()),
http_status: None,
})
}
}
/// ABI sub-client.
pub struct AbiClient<'a> {
compiler: &'a SynorCompiler,
}
impl<'a> AbiClient<'a> {
/// Extract ABI from WASM bytecode.
pub async fn extract(&self, wasm: &[u8]) -> Result<ContractAbi, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
self.compiler
.post("/abi/extract", json!({ "wasm": wasm_base64 }))
.await
}
/// Get ABI for a compiled contract.
pub async fn get(&self, contract_id: &str) -> Result<ContractAbi, CompilerError> {
self.compiler
.get(&format!("/contracts/{}/abi", contract_id))
.await
}
/// Encode function call.
pub async fn encode_call(
&self,
function_abi: &FunctionAbi,
args: Vec<serde_json::Value>,
) -> Result<String, CompilerError> {
#[derive(serde::Deserialize)]
struct Response {
data: String,
}
let result: Response = self
.compiler
.post(
"/abi/encode",
json!({
"function": function_abi,
"args": args,
}),
)
.await?;
Ok(result.data)
}
/// Decode function result.
pub async fn decode_result(
&self,
function_abi: &FunctionAbi,
data: &str,
) -> Result<Vec<serde_json::Value>, CompilerError> {
#[derive(serde::Deserialize)]
struct Response {
values: Vec<serde_json::Value>,
}
let result: Response = self
.compiler
.post(
"/abi/decode",
json!({
"function": function_abi,
"data": data,
}),
)
.await?;
Ok(result.values)
}
}
/// Analysis sub-client.
pub struct AnalysisClient<'a> {
compiler: &'a SynorCompiler,
}
impl<'a> AnalysisClient<'a> {
/// Analyze WASM bytecode.
pub async fn analyze(&self, wasm: &[u8]) -> Result<ContractAnalysis, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
self.compiler
.post("/analysis", json!({ "wasm": wasm_base64 }))
.await
}
/// Get analysis for a compiled contract.
pub async fn get(&self, contract_id: &str) -> Result<ContractAnalysis, CompilerError> {
self.compiler
.get(&format!("/contracts/{}/analysis", contract_id))
.await
}
/// Extract metadata from WASM bytecode.
pub async fn extract_metadata(&self, wasm: &[u8]) -> Result<ContractMetadata, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
self.compiler
.post("/analysis/metadata", json!({ "wasm": wasm_base64 }))
.await
}
/// Extract source map from WASM bytecode.
pub async fn extract_source_map(&self, wasm: &[u8]) -> Result<Option<SourceMap>, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
#[derive(serde::Deserialize)]
struct Response {
source_map: Option<SourceMap>,
}
let result: Response = self
.compiler
.post("/analysis/source-map", json!({ "wasm": wasm_base64 }))
.await?;
Ok(result.source_map)
}
/// Estimate gas for contract deployment.
pub async fn estimate_deploy_gas(&self, wasm: &[u8]) -> Result<u64, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
#[derive(serde::Deserialize)]
struct Response {
gas: u64,
}
let result: Response = self
.compiler
.post("/analysis/estimate-gas", json!({ "wasm": wasm_base64 }))
.await?;
Ok(result.gas)
}
/// Get security analysis.
pub async fn security_scan(&self, wasm: &[u8]) -> Result<SecurityAnalysis, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
#[derive(serde::Deserialize)]
struct Response {
security: SecurityAnalysis,
}
let result: Response = self
.compiler
.post("/analysis/security", json!({ "wasm": wasm_base64 }))
.await?;
Ok(result.security)
}
}
/// Validation sub-client.
pub struct ValidationClient<'a> {
compiler: &'a SynorCompiler,
}
impl<'a> ValidationClient<'a> {
/// Validate WASM bytecode against VM requirements.
pub async fn validate(&self, wasm: &[u8]) -> Result<ValidationResult, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
self.compiler
.post("/validate", json!({ "wasm": wasm_base64 }))
.await
}
/// Check if WASM is valid.
pub async fn is_valid(&self, wasm: &[u8]) -> Result<bool, CompilerError> {
let result = self.validate(wasm).await?;
Ok(result.valid)
}
/// Get validation errors.
pub async fn get_errors(&self, wasm: &[u8]) -> Result<Vec<String>, CompilerError> {
let result = self.validate(wasm).await?;
Ok(result.errors.into_iter().map(|e| e.message).collect())
}
/// Validate contract exports.
pub async fn validate_exports(
&self,
wasm: &[u8],
required_exports: Vec<String>,
) -> Result<bool, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
#[derive(serde::Deserialize)]
struct Response {
valid: bool,
}
let result: Response = self
.compiler
.post(
"/validate/exports",
json!({
"wasm": wasm_base64,
"required_exports": required_exports,
}),
)
.await?;
Ok(result.valid)
}
/// Validate memory limits.
pub async fn validate_memory(&self, wasm: &[u8], max_pages: u32) -> Result<bool, CompilerError> {
let wasm_base64 = base64::engine::general_purpose::STANDARD.encode(wasm);
#[derive(serde::Deserialize)]
struct Response {
valid: bool,
}
let result: Response = self
.compiler
.post(
"/validate/memory",
json!({
"wasm": wasm_base64,
"max_pages": max_pages,
}),
)
.await?;
Ok(result.valid)
}
}

View file

@ -0,0 +1,9 @@
//! Synor Compiler SDK for Rust
//!
//! Smart contract compilation, optimization, and ABI generation.
pub mod types;
pub mod client;
pub use types::*;
pub use client::*;

View file

@ -0,0 +1,490 @@
//! Synor Compiler SDK Types
//!
//! Smart contract compilation, optimization, and ABI generation.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Optimization level for WASM compilation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OptimizationLevel {
/// No optimization.
None,
/// Basic optimizations (stripping only).
Basic,
/// Optimize for size (-Os equivalent).
Size,
/// Aggressive optimization (-O3 -Os equivalent).
Aggressive,
}
impl Default for OptimizationLevel {
fn default() -> Self {
OptimizationLevel::Size
}
}
/// Options for stripping WASM sections.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StripOptions {
pub strip_debug: bool,
pub strip_producers: bool,
pub strip_names: bool,
pub strip_custom: bool,
pub preserve_sections: Vec<String>,
pub strip_unused: bool,
}
impl Default for StripOptions {
fn default() -> Self {
StripOptions {
strip_debug: true,
strip_producers: true,
strip_names: true,
strip_custom: true,
preserve_sections: Vec::new(),
strip_unused: true,
}
}
}
/// Compiler configuration.
#[derive(Clone, Debug)]
pub struct CompilerConfig {
pub api_key: String,
pub endpoint: String,
pub timeout_ms: u64,
pub retries: u32,
pub debug: bool,
pub optimization_level: OptimizationLevel,
pub strip_options: Option<StripOptions>,
pub max_contract_size: usize,
pub use_wasm_opt: bool,
pub validate: bool,
pub extract_metadata: bool,
pub generate_abi: bool,
}
impl CompilerConfig {
pub fn new(api_key: impl Into<String>) -> Self {
CompilerConfig {
api_key: api_key.into(),
endpoint: "https://compiler.synor.io/v1".to_string(),
timeout_ms: 60000,
retries: 3,
debug: false,
optimization_level: OptimizationLevel::Size,
strip_options: None,
max_contract_size: 256 * 1024,
use_wasm_opt: true,
validate: true,
extract_metadata: true,
generate_abi: true,
}
}
pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
self.endpoint = endpoint.into();
self
}
pub fn with_optimization_level(mut self, level: OptimizationLevel) -> Self {
self.optimization_level = level;
self
}
}
/// Compilation request.
#[derive(Clone, Debug, Serialize)]
pub struct CompilationRequest {
pub wasm: String, // Base64 encoded
#[serde(skip_serializing_if = "Option::is_none")]
pub optimization_level: Option<OptimizationLevel>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strip_options: Option<StripOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub use_wasm_opt: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub validate: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extract_metadata: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub generate_abi: Option<bool>,
}
/// Validation error.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValidationError {
pub code: String,
pub message: String,
pub location: Option<String>,
}
/// Validation result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValidationResult {
pub valid: bool,
#[serde(default)]
pub errors: Vec<ValidationError>,
#[serde(default)]
pub warnings: Vec<String>,
#[serde(default)]
pub export_count: usize,
#[serde(default)]
pub import_count: usize,
#[serde(default)]
pub function_count: usize,
#[serde(default)]
pub memory_pages: (u32, Option<u32>),
}
/// Contract metadata.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ContractMetadata {
pub name: Option<String>,
pub version: Option<String>,
#[serde(default)]
pub authors: Vec<String>,
pub description: Option<String>,
pub license: Option<String>,
pub repository: Option<String>,
pub build_timestamp: Option<u64>,
pub rust_version: Option<String>,
pub sdk_version: Option<String>,
#[serde(default)]
pub custom: HashMap<String, String>,
}
/// Type information.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum TypeInfo {
#[serde(rename = "u8")]
U8,
#[serde(rename = "u16")]
U16,
#[serde(rename = "u32")]
U32,
#[serde(rename = "u64")]
U64,
#[serde(rename = "u128")]
U128,
#[serde(rename = "i8")]
I8,
#[serde(rename = "i16")]
I16,
#[serde(rename = "i32")]
I32,
#[serde(rename = "i64")]
I64,
#[serde(rename = "i128")]
I128,
#[serde(rename = "bool")]
Bool,
#[serde(rename = "string")]
String,
#[serde(rename = "bytes")]
Bytes,
#[serde(rename = "fixedBytes")]
FixedBytes { size: usize },
#[serde(rename = "address")]
Address,
#[serde(rename = "hash256")]
Hash256,
#[serde(rename = "array")]
Array { element: Box<TypeInfo> },
#[serde(rename = "fixedArray")]
FixedArray { element: Box<TypeInfo>, size: usize },
#[serde(rename = "option")]
Option { inner: Box<TypeInfo> },
#[serde(rename = "tuple")]
Tuple { elements: Vec<TypeInfo> },
#[serde(rename = "struct")]
Struct {
name: String,
fields: Vec<(String, TypeInfo)>,
},
#[serde(rename = "unknown")]
Unknown { name: String },
}
impl TypeInfo {
/// Get the type name as a string.
pub fn type_name(&self) -> String {
match self {
TypeInfo::U8 => "u8".to_string(),
TypeInfo::U16 => "u16".to_string(),
TypeInfo::U32 => "u32".to_string(),
TypeInfo::U64 => "u64".to_string(),
TypeInfo::U128 => "u128".to_string(),
TypeInfo::I8 => "i8".to_string(),
TypeInfo::I16 => "i16".to_string(),
TypeInfo::I32 => "i32".to_string(),
TypeInfo::I64 => "i64".to_string(),
TypeInfo::I128 => "i128".to_string(),
TypeInfo::Bool => "bool".to_string(),
TypeInfo::String => "String".to_string(),
TypeInfo::Bytes => "Vec<u8>".to_string(),
TypeInfo::FixedBytes { size } => format!("[u8; {}]", size),
TypeInfo::Address => "Address".to_string(),
TypeInfo::Hash256 => "Hash256".to_string(),
TypeInfo::Array { element } => format!("Vec<{}>", element.type_name()),
TypeInfo::FixedArray { element, size } => {
format!("[{}; {}]", element.type_name(), size)
}
TypeInfo::Option { inner } => format!("Option<{}>", inner.type_name()),
TypeInfo::Tuple { elements } => {
let names: Vec<_> = elements.iter().map(|e| e.type_name()).collect();
format!("({})", names.join(", "))
}
TypeInfo::Struct { name, .. } => name.clone(),
TypeInfo::Unknown { name } => name.clone(),
}
}
}
/// Parameter ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ParamAbi {
pub name: String,
#[serde(rename = "type")]
pub ty: TypeInfo,
#[serde(default)]
pub indexed: bool,
}
/// Function ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionAbi {
pub name: String,
pub selector: String,
#[serde(default)]
pub inputs: Vec<ParamAbi>,
#[serde(default)]
pub outputs: Vec<ParamAbi>,
#[serde(default)]
pub view: bool,
#[serde(default)]
pub payable: bool,
pub doc: Option<String>,
}
/// Event ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventAbi {
pub name: String,
pub topic: String,
#[serde(default)]
pub params: Vec<ParamAbi>,
pub doc: Option<String>,
}
/// Error ABI.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ErrorAbi {
pub name: String,
pub selector: String,
#[serde(default)]
pub params: Vec<ParamAbi>,
pub doc: Option<String>,
}
/// Contract ABI (Application Binary Interface).
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractAbi {
pub name: String,
pub version: String,
#[serde(default)]
pub functions: Vec<FunctionAbi>,
#[serde(default)]
pub events: Vec<EventAbi>,
#[serde(default)]
pub errors: Vec<ErrorAbi>,
}
impl ContractAbi {
/// Find a function by name.
pub fn find_function(&self, name: &str) -> Option<&FunctionAbi> {
self.functions.iter().find(|f| f.name == name)
}
/// Find a function by selector.
pub fn find_by_selector(&self, selector: &str) -> Option<&FunctionAbi> {
self.functions.iter().find(|f| f.selector == selector)
}
/// Serialize to JSON.
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
/// Deserialize from JSON.
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
}
/// Compilation result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompilationResult {
pub contract_id: String,
pub code: String, // Base64 encoded
pub code_hash: String,
pub original_size: usize,
pub optimized_size: usize,
pub size_reduction: f64,
pub metadata: Option<ContractMetadata>,
pub abi: Option<ContractAbi>,
#[serde(default)]
pub estimated_deploy_gas: u64,
pub validation: Option<ValidationResult>,
#[serde(default)]
pub warnings: Vec<String>,
}
impl CompilationResult {
/// Returns size statistics as a formatted string.
pub fn size_stats(&self) -> String {
format!(
"Original: {} bytes, Optimized: {} bytes, Reduction: {:.1}%",
self.original_size, self.optimized_size, self.size_reduction
)
}
/// Decode the code from base64.
pub fn decode_code(&self) -> Result<Vec<u8>, base64::DecodeError> {
use base64::Engine;
base64::engine::general_purpose::STANDARD.decode(&self.code)
}
}
/// Source mapping entry.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SourceMapping {
pub wasm_offset: usize,
pub line: usize,
pub column: usize,
pub function: Option<String>,
}
/// Source map for debugging.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SourceMap {
pub file: String,
#[serde(default)]
pub mappings: Vec<SourceMapping>,
}
/// Size breakdown.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SizeBreakdown {
#[serde(default)]
pub code: usize,
#[serde(default)]
pub data: usize,
#[serde(default)]
pub types: usize,
#[serde(default)]
pub functions: usize,
#[serde(default)]
pub memory: usize,
#[serde(default)]
pub table: usize,
#[serde(default)]
pub exports: usize,
#[serde(default)]
pub imports: usize,
#[serde(default)]
pub custom: usize,
#[serde(default)]
pub total: usize,
}
/// Function analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionAnalysis {
pub name: String,
pub size: usize,
pub instruction_count: usize,
pub local_count: usize,
pub exported: bool,
pub estimated_gas: u64,
}
/// Import analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ImportAnalysis {
pub module: String,
pub name: String,
pub kind: String, // function, memory, table, global
pub signature: Option<String>,
}
/// Security issue.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecurityIssue {
pub severity: String, // low, medium, high, critical
#[serde(rename = "type")]
pub issue_type: String,
pub description: String,
pub location: Option<String>,
}
/// Security analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecurityAnalysis {
pub score: u8, // 0-100
#[serde(default)]
pub issues: Vec<SecurityIssue>,
#[serde(default)]
pub recommendations: Vec<String>,
}
/// Gas analysis.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GasAnalysis {
pub deployment_gas: u64,
#[serde(default)]
pub function_gas: HashMap<String, u64>,
#[serde(default)]
pub memory_init_gas: u64,
#[serde(default)]
pub data_section_gas: u64,
}
/// Contract analysis result.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractAnalysis {
pub size_breakdown: SizeBreakdown,
#[serde(default)]
pub functions: Vec<FunctionAnalysis>,
#[serde(default)]
pub imports: Vec<ImportAnalysis>,
pub security: Option<SecurityAnalysis>,
pub gas_analysis: Option<GasAnalysis>,
}
/// Compiler error.
#[derive(Debug)]
pub struct CompilerError {
pub message: String,
pub code: Option<String>,
pub http_status: Option<u16>,
}
impl std::fmt::Display for CompilerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(code) = &self.code {
write!(f, "{} ({})", self.message, code)
} else {
write!(f, "{}", self.message)
}
}
}
impl std::error::Error for CompilerError {}
/// Constants
pub const MAX_CONTRACT_SIZE: usize = 256 * 1024; // 256 KB
pub const MAX_MEMORY_PAGES: u32 = 16;

View file

@ -0,0 +1,590 @@
/**
* 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