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

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

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

483 lines
14 KiB
Go

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
}