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