- Introduced DexExample.swift demonstrating decentralized exchange operations including spot trading, perpetual futures, liquidity provision, order book management, and portfolio tracking. - Added IbcExample.swift showcasing inter-blockchain communication operations such as cross-chain transfers, channel management, packet handling, and relayer operations. - Created ZkExample.swift illustrating zero-knowledge proof operations including circuit compilation, proof generation and verification, and trusted setup ceremonies.
366 lines
12 KiB
Kotlin
366 lines
12 KiB
Kotlin
package io.synor.examples
|
|
|
|
import io.synor.ibc.*
|
|
import io.synor.ibc.types.*
|
|
import kotlinx.coroutines.runBlocking
|
|
import java.time.Duration
|
|
import java.time.Instant
|
|
|
|
/**
|
|
* Synor IBC SDK Examples for Kotlin
|
|
*
|
|
* Demonstrates Inter-Blockchain Communication operations:
|
|
* - Cross-chain transfers and messages
|
|
* - Channel lifecycle management
|
|
* - Packet handling and acknowledgments
|
|
* - Relayer operations
|
|
* - Connection monitoring
|
|
*/
|
|
fun main() = runBlocking {
|
|
// Initialize client
|
|
val config = IbcConfig(
|
|
apiKey = System.getenv("SYNOR_API_KEY") ?: "your-api-key",
|
|
endpoint = "https://ibc.synor.io/v1",
|
|
timeout = 30_000,
|
|
retries = 3,
|
|
debug = false,
|
|
defaultChain = "synor-mainnet-1",
|
|
confirmations = 1
|
|
)
|
|
|
|
val ibc = SynorIbc(config)
|
|
|
|
try {
|
|
// Check service health
|
|
val healthy = ibc.healthCheck()
|
|
println("Service healthy: $healthy\n")
|
|
|
|
// Run examples
|
|
chainsExample(ibc)
|
|
channelsExample(ibc)
|
|
transferExample(ibc)
|
|
packetExample(ibc)
|
|
relayerExample(ibc)
|
|
monitoringExample(ibc)
|
|
} finally {
|
|
ibc.close()
|
|
}
|
|
}
|
|
|
|
suspend fun chainsExample(ibc: SynorIbc) {
|
|
println("=== Chain Discovery ===")
|
|
|
|
// Get all connected chains
|
|
val chains = ibc.chains.list()
|
|
println("Connected chains: ${chains.size}")
|
|
|
|
chains.take(5).forEach { chain ->
|
|
println("\n ${chain.chainId}:")
|
|
println(" Name: ${chain.name}")
|
|
println(" Type: ${chain.chainType}")
|
|
println(" Status: ${chain.status}")
|
|
println(" Block height: ${chain.latestHeight}")
|
|
println(" Light client: ${chain.lightClient}")
|
|
}
|
|
|
|
// Get specific chain info
|
|
val synorChain = ibc.chains.get("synor-mainnet-1")
|
|
println("\nSynor chain details:")
|
|
println(" Bech32 prefix: ${synorChain.bech32Prefix}")
|
|
println(" Gas price: ${synorChain.gasPrice}")
|
|
println(" Supported features: ${synorChain.features.joinToString(", ")}")
|
|
|
|
// Get chain connections
|
|
val connections = ibc.chains.getConnections("synor-mainnet-1")
|
|
println("\nChain connections: ${connections.size}")
|
|
connections.take(3).forEach { conn ->
|
|
println(" ${conn.connectionId} -> ${conn.counterpartyChainId}")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
suspend fun channelsExample(ibc: SynorIbc) {
|
|
println("=== Channel Management ===")
|
|
|
|
// List existing channels
|
|
val channels = ibc.channels.list()
|
|
println("Active channels: ${channels.size}")
|
|
|
|
channels.take(3).forEach { channel ->
|
|
println("\n Channel ${channel.channelId}:")
|
|
println(" Port: ${channel.portId}")
|
|
println(" State: ${channel.state}")
|
|
println(" Order: ${channel.ordering}")
|
|
println(" Counterparty: ${channel.counterpartyChannelId} on ${channel.counterpartyChainId}")
|
|
println(" Version: ${channel.version}")
|
|
}
|
|
|
|
// Create a new channel (4-step handshake)
|
|
println("\nInitiating channel creation...")
|
|
|
|
// Step 1: ChanOpenInit
|
|
val initResult = ibc.channels.openInit(
|
|
ChannelInitRequest(
|
|
portId = "transfer",
|
|
counterpartyChainId = "cosmos-hub-4",
|
|
counterpartyPortId = "transfer",
|
|
version = "ics20-1",
|
|
ordering = ChannelOrdering.UNORDERED
|
|
)
|
|
)
|
|
|
|
println("Channel init:")
|
|
println(" Channel ID: ${initResult.channelId}")
|
|
println(" State: ${initResult.state}")
|
|
println(" TX hash: ${initResult.txHash}")
|
|
|
|
// Step 2: Wait for ChanOpenTry (counterparty)
|
|
println("\nWaiting for counterparty ChanOpenTry...")
|
|
val tryState = ibc.channels.waitForState(
|
|
initResult.channelId,
|
|
ChannelState.TRYOPEN,
|
|
Duration.ofMinutes(5)
|
|
)
|
|
println("Channel state: $tryState")
|
|
|
|
// Step 3: ChanOpenAck
|
|
println("\nSending ChanOpenAck...")
|
|
val ackResult = ibc.channels.openAck(
|
|
ChannelAckRequest(
|
|
channelId = initResult.channelId,
|
|
counterpartyChannelId = "channel-0",
|
|
counterpartyVersion = "ics20-1"
|
|
)
|
|
)
|
|
println("Ack TX: ${ackResult.txHash}")
|
|
|
|
// Step 4: Wait for ChanOpenConfirm (counterparty)
|
|
println("\nWaiting for channel to open...")
|
|
val openState = ibc.channels.waitForState(
|
|
initResult.channelId,
|
|
ChannelState.OPEN,
|
|
Duration.ofMinutes(5)
|
|
)
|
|
println("Channel is now: $openState")
|
|
|
|
// Get channel details
|
|
val channel = ibc.channels.get(initResult.channelId)
|
|
println("\nChannel details:")
|
|
println(" Sequences - Send: ${channel.nextSequenceSend}, Recv: ${channel.nextSequenceRecv}, Ack: ${channel.nextSequenceAck}")
|
|
|
|
println()
|
|
}
|
|
|
|
suspend fun transferExample(ibc: SynorIbc) {
|
|
println("=== Cross-Chain Transfers ===")
|
|
|
|
// Get supported tokens for transfer
|
|
val tokens = ibc.transfers.getSupportedTokens("cosmos-hub-4")
|
|
println("Transferable tokens to Cosmos Hub:")
|
|
tokens.take(5).forEach { token ->
|
|
println(" ${token.symbol} (${token.denom})")
|
|
}
|
|
|
|
// Initiate a cross-chain transfer
|
|
println("\nInitiating transfer...")
|
|
val transfer = ibc.transfers.send(
|
|
TransferRequest(
|
|
sourceChannel = "channel-0",
|
|
denom = "usynor",
|
|
amount = "1000000",
|
|
receiver = "cosmos1...",
|
|
timeoutHeight = 0, // Use timestamp instead
|
|
timeoutTimestamp = Instant.now().plusSeconds(600).toEpochMilli() * 1_000_000L,
|
|
memo = "IBC transfer from Synor"
|
|
)
|
|
)
|
|
|
|
println("Transfer initiated:")
|
|
println(" TX hash: ${transfer.txHash}")
|
|
println(" Sequence: ${transfer.sequence}")
|
|
println(" Packet ID: ${transfer.packetId}")
|
|
println(" Status: ${transfer.status}")
|
|
|
|
// Track transfer progress
|
|
println("\nTracking transfer...")
|
|
val status = ibc.transfers.track(transfer.packetId)
|
|
println("Current status: ${status.state}")
|
|
println("Source TX: ${status.sourceTxHash}")
|
|
status.destTxHash?.let { println("Dest TX: $it") }
|
|
|
|
// Wait for completion
|
|
println("\nWaiting for transfer completion...")
|
|
val finalStatus = ibc.transfers.waitForCompletion(
|
|
transfer.packetId,
|
|
Duration.ofMinutes(10)
|
|
)
|
|
|
|
println("Transfer completed:")
|
|
println(" Final status: ${finalStatus.state}")
|
|
println(" Acknowledgment: ${finalStatus.acknowledgment}")
|
|
|
|
// Get transfer history
|
|
val history = ibc.transfers.getHistory(limit = 10)
|
|
println("\nRecent transfers: ${history.size}")
|
|
history.take(3).forEach { t ->
|
|
println(" ${t.amount} ${t.denom} -> ${t.destChain} (${t.status})")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
suspend fun packetExample(ibc: SynorIbc) {
|
|
println("=== Packet Operations ===")
|
|
|
|
// List pending packets
|
|
val pending = ibc.packets.listPending("channel-0")
|
|
println("Pending packets on channel-0: ${pending.size}")
|
|
|
|
pending.take(3).forEach { packet ->
|
|
println("\n Packet #${packet.sequence}:")
|
|
println(" Source: ${packet.sourcePort}/${packet.sourceChannel}")
|
|
println(" Dest: ${packet.destPort}/${packet.destChannel}")
|
|
println(" State: ${packet.state}")
|
|
println(" Timeout: ${packet.timeoutTimestamp}")
|
|
}
|
|
|
|
// Get specific packet
|
|
val packet = ibc.packets.get("channel-0", 1)
|
|
println("\nPacket details:")
|
|
println(" Data (hex): ${packet.dataHex.take(40)}...")
|
|
println(" Created: ${packet.createdAt}")
|
|
|
|
// Get packet commitment proof
|
|
val proof = ibc.packets.getCommitmentProof("channel-0", 1)
|
|
println("\nCommitment proof:")
|
|
println(" Height: ${proof.proofHeight}")
|
|
println(" Proof size: ${proof.proof.size} bytes")
|
|
|
|
// Get acknowledgment
|
|
val ack = ibc.packets.getAcknowledgment("channel-0", 1)
|
|
println("\nAcknowledgment:")
|
|
println(" Result: ${if (ack.success) "Success" else "Error: ${ack.error}"}")
|
|
println(" TX hash: ${ack.txHash}")
|
|
|
|
// List timed-out packets
|
|
val timedOut = ibc.packets.listTimedOut()
|
|
println("\nTimed-out packets: ${timedOut.size}")
|
|
|
|
// Timeout a packet manually
|
|
if (timedOut.isNotEmpty()) {
|
|
val toTimeout = timedOut.first()
|
|
println("\nProcessing timeout for packet #${toTimeout.sequence}")
|
|
val timeout = ibc.packets.timeout(toTimeout.sourceChannel, toTimeout.sequence)
|
|
println("Timeout TX: ${timeout.txHash}")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
suspend fun relayerExample(ibc: SynorIbc) {
|
|
println("=== Relayer Operations ===")
|
|
|
|
// Get relayer status
|
|
val status = ibc.relayer.getStatus()
|
|
println("Relayer status:")
|
|
println(" Running: ${status.running}")
|
|
println(" Uptime: ${status.uptime}")
|
|
println(" Packets relayed: ${status.packetsRelayed}")
|
|
println(" Errors: ${status.errorCount}")
|
|
|
|
// List active paths
|
|
val paths = ibc.relayer.listPaths()
|
|
println("\nActive relay paths: ${paths.size}")
|
|
|
|
paths.forEach { path ->
|
|
println("\n ${path.pathId}:")
|
|
println(" ${path.sourceChain} <-> ${path.destChain}")
|
|
println(" Channel: ${path.sourceChannel} <-> ${path.destChannel}")
|
|
println(" Status: ${path.status}")
|
|
println(" Pending packets: ${path.pendingPackets}")
|
|
}
|
|
|
|
// Configure a new path
|
|
println("\nConfiguring new relay path...")
|
|
val newPath = ibc.relayer.addPath(
|
|
PathConfig(
|
|
sourceChain = "synor-mainnet-1",
|
|
destChain = "osmosis-1",
|
|
sourceChannel = "channel-1",
|
|
destChannel = "channel-100",
|
|
filterDenoms = listOf("usynor", "uosmo"),
|
|
minRelayAmount = "1000",
|
|
maxRelayAmount = "1000000000"
|
|
)
|
|
)
|
|
|
|
println("Path created: ${newPath.pathId}")
|
|
|
|
// Start relaying on path
|
|
println("\nStarting relayer on path...")
|
|
ibc.relayer.startPath(newPath.pathId)
|
|
println("Relayer started")
|
|
|
|
// Manually relay pending packets
|
|
println("\nRelaying pending packets...")
|
|
val relayResult = ibc.relayer.relayPending(newPath.pathId)
|
|
println("Relayed ${relayResult.packetCount} packets")
|
|
println("TX hashes: ${relayResult.txHashes.size}")
|
|
|
|
// Get relay history
|
|
val history = ibc.relayer.getHistory(newPath.pathId, limit = 10)
|
|
println("\nRelay history:")
|
|
history.take(3).forEach { event ->
|
|
println(" ${event.timestamp}: ${event.eventType} - ${event.packetCount} packets")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
suspend fun monitoringExample(ibc: SynorIbc) {
|
|
println("=== IBC Monitoring ===")
|
|
|
|
// Get IBC metrics
|
|
val metrics = ibc.monitoring.getMetrics()
|
|
println("IBC metrics:")
|
|
println(" Total channels: ${metrics.totalChannels}")
|
|
println(" Active channels: ${metrics.activeChannels}")
|
|
println(" Total packets: ${metrics.totalPackets}")
|
|
println(" Pending packets: ${metrics.pendingPackets}")
|
|
println(" Failed packets: ${metrics.failedPackets}")
|
|
println(" Avg relay time: ${metrics.avgRelayTime}ms")
|
|
|
|
// Get chain health
|
|
val chainHealth = ibc.monitoring.getChainHealth()
|
|
println("\nChain health:")
|
|
chainHealth.take(3).forEach { health ->
|
|
println(" ${health.chainId}:")
|
|
println(" Status: ${health.status}")
|
|
println(" Block lag: ${health.blockLag}")
|
|
println(" Last update: ${health.lastUpdate}")
|
|
}
|
|
|
|
// Get channel statistics
|
|
val stats = ibc.monitoring.getChannelStats("channel-0")
|
|
println("\nChannel-0 statistics:")
|
|
println(" Packets sent: ${stats.packetsSent}")
|
|
println(" Packets received: ${stats.packetsReceived}")
|
|
println(" Success rate: ${stats.successRate}%")
|
|
println(" Avg confirmation time: ${stats.avgConfirmationTime}ms")
|
|
|
|
// Subscribe to IBC events
|
|
println("\nSubscribing to IBC events...")
|
|
ibc.monitoring.subscribe { event ->
|
|
println("Event: ${event.type} on ${event.channelId}")
|
|
}
|
|
|
|
// Get alerts
|
|
val alerts = ibc.monitoring.getAlerts()
|
|
println("\nActive alerts: ${alerts.size}")
|
|
alerts.forEach { alert ->
|
|
println(" [${alert.severity}] ${alert.message}")
|
|
}
|
|
|
|
println()
|
|
}
|