356 lines
12 KiB
Dart
356 lines
12 KiB
Dart
/// Synor IBC SDK Examples for Flutter/Dart
|
|
///
|
|
/// Demonstrates Inter-Blockchain Communication operations:
|
|
/// - Cross-chain token transfers
|
|
/// - Channel management
|
|
/// - Packet handling
|
|
/// - Relayer operations
|
|
library;
|
|
|
|
import 'dart:io';
|
|
import 'package:synor_ibc/synor_ibc.dart';
|
|
|
|
Future<void> main() async {
|
|
// Initialize client
|
|
final config = IbcConfig(
|
|
apiKey: Platform.environment['SYNOR_API_KEY'] ?? 'your-api-key',
|
|
endpoint: 'https://ibc.synor.io/v1',
|
|
timeout: const Duration(seconds: 30),
|
|
retries: 3,
|
|
debug: false,
|
|
defaultNetwork: Network.mainnet,
|
|
);
|
|
|
|
final ibc = SynorIbc(config);
|
|
|
|
try {
|
|
// Check service health
|
|
final healthy = await ibc.healthCheck();
|
|
print('Service healthy: $healthy\n');
|
|
|
|
// Run examples
|
|
await chainsExample(ibc);
|
|
await channelsExample(ibc);
|
|
await transferExample(ibc);
|
|
await packetExample(ibc);
|
|
await relayerExample(ibc);
|
|
await connectionExample(ibc);
|
|
} finally {
|
|
await ibc.close();
|
|
}
|
|
}
|
|
|
|
Future<void> chainsExample(SynorIbc ibc) async {
|
|
print('=== Connected Chains ===');
|
|
|
|
// Get all connected chains
|
|
final chains = await ibc.chains.list();
|
|
print('Connected chains: ${chains.length}');
|
|
|
|
for (final chain in chains) {
|
|
print(' ${chain.chainId}:');
|
|
print(' Name: ${chain.name}');
|
|
print(' Status: ${chain.status}');
|
|
print(' Block height: ${chain.latestHeight}');
|
|
print(' Channels: ${chain.channelCount}');
|
|
}
|
|
|
|
// Get specific chain info
|
|
final cosmos = await ibc.chains.get('cosmoshub-4');
|
|
print('\nCosmos Hub details:');
|
|
print(' RPC: ${cosmos.rpcEndpoint}');
|
|
print(' Rest: ${cosmos.restEndpoint}');
|
|
print(' Native denom: ${cosmos.nativeDenom}');
|
|
print(' Prefix: ${cosmos.bech32Prefix}');
|
|
|
|
// Get supported assets on a chain
|
|
final assets = await ibc.chains.getAssets('cosmoshub-4');
|
|
print('\nSupported assets on Cosmos Hub:');
|
|
for (final asset in assets.take(5)) {
|
|
print(' ${asset.symbol}: ${asset.denom}');
|
|
print(' Origin: ${asset.originChain}');
|
|
print(' Decimals: ${asset.decimals}');
|
|
}
|
|
|
|
// Get chain paths (routes)
|
|
final paths = await ibc.chains.getPaths('synor-1', 'cosmoshub-4');
|
|
print('\nPaths from Synor to Cosmos Hub:');
|
|
for (final path in paths) {
|
|
print(' ${path.sourceChannel} -> ${path.destChannel}');
|
|
print(' Hops: ${path.hops}');
|
|
print(' Avg time: ${path.avgTransferTime}s');
|
|
}
|
|
|
|
print('');
|
|
}
|
|
|
|
Future<void> channelsExample(SynorIbc ibc) async {
|
|
print('=== Channel Management ===');
|
|
|
|
// List all channels
|
|
final channels = await ibc.channels.list();
|
|
print('Total channels: ${channels.length}');
|
|
|
|
// Filter by state
|
|
final openChannels =
|
|
channels.where((c) => c.state == ChannelState.open).toList();
|
|
print('Open channels: ${openChannels.length}');
|
|
|
|
for (final channel in openChannels.take(3)) {
|
|
print('\n Channel ${channel.channelId}:');
|
|
print(' Port: ${channel.portId}');
|
|
print(' Counterparty: ${channel.counterpartyChannelId} on ${channel.counterpartyChainId}');
|
|
print(' Ordering: ${channel.ordering}');
|
|
print(' Version: ${channel.version}');
|
|
print(' State: ${channel.state}');
|
|
}
|
|
|
|
// Get specific channel
|
|
final channel = await ibc.channels.get('channel-0');
|
|
print('\nChannel-0 details:');
|
|
print(' Connection: ${channel.connectionId}');
|
|
print(' Counterparty port: ${channel.counterpartyPortId}');
|
|
|
|
// Get channel statistics
|
|
final stats = await ibc.channels.getStats('channel-0');
|
|
print('\nChannel-0 statistics:');
|
|
print(' Total packets sent: ${stats.packetsSent}');
|
|
print(' Total packets received: ${stats.packetsReceived}');
|
|
print(' Pending packets: ${stats.pendingPackets}');
|
|
print(' Success rate: ${stats.successRate}%');
|
|
print(' Avg relay time: ${stats.avgRelayTime}s');
|
|
|
|
// Get channel capacity
|
|
final capacity = await ibc.channels.getCapacity('channel-0');
|
|
print('\nChannel-0 capacity:');
|
|
print(' Max throughput: ${capacity.maxPacketsPerBlock} packets/block');
|
|
print(' Current utilization: ${capacity.utilization}%');
|
|
|
|
print('');
|
|
}
|
|
|
|
Future<void> transferExample(SynorIbc ibc) async {
|
|
print('=== Cross-Chain Transfers ===');
|
|
|
|
// Estimate transfer fee
|
|
final estimate = await ibc.transfers.estimateFee(FeeEstimateRequest(
|
|
sourceChain: 'synor-1',
|
|
destChain: 'cosmoshub-4',
|
|
denom: 'usyn',
|
|
amount: '1000000', // 1 SYN (6 decimals)
|
|
));
|
|
|
|
print('Transfer fee estimate:');
|
|
print(' Gas: ${estimate.gas}');
|
|
print(' Fee: ${estimate.fee} ${estimate.feeDenom}');
|
|
print(' Timeout: ${estimate.timeout}s');
|
|
|
|
// Initiate a transfer
|
|
print('\nInitiating transfer...');
|
|
final transfer = await ibc.transfers.send(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: DateTime.now().add(const Duration(minutes: 10)).millisecondsSinceEpoch,
|
|
));
|
|
|
|
print('Transfer initiated:');
|
|
print(' TX Hash: ${transfer.txHash}');
|
|
print(' Sequence: ${transfer.sequence}');
|
|
print(' Status: ${transfer.status}');
|
|
|
|
// Track transfer status
|
|
print('\nTracking transfer...');
|
|
final status = await ibc.transfers.getStatus(transfer.txHash);
|
|
print('Current status: ${status.state}');
|
|
print(' Source confirmed: ${status.sourceConfirmed}');
|
|
print(' Relayed: ${status.relayed}');
|
|
print(' Dest confirmed: ${status.destConfirmed}');
|
|
|
|
// Get transfer history
|
|
final history = await ibc.transfers.getHistory('synor1abc...', limit: 5);
|
|
print('\nTransfer history (last 5):');
|
|
for (final tx in history) {
|
|
final direction = tx.sender == 'synor1abc...' ? 'OUT' : 'IN';
|
|
print(' $direction ${tx.amount} ${tx.denom} (${tx.status})');
|
|
}
|
|
|
|
// Get pending transfers
|
|
final pending = await ibc.transfers.getPending('synor1abc...');
|
|
print('\nPending transfers: ${pending.length}');
|
|
|
|
print('');
|
|
}
|
|
|
|
Future<void> packetExample(SynorIbc ibc) async {
|
|
print('=== Packet Handling ===');
|
|
|
|
// Get pending packets
|
|
final packets = await ibc.packets.getPending('channel-0');
|
|
print('Pending packets on channel-0: ${packets.length}');
|
|
|
|
for (final packet in packets.take(3)) {
|
|
print('\n Packet ${packet.sequence}:');
|
|
print(' Source: ${packet.sourcePort}/${packet.sourceChannel}');
|
|
print(' Dest: ${packet.destPort}/${packet.destChannel}');
|
|
print(' State: ${packet.state}');
|
|
print(' Data size: ${packet.data.length} bytes');
|
|
print(' Timeout height: ${packet.timeoutHeight}');
|
|
print(' Timeout timestamp: ${packet.timeoutTimestamp}');
|
|
}
|
|
|
|
// Get packet by sequence
|
|
final packet = await ibc.packets.get('channel-0', 1);
|
|
print('\nPacket details:');
|
|
print(' Commitment: ${packet.commitment}');
|
|
print(' Receipt: ${packet.receipt}');
|
|
print(' Acknowledgement: ${packet.acknowledgement}');
|
|
|
|
// Get packet receipts
|
|
final receipts = await ibc.packets.getReceipts('channel-0', [1, 2, 3]);
|
|
print('\nPacket receipts:');
|
|
for (final entry in receipts.entries) {
|
|
final status = entry.value ? 'received' : 'not received';
|
|
print(' Sequence ${entry.key}: $status');
|
|
}
|
|
|
|
// Get timed out packets
|
|
final timedOut = await ibc.packets.getTimedOut('channel-0');
|
|
print('\nTimed out packets: ${timedOut.length}');
|
|
for (final p in timedOut) {
|
|
print(' Sequence ${p.sequence}: timeout at ${p.timeoutTimestamp}');
|
|
}
|
|
|
|
// Get unreceived packets
|
|
final unreceived = await ibc.packets.getUnreceived('channel-0');
|
|
final unreceivedStr = unreceived.isEmpty ? 'none' : unreceived.join(', ');
|
|
print('\nUnreceived packet sequences: $unreceivedStr');
|
|
|
|
// Get unacknowledged packets
|
|
final unacked = await ibc.packets.getUnacknowledged('channel-0');
|
|
final unackedStr = unacked.isEmpty ? 'none' : unacked.join(', ');
|
|
print('Unacknowledged packet sequences: $unackedStr');
|
|
|
|
print('');
|
|
}
|
|
|
|
Future<void> relayerExample(SynorIbc ibc) async {
|
|
print('=== Relayer Operations ===');
|
|
|
|
// Get active relayers
|
|
final relayers = await ibc.relayers.list();
|
|
print('Active relayers: ${relayers.length}');
|
|
|
|
for (final relayer in relayers.take(3)) {
|
|
print('\n ${relayer.address}:');
|
|
print(' Chains: ${relayer.chains.join(", ")}');
|
|
print(' Packets relayed: ${relayer.packetsRelayed}');
|
|
print(' Success rate: ${relayer.successRate}%');
|
|
print(' Avg latency: ${relayer.avgLatency}ms');
|
|
print(' Fee rate: ${relayer.feeRate}%');
|
|
}
|
|
|
|
// Get relayer statistics
|
|
final stats = await ibc.relayers.getStats();
|
|
print('\nGlobal relayer statistics:');
|
|
print(' Total relayers: ${stats.totalRelayers}');
|
|
print(' Active relayers: ${stats.activeRelayers}');
|
|
print(' Packets relayed (24h): ${stats.packetsRelayed24h}');
|
|
print(' Total fees earned: ${stats.totalFeesEarned}');
|
|
|
|
// Register as a relayer
|
|
print('\nRegistering as relayer...');
|
|
final registration = await ibc.relayers.register(RelayerRegistration(
|
|
chains: ['synor-1', 'cosmoshub-4'],
|
|
feeRate: 0.1, // 0.1% fee
|
|
minPacketSize: 0,
|
|
maxPacketSize: 1000000,
|
|
));
|
|
print('Registered with address: ${registration.relayerAddress}');
|
|
|
|
// Start relaying (in background)
|
|
print('\nStarting relay service...');
|
|
final relaySession = await ibc.relayers.startRelay(RelayConfig(
|
|
channels: ['channel-0'],
|
|
autoAck: true,
|
|
batchSize: 10,
|
|
pollInterval: 5000,
|
|
));
|
|
print('Relay session started: ${relaySession.sessionId}');
|
|
|
|
// Get relay queue
|
|
final queue = await ibc.relayers.getQueue('channel-0');
|
|
print('\nRelay queue for channel-0: ${queue.length} packets');
|
|
|
|
// Manually relay a packet
|
|
if (queue.isNotEmpty) {
|
|
print('\nRelaying packet...');
|
|
final relayResult = await ibc.relayers.relayPacket(queue.first.sequence, 'channel-0');
|
|
print('Relay result: ${relayResult.status}');
|
|
print(' TX Hash: ${relayResult.txHash}');
|
|
print(' Fee earned: ${relayResult.feeEarned}');
|
|
}
|
|
|
|
// Stop relay session
|
|
await ibc.relayers.stopRelay(relaySession.sessionId);
|
|
print('\nRelay session stopped');
|
|
|
|
print('');
|
|
}
|
|
|
|
Future<void> connectionExample(SynorIbc ibc) async {
|
|
print('=== Connection Information ===');
|
|
|
|
// List connections
|
|
final connections = await ibc.connections.list();
|
|
print('Total connections: ${connections.length}');
|
|
|
|
for (final conn in connections.take(3)) {
|
|
print('\n ${conn.connectionId}:');
|
|
print(' Client: ${conn.clientId}');
|
|
print(' Counterparty: ${conn.counterpartyConnectionId}');
|
|
print(' State: ${conn.state}');
|
|
print(' Versions: ${conn.versions.join(", ")}');
|
|
}
|
|
|
|
// Get connection details
|
|
final connection = await ibc.connections.get('connection-0');
|
|
print('\nConnection-0 details:');
|
|
print(' Delay period: ${connection.delayPeriod}ns');
|
|
print(' Counterparty client: ${connection.counterpartyClientId}');
|
|
print(' Counterparty prefix: ${connection.counterpartyPrefix}');
|
|
|
|
// Get client state
|
|
final client = await ibc.clients.get(connection.clientId);
|
|
print('\nClient state:');
|
|
print(' Chain ID: ${client.chainId}');
|
|
print(' Trust level: ${client.trustLevel}');
|
|
print(' Trusting period: ${client.trustingPeriod}');
|
|
print(' Unbonding period: ${client.unbondingPeriod}');
|
|
print(' Latest height: ${client.latestHeight}');
|
|
print(' Frozen: ${client.frozen}');
|
|
|
|
// Get consensus state
|
|
final consensus = await ibc.clients.getConsensusState(
|
|
connection.clientId,
|
|
client.latestHeight,
|
|
);
|
|
print('\nConsensus state at height ${client.latestHeight}:');
|
|
print(' Timestamp: ${consensus.timestamp}');
|
|
final rootPreview = consensus.root.length > 20
|
|
? consensus.root.substring(0, 20)
|
|
: consensus.root;
|
|
print(' Root: $rootPreview...');
|
|
final validatorsPreview = consensus.nextValidatorsHash.length > 20
|
|
? consensus.nextValidatorsHash.substring(0, 20)
|
|
: consensus.nextValidatorsHash;
|
|
print(' Next validators hash: $validatorsPreview...');
|
|
|
|
print('');
|
|
}
|