// Package main demonstrates the Synor IBC SDK for Go // // This example covers Inter-Blockchain Communication operations: // - Cross-chain token transfers // - Channel management // - Packet handling // - Relayer operations package main import ( "context" "fmt" "log" "os" "time" "github.com/synor/sdk-go/ibc" ) func main() { // Initialize client config := ibc.Config{ APIKey: getEnv("SYNOR_API_KEY", "your-api-key"), Endpoint: "https://ibc.synor.io/v1", Timeout: 30 * time.Second, Retries: 3, Debug: false, DefaultNetwork: ibc.NetworkMainnet, } client, err := ibc.NewClient(config) if err != nil { log.Fatalf("Failed to create client: %v", err) } defer client.Close() ctx := context.Background() // Check service health healthy, err := client.HealthCheck(ctx) if err != nil { log.Printf("Health check failed: %v", err) } else { fmt.Printf("Service healthy: %v\n\n", healthy) } // Run examples chainsExample(ctx, client) channelsExample(ctx, client) transferExample(ctx, client) packetExample(ctx, client) relayerExample(ctx, client) connectionExample(ctx, client) } func chainsExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Connected Chains ===") // Get all connected chains chains, err := client.Chains.List(ctx) if err != nil { log.Printf("Failed to list chains: %v", err) return } fmt.Printf("Connected chains: %d\n", len(chains)) for _, chain := range chains { fmt.Printf(" %s:\n", chain.ChainID) fmt.Printf(" Name: %s\n", chain.Name) fmt.Printf(" Status: %s\n", chain.Status) fmt.Printf(" Block height: %d\n", chain.LatestHeight) fmt.Printf(" Channels: %d\n", chain.ChannelCount) } // Get specific chain info cosmos, err := client.Chains.Get(ctx, "cosmoshub-4") if err != nil { log.Printf("Failed to get Cosmos Hub: %v", err) return } fmt.Println("\nCosmos Hub details:") fmt.Printf(" RPC: %s\n", cosmos.RPCEndpoint) fmt.Printf(" Rest: %s\n", cosmos.RestEndpoint) fmt.Printf(" Native denom: %s\n", cosmos.NativeDenom) fmt.Printf(" Prefix: %s\n", cosmos.Bech32Prefix) // Get supported assets on a chain assets, err := client.Chains.GetAssets(ctx, "cosmoshub-4") if err != nil { log.Printf("Failed to get assets: %v", err) return } fmt.Println("\nSupported assets on Cosmos Hub:") for _, asset := range assets[:min(5, len(assets))] { fmt.Printf(" %s: %s\n", asset.Symbol, asset.Denom) fmt.Printf(" Origin: %s\n", asset.OriginChain) fmt.Printf(" Decimals: %d\n", asset.Decimals) } // Get chain paths (routes) paths, err := client.Chains.GetPaths(ctx, "synor-1", "cosmoshub-4") if err != nil { log.Printf("Failed to get paths: %v", err) return } fmt.Println("\nPaths from Synor to Cosmos Hub:") for _, path := range paths { fmt.Printf(" %s -> %s\n", path.SourceChannel, path.DestChannel) fmt.Printf(" Hops: %d\n", path.Hops) fmt.Printf(" Avg time: %ds\n", path.AvgTransferTime) } fmt.Println() } func channelsExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Channel Management ===") // List all channels channels, err := client.Channels.List(ctx) if err != nil { log.Printf("Failed to list channels: %v", err) return } fmt.Printf("Total channels: %d\n", len(channels)) // Filter by state var openChannels []ibc.Channel for _, c := range channels { if c.State == ibc.ChannelStateOpen { openChannels = append(openChannels, c) } } fmt.Printf("Open channels: %d\n", len(openChannels)) for _, channel := range openChannels[:min(3, len(openChannels))] { fmt.Printf("\n Channel %s:\n", channel.ChannelID) fmt.Printf(" Port: %s\n", channel.PortID) fmt.Printf(" Counterparty: %s on %s\n", channel.CounterpartyChannelID, channel.CounterpartyChainID) fmt.Printf(" Ordering: %s\n", channel.Ordering) fmt.Printf(" Version: %s\n", channel.Version) fmt.Printf(" State: %s\n", channel.State) } // Get specific channel channel, err := client.Channels.Get(ctx, "channel-0") if err != nil { log.Printf("Failed to get channel: %v", err) return } fmt.Println("\nChannel-0 details:") fmt.Printf(" Connection: %s\n", channel.ConnectionID) fmt.Printf(" Counterparty port: %s\n", channel.CounterpartyPortID) // Get channel statistics stats, err := client.Channels.GetStats(ctx, "channel-0") if err != nil { log.Printf("Failed to get channel stats: %v", err) return } fmt.Println("\nChannel-0 statistics:") fmt.Printf(" Total packets sent: %d\n", stats.PacketsSent) fmt.Printf(" Total packets received: %d\n", stats.PacketsReceived) fmt.Printf(" Pending packets: %d\n", stats.PendingPackets) fmt.Printf(" Success rate: %.1f%%\n", stats.SuccessRate) fmt.Printf(" Avg relay time: %ds\n", stats.AvgRelayTime) // Get channel capacity capacity, err := client.Channels.GetCapacity(ctx, "channel-0") if err != nil { log.Printf("Failed to get channel capacity: %v", err) return } fmt.Println("\nChannel-0 capacity:") fmt.Printf(" Max throughput: %d packets/block\n", capacity.MaxPacketsPerBlock) fmt.Printf(" Current utilization: %.1f%%\n", capacity.Utilization) fmt.Println() } func transferExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Cross-Chain Transfers ===") // Estimate transfer fee estimate, err := client.Transfers.EstimateFee(ctx, &ibc.FeeEstimateRequest{ SourceChain: "synor-1", DestChain: "cosmoshub-4", Denom: "usyn", Amount: "1000000", // 1 SYN (6 decimals) }) if err != nil { log.Printf("Failed to estimate fee: %v", err) return } fmt.Println("Transfer fee estimate:") fmt.Printf(" Gas: %d\n", estimate.Gas) fmt.Printf(" Fee: %s %s\n", estimate.Fee, estimate.FeeDenom) fmt.Printf(" Timeout: %ds\n", estimate.Timeout) // Initiate a transfer fmt.Println("\nInitiating transfer...") transfer, err := client.Transfers.Send(ctx, &ibc.TransferRequest{ SourceChain: "synor-1", DestChain: "cosmoshub-4", Channel: "channel-0", Sender: "synor1abc...", // Your address Receiver: "cosmos1xyz...", // Recipient address Denom: "usyn", Amount: "1000000", // 1 SYN Memo: "Cross-chain transfer example", TimeoutHeight: 0, // Use timestamp instead TimeoutTimestamp: time.Now().Add(10 * time.Minute).UnixMilli(), }) if err != nil { log.Printf("Failed to initiate transfer: %v", err) return } fmt.Println("Transfer initiated:") fmt.Printf(" TX Hash: %s\n", transfer.TxHash) fmt.Printf(" Sequence: %d\n", transfer.Sequence) fmt.Printf(" Status: %s\n", transfer.Status) // Track transfer status fmt.Println("\nTracking transfer...") status, err := client.Transfers.GetStatus(ctx, transfer.TxHash) if err != nil { log.Printf("Failed to get transfer status: %v", err) return } fmt.Printf("Current status: %s\n", status.State) fmt.Printf(" Source confirmed: %v\n", status.SourceConfirmed) fmt.Printf(" Relayed: %v\n", status.Relayed) fmt.Printf(" Dest confirmed: %v\n", status.DestConfirmed) // Get transfer history history, err := client.Transfers.GetHistory(ctx, "synor1abc...", 5) if err != nil { log.Printf("Failed to get transfer history: %v", err) return } fmt.Println("\nTransfer history (last 5):") for _, tx := range history { direction := "OUT" if tx.Sender != "synor1abc..." { direction = "IN" } fmt.Printf(" %s %s %s (%s)\n", direction, tx.Amount, tx.Denom, tx.Status) } // Get pending transfers pending, err := client.Transfers.GetPending(ctx, "synor1abc...") if err != nil { log.Printf("Failed to get pending transfers: %v", err) return } fmt.Printf("\nPending transfers: %d\n", len(pending)) fmt.Println() } func packetExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Packet Handling ===") // Get pending packets packets, err := client.Packets.GetPending(ctx, "channel-0") if err != nil { log.Printf("Failed to get pending packets: %v", err) return } fmt.Printf("Pending packets on channel-0: %d\n", len(packets)) for _, packet := range packets[:min(3, len(packets))] { fmt.Printf("\n Packet %d:\n", packet.Sequence) fmt.Printf(" Source: %s/%s\n", packet.SourcePort, packet.SourceChannel) fmt.Printf(" Dest: %s/%s\n", packet.DestPort, packet.DestChannel) fmt.Printf(" State: %s\n", packet.State) fmt.Printf(" Data size: %d bytes\n", len(packet.Data)) fmt.Printf(" Timeout height: %d\n", packet.TimeoutHeight) fmt.Printf(" Timeout timestamp: %d\n", packet.TimeoutTimestamp) } // Get packet by sequence packet, err := client.Packets.Get(ctx, "channel-0", 1) if err != nil { log.Printf("Failed to get packet: %v", err) return } fmt.Println("\nPacket details:") fmt.Printf(" Commitment: %s\n", packet.Commitment) fmt.Printf(" Receipt: %s\n", packet.Receipt) fmt.Printf(" Acknowledgement: %s\n", packet.Acknowledgement) // Get packet receipts receipts, err := client.Packets.GetReceipts(ctx, "channel-0", []uint64{1, 2, 3}) if err != nil { log.Printf("Failed to get receipts: %v", err) return } fmt.Println("\nPacket receipts:") for seq, receipt := range receipts { status := "not received" if receipt { status = "received" } fmt.Printf(" Sequence %d: %s\n", seq, status) } // Get timed out packets timedOut, err := client.Packets.GetTimedOut(ctx, "channel-0") if err != nil { log.Printf("Failed to get timed out packets: %v", err) return } fmt.Printf("\nTimed out packets: %d\n", len(timedOut)) for _, p := range timedOut { fmt.Printf(" Sequence %d: timeout at %d\n", p.Sequence, p.TimeoutTimestamp) } // Get unreceived packets unreceived, err := client.Packets.GetUnreceived(ctx, "channel-0") if err != nil { log.Printf("Failed to get unreceived packets: %v", err) return } unreceivedStr := "none" if len(unreceived) > 0 { unreceivedStr = fmt.Sprintf("%v", unreceived) } fmt.Printf("\nUnreceived packet sequences: %s\n", unreceivedStr) // Get unacknowledged packets unacked, err := client.Packets.GetUnacknowledged(ctx, "channel-0") if err != nil { log.Printf("Failed to get unacknowledged packets: %v", err) return } unackedStr := "none" if len(unacked) > 0 { unackedStr = fmt.Sprintf("%v", unacked) } fmt.Printf("Unacknowledged packet sequences: %s\n", unackedStr) fmt.Println() } func relayerExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Relayer Operations ===") // Get active relayers relayers, err := client.Relayers.List(ctx) if err != nil { log.Printf("Failed to list relayers: %v", err) return } fmt.Printf("Active relayers: %d\n", len(relayers)) for _, relayer := range relayers[:min(3, len(relayers))] { fmt.Printf("\n %s:\n", relayer.Address) fmt.Printf(" Chains: %v\n", relayer.Chains) fmt.Printf(" Packets relayed: %d\n", relayer.PacketsRelayed) fmt.Printf(" Success rate: %.1f%%\n", relayer.SuccessRate) fmt.Printf(" Avg latency: %dms\n", relayer.AvgLatency) fmt.Printf(" Fee rate: %.2f%%\n", relayer.FeeRate) } // Get relayer statistics stats, err := client.Relayers.GetStats(ctx) if err != nil { log.Printf("Failed to get relayer stats: %v", err) return } fmt.Println("\nGlobal relayer statistics:") fmt.Printf(" Total relayers: %d\n", stats.TotalRelayers) fmt.Printf(" Active relayers: %d\n", stats.ActiveRelayers) fmt.Printf(" Packets relayed (24h): %d\n", stats.PacketsRelayed24h) fmt.Printf(" Total fees earned: %s\n", stats.TotalFeesEarned) // Register as a relayer fmt.Println("\nRegistering as relayer...") registration, err := client.Relayers.Register(ctx, &ibc.RelayerRegistration{ Chains: []string{"synor-1", "cosmoshub-4"}, FeeRate: 0.1, // 0.1% fee MinPacketSize: 0, MaxPacketSize: 1000000, }) if err != nil { log.Printf("Failed to register as relayer: %v", err) return } fmt.Printf("Registered with address: %s\n", registration.RelayerAddress) // Start relaying (in background) fmt.Println("\nStarting relay service...") relaySession, err := client.Relayers.StartRelay(ctx, &ibc.RelayConfig{ Channels: []string{"channel-0"}, AutoAck: true, BatchSize: 10, PollInterval: 5000, }) if err != nil { log.Printf("Failed to start relay: %v", err) return } fmt.Printf("Relay session started: %s\n", relaySession.SessionID) // Get relay queue queue, err := client.Relayers.GetQueue(ctx, "channel-0") if err != nil { log.Printf("Failed to get relay queue: %v", err) return } fmt.Printf("\nRelay queue for channel-0: %d packets\n", len(queue)) // Manually relay a packet if len(queue) > 0 { fmt.Println("\nRelaying packet...") relayResult, err := client.Relayers.RelayPacket(ctx, queue[0].Sequence, "channel-0") if err != nil { log.Printf("Failed to relay packet: %v", err) } else { fmt.Printf("Relay result: %s\n", relayResult.Status) fmt.Printf(" TX Hash: %s\n", relayResult.TxHash) fmt.Printf(" Fee earned: %s\n", relayResult.FeeEarned) } } // Stop relay session err = client.Relayers.StopRelay(ctx, relaySession.SessionID) if err != nil { log.Printf("Failed to stop relay: %v", err) } else { fmt.Println("\nRelay session stopped") } fmt.Println() } func connectionExample(ctx context.Context, client *ibc.Client) { fmt.Println("=== Connection Information ===") // List connections connections, err := client.Connections.List(ctx) if err != nil { log.Printf("Failed to list connections: %v", err) return } fmt.Printf("Total connections: %d\n", len(connections)) for _, conn := range connections[:min(3, len(connections))] { fmt.Printf("\n %s:\n", conn.ConnectionID) fmt.Printf(" Client: %s\n", conn.ClientID) fmt.Printf(" Counterparty: %s\n", conn.CounterpartyConnectionID) fmt.Printf(" State: %s\n", conn.State) fmt.Printf(" Versions: %v\n", conn.Versions) } // Get connection details connection, err := client.Connections.Get(ctx, "connection-0") if err != nil { log.Printf("Failed to get connection: %v", err) return } fmt.Println("\nConnection-0 details:") fmt.Printf(" Delay period: %dns\n", connection.DelayPeriod) fmt.Printf(" Counterparty client: %s\n", connection.CounterpartyClientID) fmt.Printf(" Counterparty prefix: %s\n", connection.CounterpartyPrefix) // Get client state clientState, err := client.Clients.Get(ctx, connection.ClientID) if err != nil { log.Printf("Failed to get client state: %v", err) return } fmt.Println("\nClient state:") fmt.Printf(" Chain ID: %s\n", clientState.ChainID) fmt.Printf(" Trust level: %s\n", clientState.TrustLevel) fmt.Printf(" Trusting period: %s\n", clientState.TrustingPeriod) fmt.Printf(" Unbonding period: %s\n", clientState.UnbondingPeriod) fmt.Printf(" Latest height: %d\n", clientState.LatestHeight) fmt.Printf(" Frozen: %v\n", clientState.Frozen) // Get consensus state consensus, err := client.Clients.GetConsensusState(ctx, connection.ClientID, clientState.LatestHeight) if err != nil { log.Printf("Failed to get consensus state: %v", err) return } fmt.Printf("\nConsensus state at height %d:\n", clientState.LatestHeight) fmt.Printf(" Timestamp: %s\n", consensus.Timestamp) fmt.Printf(" Root: %s...\n", consensus.Root[:min(20, len(consensus.Root))]) fmt.Printf(" Next validators hash: %s...\n", consensus.NextValidatorsHash[:min(20, len(consensus.NextValidatorsHash))]) fmt.Println() } func min(a, b int) int { if a < b { return a } return b } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }