synor/sdk/flutter/lib/src/ibc/synor_ibc.dart
Gulshan Yadav 97add23062 feat(sdk): implement IBC SDK for all 12 languages
Implement Inter-Blockchain Communication (IBC) SDK with full ICS
protocol support across all 12 programming languages:
- JavaScript/TypeScript, Python, Go, Rust
- Java, Kotlin, Swift, Flutter/Dart
- C, C++, C#/.NET, Ruby

Features:
- Light client management (Tendermint, Solo Machine, WASM)
- Connection handshake (4-way: Init, Try, Ack, Confirm)
- Channel management with ordered/unordered support
- ICS-20 fungible token transfers
- HTLC atomic swaps with hashlock (SHA256) and timelock
- Packet relay with timeout handling
2026-01-28 12:53:46 +05:30

278 lines
7.5 KiB
Dart

/// Synor IBC SDK for Flutter/Dart
///
/// Inter-Blockchain Communication (IBC) protocol for cross-chain interoperability.
library synor_ibc;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'types.dart';
export 'types.dart';
/// Main IBC client
class SynorIbc {
final IbcConfig config;
final http.Client _client;
bool _closed = false;
late final LightClientClient clients;
late final ConnectionsClient connections;
late final ChannelsClient channels;
late final TransferClient transfer;
late final SwapsClient swaps;
SynorIbc(this.config) : _client = http.Client() {
clients = LightClientClient(this);
connections = ConnectionsClient(this);
channels = ChannelsClient(this);
transfer = TransferClient(this);
swaps = SwapsClient(this);
}
String get chainId => config.chainId;
Future<Map<String, dynamic>> getChainInfo() async {
return _get('/chain');
}
Future<Height> getHeight() async {
final result = await _get('/chain/height');
return Height.fromJson(result);
}
Future<bool> healthCheck() async {
try {
final result = await _get('/health');
return result['status'] == 'healthy';
} catch (_) {
return false;
}
}
void close() {
_closed = true;
_client.close();
}
bool get isClosed => _closed;
// Internal HTTP methods
Future<Map<String, dynamic>> _get(String path) async {
_checkClosed();
final response = await _client.get(
Uri.parse('${config.endpoint}$path'),
headers: _headers,
);
return _handleResponse(response);
}
Future<Map<String, dynamic>> _post(String path, Map<String, dynamic> body) async {
_checkClosed();
final response = await _client.post(
Uri.parse('${config.endpoint}$path'),
headers: _headers,
body: jsonEncode(body),
);
return _handleResponse(response);
}
Map<String, String> get _headers => {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${config.apiKey}',
'X-SDK-Version': 'flutter/0.1.0',
'X-Chain-Id': config.chainId,
};
Map<String, dynamic> _handleResponse(http.Response response) {
if (response.statusCode >= 400) {
Map<String, dynamic>? error;
try {
error = jsonDecode(response.body);
} catch (_) {}
throw IbcException(
error?['message'] ?? 'HTTP ${response.statusCode}',
error?['code'],
response.statusCode,
);
}
return jsonDecode(response.body);
}
void _checkClosed() {
if (_closed) {
throw const IbcException('Client has been closed', 'CLIENT_CLOSED');
}
}
}
/// Light client sub-client
class LightClientClient {
final SynorIbc _ibc;
LightClientClient(this._ibc);
Future<ClientId> create({
required ClientType clientType,
required ClientState clientState,
required Map<String, dynamic> consensusState,
}) async {
final result = await _ibc._post('/clients', {
'client_type': clientType.name,
'client_state': clientState.toJson(),
'consensus_state': consensusState,
});
return ClientId(result['client_id']);
}
Future<ClientState> getState(ClientId clientId) async {
final result = await _ibc._get('/clients/${clientId.id}/state');
return ClientState.fromJson(result);
}
Future<List<Map<String, dynamic>>> list() async {
final result = await _ibc._get('/clients');
return List<Map<String, dynamic>>.from(result['clients'] ?? []);
}
}
/// Connections sub-client
class ConnectionsClient {
final SynorIbc _ibc;
ConnectionsClient(this._ibc);
Future<ConnectionId> openInit({
required ClientId clientId,
required ClientId counterpartyClientId,
}) async {
final result = await _ibc._post('/connections/init', {
'client_id': clientId.id,
'counterparty_client_id': counterpartyClientId.id,
});
return ConnectionId(result['connection_id']);
}
Future<Map<String, dynamic>> get(ConnectionId connectionId) async {
return _ibc._get('/connections/${connectionId.id}');
}
Future<List<Map<String, dynamic>>> list() async {
final result = await _ibc._get('/connections');
return List<Map<String, dynamic>>.from(result['connections'] ?? []);
}
}
/// Channels sub-client
class ChannelsClient {
final SynorIbc _ibc;
ChannelsClient(this._ibc);
Future<void> bindPort(PortId portId, String module) async {
await _ibc._post('/ports/bind', {'port_id': portId.id, 'module': module});
}
Future<ChannelId> openInit({
required PortId portId,
required ChannelOrder ordering,
required ConnectionId connectionId,
required PortId counterpartyPort,
required String version,
}) async {
final result = await _ibc._post('/channels/init', {
'port_id': portId.id,
'ordering': ordering.name,
'connection_id': connectionId.id,
'counterparty_port': counterpartyPort.id,
'version': version,
});
return ChannelId(result['channel_id']);
}
Future<Map<String, dynamic>> get(PortId portId, ChannelId channelId) async {
return _ibc._get('/channels/${portId.id}/${channelId.id}');
}
Future<List<Map<String, dynamic>>> list() async {
final result = await _ibc._get('/channels');
return List<Map<String, dynamic>>.from(result['channels'] ?? []);
}
}
/// Transfer sub-client (ICS-20)
class TransferClient {
final SynorIbc _ibc;
TransferClient(this._ibc);
Future<Map<String, dynamic>> transfer({
required String sourcePort,
required String sourceChannel,
required String denom,
required String amount,
required String sender,
required String receiver,
Timeout? timeout,
String? memo,
}) async {
final body = <String, dynamic>{
'source_port': sourcePort,
'source_channel': sourceChannel,
'token': {'denom': denom, 'amount': amount},
'sender': sender,
'receiver': receiver,
};
if (timeout != null) {
body['timeout_height'] = timeout.height.toJson();
body['timeout_timestamp'] = timeout.timestamp.toString();
}
if (memo != null) body['memo'] = memo;
return _ibc._post('/transfer', body);
}
Future<Map<String, dynamic>> getDenomTrace(String ibcDenom) async {
return _ibc._get('/transfer/denom_trace/$ibcDenom');
}
}
/// Swaps sub-client (HTLC)
class SwapsClient {
final SynorIbc _ibc;
SwapsClient(this._ibc);
Future<Map<String, dynamic>> initiate({
required String responder,
required Map<String, dynamic> initiatorAsset,
required Map<String, dynamic> responderAsset,
}) async {
return _ibc._post('/swaps/initiate', {
'responder': responder,
'initiator_asset': initiatorAsset,
'responder_asset': responderAsset,
});
}
Future<void> lock(SwapId swapId) async {
await _ibc._post('/swaps/${swapId.id}/lock', {});
}
Future<Map<String, dynamic>> respond(SwapId swapId, Map<String, dynamic> asset) async {
return _ibc._post('/swaps/${swapId.id}/respond', {'asset': asset});
}
Future<Map<String, dynamic>> claim(SwapId swapId, List<int> secret) async {
return _ibc._post('/swaps/${swapId.id}/claim', {
'secret': base64Encode(secret),
});
}
Future<Map<String, dynamic>> refund(SwapId swapId) async {
return _ibc._post('/swaps/${swapId.id}/refund', {});
}
Future<AtomicSwap> get(SwapId swapId) async {
final result = await _ibc._get('/swaps/${swapId.id}');
return AtomicSwap.fromJson(result);
}
Future<List<AtomicSwap>> listActive() async {
final result = await _ibc._get('/swaps/active');
return (result['swaps'] as List).map((e) => AtomicSwap.fromJson(e)).toList();
}
}