Go SDK Examples

High-performance blockchain integration with Go. Perfect for building scalable infrastructure, validators, and high-throughput applications on Omne.

Installation & Setup

Module Installation

Install the Omne Go SDK using Go modules with proper dependency management.

# Initialize Go module (if starting new project) go mod init your-project-name # Install Omne Go SDK go get github.com/omne-network/omne-go-sdk@latest # Install additional dependencies for examples go get github.com/ethereum/go-ethereum@latest go get github.com/gorilla/websocket@latest go get github.com/gin-gonic/gin@latest

Basic Client Setup

Configure the Go client with proper connection management and error handling.

package main import ( "context" "crypto/ecdsa" "fmt" "log" "math/big" "os" "time" "github.com/ethereum/go-ethereum/crypto" "github.com/omne-network/omne-go-sdk/client" "github.com/omne-network/omne-go-sdk/types" ) type OmneConfig struct { NetworkURL string ChainID *big.Int PrivateKey *ecdsa.PrivateKey GasPrice *big.Int GasLimit uint64 Timeout time.Duration } func NewOmneClient() (*client.Client, error) { // Load configuration from environment config := &OmneConfig{ NetworkURL: getEnvOrDefault("OMNE_RPC_URL", "https://mainnet.omne.network"), ChainID: big.NewInt(1), // Mainnet chain ID GasLimit: 21000, Timeout: 30 * time.Second, } // Load private key from environment privateKeyHex := os.Getenv("OMNE_PRIVATE_KEY") if privateKeyHex == "" { return nil, fmt.Errorf("OMNE_PRIVATE_KEY environment variable not set") } privateKey, err := crypto.HexToECDSA(privateKeyHex) if err != nil { return nil, fmt.Errorf("invalid private key: %v", err) } config.PrivateKey = privateKey // Create client with retry logic clientConfig := &client.Config{ NetworkURL: config.NetworkURL, ChainID: config.ChainID, MaxRetries: 3, RetryDelay: time.Second, ConnectionTimeout: config.Timeout, ReadTimeout: config.Timeout, WriteTimeout: config.Timeout, } omneClient, err := client.NewClient(clientConfig) if err != nil { return nil, fmt.Errorf("failed to create client: %v", err) } // Verify connection ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() blockNumber, err := omneClient.BlockNumber(ctx) if err != nil { return nil, fmt.Errorf("failed to connect to network: %v", err) } fmt.Printf("Connected to Omne network, latest block: %d\n", blockNumber) // Get account balance publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { return nil, fmt.Errorf("error casting public key to ECDSA") } address := crypto.PubkeyToAddress(*publicKeyECDSA) balance, err := omneClient.BalanceAt(ctx, address, nil) if err != nil { return nil, fmt.Errorf("failed to get balance: %v", err) } fmt.Printf("Account address: %s\n", address.Hex()) fmt.Printf("Account balance: %s OMNE\n", client.WeiToEther(balance)) return omneClient, nil } func getEnvOrDefault(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } func main() { client, err := NewOmneClient() if err != nil { log.Fatal(err) } defer client.Close() fmt.Println("Omne Go client initialized successfully!") }

High-Performance Transactions

Concurrent Transaction Processing

Process thousands of transactions concurrently with proper rate limiting and error handling.

package main import ( "context" "crypto/ecdsa" "fmt" "log" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/omne-network/omne-go-sdk/client" "golang.org/x/time/rate" ) type TransactionBatch struct { client *client.Client privateKey *ecdsa.PrivateKey chainID *big.Int rateLimiter *rate.Limiter maxWorkers int } type TransactionRequest struct { To common.Address Value *big.Int Data []byte GasLimit uint64 } type TransactionResult struct { Request *TransactionRequest Hash common.Hash Error error GasUsed uint64 Success bool } func NewTransactionBatch(client *client.Client, privateKey *ecdsa.PrivateKey, chainID *big.Int) *TransactionBatch { return &TransactionBatch{ client: client, privateKey: privateKey, chainID: chainID, rateLimiter: rate.NewLimiter(rate.Limit(50), 100), // 50 TPS with burst of 100 maxWorkers: 20, } } func (tb *TransactionBatch) ProcessBatch(ctx context.Context, requests []*TransactionRequest) ([]*TransactionResult, error) { if len(requests) == 0 { return nil, fmt.Errorf("no transaction requests provided") } fmt.Printf("Processing batch of %d transactions\n", len(requests)) // Create channels for work distribution requestChan := make(chan *TransactionRequest, len(requests)) resultChan := make(chan *TransactionResult, len(requests)) // Start worker goroutines var wg sync.WaitGroup for i := 0; i < tb.maxWorkers; i++ { wg.Add(1) go tb.worker(ctx, requestChan, resultChan, &wg) } // Send requests to workers go func() { defer close(requestChan) for _, req := range requests { select { case requestChan <- req: case <-ctx.Done(): return } } }() // Collect results results := make([]*TransactionResult, 0, len(requests)) for i := 0; i < len(requests); i++ { select { case result := <-resultChan: results = append(results, result) case <-ctx.Done(): return nil, ctx.Err() } } // Wait for all workers to finish wg.Wait() close(resultChan) // Process final results successful := 0 totalGasUsed := uint64(0) for _, result := range results { if result.Success { successful++ totalGasUsed += result.GasUsed } } fmt.Printf("Batch complete: %d/%d successful, total gas used: %d\n", successful, len(requests), totalGasUsed) return results, nil } func (tb *TransactionBatch) worker(ctx context.Context, requestChan <-chan *TransactionRequest, resultChan chan<- *TransactionResult, wg *sync.WaitGroup) { defer wg.Done() for { select { case req, ok := <-requestChan: if !ok { return } // Rate limiting if err := tb.rateLimiter.Wait(ctx); err != nil { resultChan <- &TransactionResult{ Request: req, Error: fmt.Errorf("rate limit error: %v", err), Success: false, } continue } result := tb.processTransaction(ctx, req) resultChan <- result case <-ctx.Done(): return } } } func (tb *TransactionBatch) processTransaction(ctx context.Context, req *TransactionRequest) *TransactionResult { result := &TransactionResult{Request: req} // Get current nonce nonce, err := tb.client.PendingNonceAt(ctx, crypto.PubkeyToAddress(tb.privateKey.PublicKey)) if err != nil { result.Error = fmt.Errorf("failed to get nonce: %v", err) return result } // Get current gas price gasPrice, err := tb.client.SuggestGasPrice(ctx) if err != nil { result.Error = fmt.Errorf("failed to get gas price: %v", err) return result } // Create transaction tx := types.NewTx(&types.LegacyTx{ Nonce: nonce, To: &req.To, Value: req.Value, Gas: req.GasLimit, GasPrice: gasPrice, Data: req.Data, }) // Sign transaction signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tb.chainID), tb.privateKey) if err != nil { result.Error = fmt.Errorf("failed to sign transaction: %v", err) return result } // Send transaction if err := tb.client.SendTransaction(ctx, signedTx); err != nil { result.Error = fmt.Errorf("failed to send transaction: %v", err) return result } result.Hash = signedTx.Hash() // Wait for confirmation (with timeout) confirmCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() receipt, err := tb.client.WaitForTransaction(confirmCtx, result.Hash) if err != nil { result.Error = fmt.Errorf("transaction confirmation failed: %v", err) return result } result.GasUsed = receipt.GasUsed result.Success = receipt.Status == types.ReceiptStatusSuccessful if !result.Success { result.Error = fmt.Errorf("transaction failed in block %d", receipt.BlockNumber.Uint64()) } return result } // Example usage for mass token distribution func ExampleMassDistribution() { // Initialize client (assuming you have this function) client, privateKey, chainID, err := initializeClient() if err != nil { log.Fatal(err) } batch := NewTransactionBatch(client, privateKey, chainID) // Create distribution list recipients := []struct { Address common.Address Amount *big.Int }{ {common.HexToAddress("0x742d35Cc6634C0532925a3b8D0A4E643C8b1c95b"), big.NewInt(1e18)}, // 1 OMNE {common.HexToAddress("0x123..."), big.NewInt(5e17)}, // 0.5 OMNE // ... more recipients } // Convert to transaction requests var requests []*TransactionRequest for _, recipient := range recipients { requests = append(requests, &TransactionRequest{ To: recipient.Address, Value: recipient.Amount, Data: nil, GasLimit: 21000, }) } // Process batch ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() results, err := batch.ProcessBatch(ctx, requests) if err != nil { log.Fatal(err) } // Analyze results for _, result := range results { if result.Success { fmt.Printf("✓ Sent %s OMNE to %s (tx: %s)\n", client.WeiToEther(result.Request.Value), result.Request.To.Hex(), result.Hash.Hex()) } else { fmt.Printf("✗ Failed to send to %s: %v\n", result.Request.To.Hex(), result.Error) } } }

Optimized Gas Management

Advanced gas optimization strategies for high-volume applications.

package main import ( "context" "fmt" "math/big" "sort" "sync" "time" "github.com/omne-network/omne-go-sdk/client" ) type GasManager struct { client *client.Client priceHistory []GasPricePoint mu sync.RWMutex updateInterval time.Duration maxHistory int } type GasPricePoint struct { Price *big.Int Timestamp time.Time BlockNum uint64 } type GasStrategy struct { Type string // "fast", "standard", "economy" Multiplier float64 MaxPrice *big.Int Timeout time.Duration } func NewGasManager(client *client.Client) *GasManager { gm := &GasManager{ client: client, priceHistory: make([]GasPricePoint, 0), updateInterval: 15 * time.Second, maxHistory: 100, } // Start background price monitoring go gm.startPriceMonitoring() return gm } func (gm *GasManager) startPriceMonitoring() { ticker := time.NewTicker(gm.updateInterval) defer ticker.Stop() for { select { case <-ticker.C: gm.updateGasPrice() } } } func (gm *GasManager) updateGasPrice() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() price, err := gm.client.SuggestGasPrice(ctx) if err != nil { fmt.Printf("Failed to update gas price: %v\n", err) return } blockNum, err := gm.client.BlockNumber(ctx) if err != nil { fmt.Printf("Failed to get block number: %v\n", err) return } gm.mu.Lock() defer gm.mu.Unlock() point := GasPricePoint{ Price: price, Timestamp: time.Now(), BlockNum: blockNum, } gm.priceHistory = append(gm.priceHistory, point) // Keep only recent history if len(gm.priceHistory) > gm.maxHistory { gm.priceHistory = gm.priceHistory[1:] } } func (gm *GasManager) GetOptimalGasPrice(strategy GasStrategy) (*big.Int, error) { gm.mu.RLock() defer gm.mu.RUnlock() if len(gm.priceHistory) == 0 { // Fallback to network suggestion ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() return gm.client.SuggestGasPrice(ctx) } // Get recent prices recentPrices := make([]*big.Int, 0) cutoff := time.Now().Add(-5 * time.Minute) for _, point := range gm.priceHistory { if point.Timestamp.After(cutoff) { recentPrices = append(recentPrices, point.Price) } } if len(recentPrices) == 0 { return gm.priceHistory[len(gm.priceHistory)-1].Price, nil } // Sort prices for percentile calculation sort.Slice(recentPrices, func(i, j int) bool { return recentPrices[i].Cmp(recentPrices[j]) < 0 }) var basePrice *big.Int switch strategy.Type { case "fast": // 90th percentile for fast confirmation idx := int(float64(len(recentPrices)) * 0.9) if idx >= len(recentPrices) { idx = len(recentPrices) - 1 } basePrice = recentPrices[idx] case "standard": // 50th percentile (median) for standard confirmation idx := len(recentPrices) / 2 basePrice = recentPrices[idx] case "economy": // 25th percentile for economy confirmation idx := int(float64(len(recentPrices)) * 0.25) basePrice = recentPrices[idx] default: // Default to standard idx := len(recentPrices) / 2 basePrice = recentPrices[idx] } // Apply multiplier multiplierBig := big.NewFloat(strategy.Multiplier) basePriceFloat := new(big.Float).SetInt(basePrice) finalPriceFloat := new(big.Float).Mul(basePriceFloat, multiplierBig) finalPrice, _ := finalPriceFloat.Int(nil) // Check max price limit if strategy.MaxPrice != nil && finalPrice.Cmp(strategy.MaxPrice) > 0 { finalPrice = strategy.MaxPrice } return finalPrice, nil } func (gm *GasManager) EstimateTransactionCost(ctx context.Context, gasLimit uint64, strategy GasStrategy) (*big.Int, error) { gasPrice, err := gm.GetOptimalGasPrice(strategy) if err != nil { return nil, err } gasCost := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) return gasCost, nil } func (gm *GasManager) WaitForOptimalGasPrice(ctx context.Context, targetPrice *big.Int, timeout time.Duration) (*big.Int, error) { deadline := time.Now().Add(timeout) ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return nil, ctx.Err() case <-ticker.C: if time.Now().After(deadline) { return nil, fmt.Errorf("timeout waiting for optimal gas price") } currentPrice, err := gm.client.SuggestGasPrice(ctx) if err != nil { continue } if currentPrice.Cmp(targetPrice) <= 0 { return currentPrice, nil } fmt.Printf("Waiting for gas price to drop below %s gwei (current: %s gwei)\n", client.WeiToGwei(targetPrice), client.WeiToGwei(currentPrice)) } } } // Advanced gas optimization example func ExampleGasOptimization() { client, err := NewOmneClient() if err != nil { log.Fatal(err) } gasManager := NewGasManager(client) // Define different strategies strategies := map[string]GasStrategy{ "urgent": { Type: "fast", Multiplier: 1.5, MaxPrice: client.GweiToWei(big.NewInt(200)), // Max 200 gwei Timeout: 1 * time.Minute, }, "normal": { Type: "standard", Multiplier: 1.1, MaxPrice: client.GweiToWei(big.NewInt(100)), // Max 100 gwei Timeout: 5 * time.Minute, }, "patient": { Type: "economy", Multiplier: 1.0, MaxPrice: client.GweiToWei(big.NewInt(50)), // Max 50 gwei Timeout: 30 * time.Minute, }, } // Example: Choose strategy based on transaction priority transactionPriority := "normal" // Could be dynamic based on user input strategy := strategies[transactionPriority] ctx := context.Background() gasPrice, err := gasManager.GetOptimalGasPrice(strategy) if err != nil { log.Fatal(err) } estimatedCost, err := gasManager.EstimateTransactionCost(ctx, 21000, strategy) if err != nil { log.Fatal(err) } fmt.Printf("Selected strategy: %s\n", transactionPriority) fmt.Printf("Optimal gas price: %s gwei\n", client.WeiToGwei(gasPrice)) fmt.Printf("Estimated transaction cost: %s OMNE\n", client.WeiToEther(estimatedCost)) // Wait for even better price if not urgent if transactionPriority == "patient" { targetPrice := client.GweiToWei(big.NewInt(30)) // Wait for 30 gwei betterPrice, err := gasManager.WaitForOptimalGasPrice(ctx, targetPrice, strategy.Timeout) if err == nil { fmt.Printf("Got better price: %s gwei\n", client.WeiToGwei(betterPrice)) gasPrice = betterPrice } } // Use the optimized gas price for your transaction... }

Smart Contract Integration

Contract Factory & Deployment

Automated contract compilation, deployment, and verification with Go.

package main import ( "context" "crypto/ecdsa" "encoding/hex" "fmt" "math/big" "strings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/omne-network/omne-go-sdk/client" "github.com/omne-network/omne-go-sdk/compiler" ) type ContractFactory struct { client *client.Client privateKey *ecdsa.PrivateKey chainID *big.Int compiler *compiler.SolidityCompiler } type DeploymentResult struct { Address common.Address TransactionHash common.Hash Contract *Contract GasUsed uint64 DeploymentCost *big.Int } type Contract struct { Address common.Address ABI abi.ABI Bytecode string BoundContract *bind.BoundContract } func NewContractFactory(client *client.Client, privateKey *ecdsa.PrivateKey, chainID *big.Int) *ContractFactory { return &ContractFactory{ client: client, privateKey: privateKey, chainID: chainID, compiler: compiler.NewSolidityCompiler(), } } func (cf *ContractFactory) CompileAndDeploy(ctx context.Context, soliditySource string, contractName string, constructorArgs ...interface{}) (*DeploymentResult, error) { fmt.Printf("Compiling contract: %s\n", contractName) // Compile Solidity source compiled, err := cf.compiler.Compile(soliditySource) if err != nil { return nil, fmt.Errorf("compilation failed: %v", err) } contractData, exists := compiled.Contracts[contractName] if !exists { return nil, fmt.Errorf("contract %s not found in compilation output", contractName) } // Parse ABI parsedABI, err := abi.JSON(strings.NewReader(contractData.ABI)) if err != nil { return nil, fmt.Errorf("failed to parse ABI: %v", err) } // Prepare constructor arguments var constructorData []byte if len(constructorArgs) > 0 { constructorData, err = parsedABI.Pack("", constructorArgs...) if err != nil { return nil, fmt.Errorf("failed to pack constructor arguments: %v", err) } } // Combine bytecode with constructor arguments bytecode := contractData.Bytecode if len(constructorData) > 0 { bytecode = append(bytecode, constructorData...) } fmt.Printf("Deploying contract (bytecode size: %d bytes)\n", len(bytecode)) // Create deployment transaction auth, err := bind.NewKeyedTransactorWithChainID(cf.privateKey, cf.chainID) if err != nil { return nil, fmt.Errorf("failed to create transactor: %v", err) } // Estimate gas for deployment gasLimit, err := cf.client.EstimateGas(ctx, &client.CallMsg{ From: auth.From, Data: bytecode, }) if err != nil { return nil, fmt.Errorf("failed to estimate gas: %v", err) } auth.GasLimit = gasLimit + (gasLimit / 10) // Add 10% buffer // Get current gas price gasPrice, err := cf.client.SuggestGasPrice(ctx) if err != nil { return nil, fmt.Errorf("failed to get gas price: %v", err) } auth.GasPrice = gasPrice // Deploy contract address, tx, contract, err := bind.DeployContract(auth, parsedABI, bytecode, cf.client) if err != nil { return nil, fmt.Errorf("deployment failed: %v", err) } fmt.Printf("Contract deployed at: %s\n", address.Hex()) fmt.Printf("Deployment transaction: %s\n", tx.Hash().Hex()) // Wait for deployment confirmation receipt, err := cf.client.WaitForTransaction(ctx, tx.Hash()) if err != nil { return nil, fmt.Errorf("deployment confirmation failed: %v", err) } if receipt.Status != types.ReceiptStatusSuccessful { return nil, fmt.Errorf("deployment transaction failed") } deploymentCost := new(big.Int).Mul(gasPrice, big.NewInt(int64(receipt.GasUsed))) fmt.Printf("Deployment confirmed in block %d\n", receipt.BlockNumber.Uint64()) fmt.Printf("Gas used: %d\n", receipt.GasUsed) fmt.Printf("Deployment cost: %s OMNE\n", client.WeiToEther(deploymentCost)) return &DeploymentResult{ Address: address, TransactionHash: tx.Hash(), Contract: &Contract{ Address: address, ABI: parsedABI, Bytecode: hex.EncodeToString(bytecode), BoundContract: contract, }, GasUsed: receipt.GasUsed, DeploymentCost: deploymentCost, }, nil } // Example: Deploy and interact with an ERC-20 token func ExampleERC20Deployment() { client, privateKey, chainID, err := initializeClient() if err != nil { log.Fatal(err) } factory := NewContractFactory(client, privateKey, chainID) // ERC-20 contract source erc20Source := ` // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract OmneToken { string public name; string public symbol; uint8 public decimals = 18; uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); constructor(string memory _name, string memory _symbol, uint256 _totalSupply) { name = _name; symbol = _symbol; totalSupply = _totalSupply * 10**decimals; balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); } function transfer(address to, uint256 value) public returns (bool) { require(balanceOf[msg.sender] >= value, "Insufficient balance"); balanceOf[msg.sender] -= value; balanceOf[to] += value; emit Transfer(msg.sender, to, value); return true; } function approve(address spender, uint256 value) public returns (bool) { allowance[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } function transferFrom(address from, address to, uint256 value) public returns (bool) { require(balanceOf[from] >= value, "Insufficient balance"); require(allowance[from][msg.sender] >= value, "Insufficient allowance"); balanceOf[from] -= value; balanceOf[to] += value; allowance[from][msg.sender] -= value; emit Transfer(from, to, value); return true; } } ` ctx := context.Background() // Deploy token contract result, err := factory.CompileAndDeploy(ctx, erc20Source, "OmneToken", "Omne Governance Token", // name "OGT", // symbol big.NewInt(1000000), // total supply (1M tokens) ) if err != nil { log.Fatal(err) } fmt.Printf("\n=== Token Deployment Successful ===\n") fmt.Printf("Contract Address: %s\n", result.Address.Hex()) fmt.Printf("Total Cost: %s OMNE\n", client.WeiToEther(result.DeploymentCost)) // Interact with deployed contract token := result.Contract // Read token information name, err := token.BoundContract.Call(&bind.CallOpts{}, "name") if err != nil { log.Fatal(err) } symbol, err := token.BoundContract.Call(&bind.CallOpts{}, "symbol") if err != nil { log.Fatal(err) } totalSupply, err := token.BoundContract.Call(&bind.CallOpts{}, "totalSupply") if err != nil { log.Fatal(err) } fmt.Printf("\n=== Token Information ===\n") fmt.Printf("Name: %s\n", name[0].(string)) fmt.Printf("Symbol: %s\n", symbol[0].(string)) fmt.Printf("Total Supply: %s\n", totalSupply[0].(*big.Int)) // Transfer tokens auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, chainID) transferAmount := big.NewInt(1000 * 1e18) // 1000 tokens recipient := common.HexToAddress("0x742d35Cc6634C0532925a3b8D0A4E643C8b1c95b") tx, err := token.BoundContract.Transact(auth, "transfer", recipient, transferAmount) if err != nil { log.Fatal(err) } receipt, err := client.WaitForTransaction(ctx, tx.Hash()) if err != nil { log.Fatal(err) } if receipt.Status == types.ReceiptStatusSuccessful { fmt.Printf("\nTransfer successful: %s\n", tx.Hash().Hex()) } else { fmt.Printf("\nTransfer failed: %s\n", tx.Hash().Hex()) } }

Event Subscription & Real-time Monitoring

Subscribe to blockchain events with WebSocket connections and real-time processing.

package main import ( "context" "fmt" "log" "math/big" "sync" "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/gorilla/websocket" "github.com/omne-network/omne-go-sdk/client" ) type EventMonitor struct { client *client.Client wsClient *client.WebSocketClient subscriptions map[string]*EventSubscription eventHandlers map[string][]EventHandler mu sync.RWMutex ctx context.Context cancel context.CancelFunc } type EventSubscription struct { ID string Contract common.Address EventName string Filter EventFilter Channel chan *Event Active bool } type EventFilter struct { FromBlock *big.Int ToBlock *big.Int Topics [][]common.Hash } type Event struct { Address common.Address Topics []common.Hash Data []byte BlockNumber uint64 TransactionHash common.Hash LogIndex uint Removed bool Timestamp time.Time } type EventHandler func(*Event) error func NewEventMonitor(client *client.Client, wsURL string) (*EventMonitor, error) { wsClient, err := client.NewWebSocketClient(wsURL) if err != nil { return nil, fmt.Errorf("failed to create WebSocket client: %v", err) } ctx, cancel := context.WithCancel(context.Background()) em := &EventMonitor{ client: client, wsClient: wsClient, subscriptions: make(map[string]*EventSubscription), eventHandlers: make(map[string][]EventHandler), ctx: ctx, cancel: cancel, } go em.processEvents() return em, nil } func (em *EventMonitor) SubscribeToContract(contractAddress common.Address, contractABI abi.ABI, eventNames []string) error { em.mu.Lock() defer em.mu.Unlock() for _, eventName := range eventNames { // Get event signature from ABI event, exists := contractABI.Events[eventName] if !exists { return fmt.Errorf("event %s not found in contract ABI", eventName) } // Create subscription subscriptionID := fmt.Sprintf("%s_%s", contractAddress.Hex(), eventName) subscription := &EventSubscription{ ID: subscriptionID, Contract: contractAddress, EventName: eventName, Filter: EventFilter{ FromBlock: nil, // Latest Topics: [][]common.Hash{{event.ID}}, }, Channel: make(chan *Event, 100), Active: true, } em.subscriptions[subscriptionID] = subscription // Subscribe via WebSocket err := em.wsClient.SubscribeToLogs(em.ctx, subscription.Filter, subscription.Channel) if err != nil { return fmt.Errorf("failed to subscribe to %s events: %v", eventName, err) } fmt.Printf("Subscribed to %s events from %s\n", eventName, contractAddress.Hex()) } return nil } func (em *EventMonitor) RegisterHandler(contractAddress common.Address, eventName string, handler EventHandler) { em.mu.Lock() defer em.mu.Unlock() key := fmt.Sprintf("%s_%s", contractAddress.Hex(), eventName) if em.eventHandlers[key] == nil { em.eventHandlers[key] = make([]EventHandler, 0) } em.eventHandlers[key] = append(em.eventHandlers[key], handler) } func (em *EventMonitor) processEvents() { for { select { case <-em.ctx.Done(): return default: em.mu.RLock() for _, subscription := range em.subscriptions { if !subscription.Active { continue } select { case event := <-subscription.Channel: em.handleEvent(subscription, event) default: // Non-blocking } } em.mu.RUnlock() time.Sleep(100 * time.Millisecond) } } } func (em *EventMonitor) handleEvent(subscription *EventSubscription, event *Event) { handlerKey := fmt.Sprintf("%s_%s", subscription.Contract.Hex(), subscription.EventName) em.mu.RLock() handlers := em.eventHandlers[handlerKey] em.mu.RUnlock() for _, handler := range handlers { go func(h EventHandler) { if err := h(event); err != nil { log.Printf("Event handler error: %v", err) } }(handler) } } func (em *EventMonitor) GetHistoricalEvents(contractAddress common.Address, eventName string, fromBlock, toBlock *big.Int) ([]*Event, error) { // Implementation for querying historical events logs, err := em.client.FilterLogs(em.ctx, &client.FilterQuery{ FromBlock: fromBlock, ToBlock: toBlock, Addresses: []common.Address{contractAddress}, }) if err != nil { return nil, err } events := make([]*Event, len(logs)) for i, log := range logs { events[i] = &Event{ Address: log.Address, Topics: log.Topics, Data: log.Data, BlockNumber: log.BlockNumber, TransactionHash: log.TxHash, LogIndex: log.Index, Removed: log.Removed, } } return events, nil } func (em *EventMonitor) Stop() { em.cancel() em.wsClient.Close() } // Example usage with ERC-20 token monitoring func ExampleTokenMonitoring() { client, err := NewOmneClient() if err != nil { log.Fatal(err) } monitor, err := NewEventMonitor(client, "wss://mainnet.omne.network/ws") if err != nil { log.Fatal(err) } defer monitor.Stop() // ERC-20 ABI (simplified) erc20ABI := `[ { "anonymous": false, "inputs": [ {"indexed": true, "name": "from", "type": "address"}, {"indexed": true, "name": "to", "type": "address"}, {"indexed": false, "name": "value", "type": "uint256"} ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "owner", "type": "address"}, {"indexed": true, "name": "spender", "type": "address"}, {"indexed": false, "name": "value", "type": "uint256"} ], "name": "Approval", "type": "event" } ]` parsedABI, err := abi.JSON(strings.NewReader(erc20ABI)) if err != nil { log.Fatal(err) } // Token contract address to monitor tokenAddress := common.HexToAddress("0x...") // Subscribe to token events err = monitor.SubscribeToContract(tokenAddress, parsedABI, []string{"Transfer", "Approval"}) if err != nil { log.Fatal(err) } // Register transfer event handler monitor.RegisterHandler(tokenAddress, "Transfer", func(event *Event) error { // Parse transfer event from := common.BytesToAddress(event.Topics[1].Bytes()) to := common.BytesToAddress(event.Topics[2].Bytes()) value := new(big.Int).SetBytes(event.Data) fmt.Printf("🔄 Transfer detected:\n") fmt.Printf(" From: %s\n", from.Hex()) fmt.Printf(" To: %s\n", to.Hex()) fmt.Printf(" Amount: %s tokens\n", client.WeiToEther(value)) fmt.Printf(" Block: %d\n", event.BlockNumber) fmt.Printf(" Tx: %s\n", event.TransactionHash.Hex()) // Alert for large transfers largeAmount := big.NewInt(0).Mul(big.NewInt(1000), big.NewInt(1e18)) // 1000 tokens if value.Cmp(largeAmount) > 0 { fmt.Printf("🚨 LARGE TRANSFER ALERT: %s tokens\n", client.WeiToEther(value)) // Send notification, store in database, etc. } return nil }) // Register approval event handler monitor.RegisterHandler(tokenAddress, "Approval", func(event *Event) error { owner := common.BytesToAddress(event.Topics[1].Bytes()) spender := common.BytesToAddress(event.Topics[2].Bytes()) value := new(big.Int).SetBytes(event.Data) fmt.Printf("✅ Approval detected:\n") fmt.Printf(" Owner: %s\n", owner.Hex()) fmt.Printf(" Spender: %s\n", spender.Hex()) fmt.Printf(" Amount: %s tokens\n", client.WeiToEther(value)) return nil }) // Keep monitoring fmt.Println("Starting event monitoring... Press Ctrl+C to stop") select {} }

Go SDK Best Practices

🚀 Performance Optimization

  • • Use goroutines for concurrent transaction processing
  • • Implement connection pooling for high-throughput applications
  • • Batch multiple operations to reduce network roundtrips
  • • Use WebSocket connections for real-time event monitoring
  • • Cache frequently accessed contract ABIs and addresses

⚡ Concurrency Patterns

  • • Use worker pools for transaction processing with rate limiting
  • • Implement proper mutex locks for shared state access
  • • Use channels for safe communication between goroutines
  • • Apply circuit breaker pattern for external service calls
  • • Implement graceful shutdown with context cancellation

🔐 Security & Reliability

  • • Validate all input parameters and contract addresses
  • • Implement comprehensive error handling with proper logging
  • • Use context with timeouts for all network operations
  • • Implement retry logic with exponential backoff
  • • Store private keys securely using environment variables

📊 Monitoring & Observability

  • • Implement structured logging with appropriate log levels
  • • Track transaction success rates and gas usage metrics
  • • Monitor connection health and automatically reconnect
  • • Use distributed tracing for complex transaction flows
  • • Set up alerting for failed transactions and network issues

Related Examples