import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/pointycastle.dart';
import 'package:http/http.dart' as http;
const _lcd = 'https://rest.provider-sentry-01.hub-testnet.polypore.xyz'; // Cosmos Hub provider testnet
const _chainId = 'provider';
const _denom = 'uatom';
const _gasLimit = '200000';
const _feeAmount = '5000';
Future<String> sendCosmosTransfer({
required String to,
required double amount,
required String fromAddress,
required String pubkeyHex, // hex-encoded compressed secp256k1 pubkey
required SignWithEvm signRaw, // callback: (Uint8List digest) → Future<String>
}) async {
final microAmount = (amount * 1000000).round().toString();
final pubkeyBytes = hexToBytes(pubkeyHex);
// Fetch account info and chain ID in parallel
final results = await Future.wait([
_lcdGet('/cosmos/auth/v1beta1/accounts/$fromAddress'),
_lcdGet('/cosmos/base/tendermint/v1beta1/node_info'),
]);
final account = (results[0]['account'] ?? {}) as Map<String, dynamic>;
final accountNumber = (account['account_number'] ?? '0').toString();
final sequence = (account['sequence'] ?? '0').toString();
final nodeInfo = (results[1]['default_node_info'] ?? {}) as Map<String, dynamic>;
final chainId = (nodeInfo['network'] ?? _chainId).toString();
// Build Amino sign doc
final signDoc = {
'account_number': accountNumber,
'chain_id': chainId,
'fee': {
'amount': [{'amount': _feeAmount, 'denom': _denom}],
'gas': _gasLimit,
},
'memo': '',
'msgs': [
{
'type': 'cosmos-sdk/MsgSend',
'value': {
'amount': [{'amount': microAmount, 'denom': _denom}],
'from_address': fromAddress,
'to_address': to,
},
},
],
'sequence': sequence,
};
final digest = Digest('SHA-256').process(
Uint8List.fromList(utf8.encode(_sortedJsonStringify(signDoc))),
);
final signature = await signRaw(digest);
// Normalize to low-S (required by Cosmos SDK)
final sigRaw = hexToBytes(strip0x(signature));
final sNorm = normalizeLowS(
BigInt.parse(bytesToHex(sigRaw.sublist(32, 64)), radix: 16),
);
final compactSig = hexToBytes(
bytesToHex(sigRaw.sublist(0, 32)) + sNorm.toRadixString(16).padLeft(64, '0'),
);
// Encode as Protobuf TxRaw and broadcast
// (See flutter-multichain-demo/lib/chains/cosmos.dart for full Protobuf encoding)
return _broadcastTx(compactSig, pubkeyBytes, signDoc);
}
Future<Map<String, dynamic>> _lcdGet(String path) async {
final res = await http.get(Uri.parse('$_lcd$path'));
if (res.statusCode != 200) throw Exception('Cosmos LCD error: $path');
return json.decode(res.body) as Map<String, dynamic>;
}