Fix Koios API integration for unfunded addresses

- Add trailing slash to Koios base URLs (required by Retrofit)
- Handle empty response bodies for unfunded addresses (returns [] from API)
- getBalance now returns 0 for unfunded addresses instead of failing
- getUtxos now returns empty list for unfunded addresses
- Add debug logging for Koios responses
This commit is contained in:
Kayos 2026-03-28 13:18:08 -07:00
parent 9e9192dd3b
commit 9613a1e6fc
2 changed files with 45 additions and 30 deletions

View file

@ -48,8 +48,8 @@ object CardanoNetworkConfig {
* Rate limits: 100 req/10s for anonymous users.
*/
val KOIOS_BASE_URL: String = when (NETWORK) {
CardanoNetwork.TESTNET -> "https://preprod.koios.rest/api/v1"
CardanoNetwork.MAINNET -> "https://api.koios.rest/api/v1"
CardanoNetwork.TESTNET -> "https://preprod.koios.rest/api/v1/"
CardanoNetwork.MAINNET -> "https://api.koios.rest/api/v1/"
}
/**

View file

@ -52,48 +52,62 @@ class KoiosCardanoClient @Inject constructor() : CardanoClient {
throttleRequest()
val result = backendService.addressService.getAddressInfo(address)
if (result.isSuccessful) {
val info = result.value
val lovelace = info.amount
?.find { it.unit == "lovelace" }
?.quantity
?.toLong()
?: 0L
Result.success(lovelace)
} else {
Result.failure(parseError(result.response))
Timber.tag(TAG).d("getBalance result: isSuccessful=${result.isSuccessful}, response=${result.response?.take(200)}")
when {
result.isSuccessful -> {
val info = result.value
val lovelace = info.amount
?.find { it.unit == "lovelace" }
?.quantity
?.toLong()
?: 0L
Result.success(lovelace)
}
result.response?.contains("Empty") == true -> {
// Empty response means unfunded address - return 0 balance
Timber.tag(TAG).d("Address has no history, returning 0 balance")
Result.success(0L)
}
else -> {
Result.failure(parseError(result.response))
}
}
}
}
override suspend fun getUtxos(address: String): Result<List<Utxo>> =
withRetry("getUtxos($address)") {
withContext(Dispatchers.IO) {
throttleRequest()
val result = backendService.utxoService.getUtxos(address, 100, 1)
if (result.isSuccessful) {
val utxos = result.value.map { utxo ->
val lovelace = utxo.amount
?.find { it.unit == "lovelace" }
?.quantity
?.toLong()
?: 0L
when {
result.isSuccessful -> {
val utxos = result.value.map { utxo ->
val lovelace = utxo.amount
?.find { it.unit == "lovelace" }
?.quantity
?.toLong()
?: 0L
Utxo(
txHash = utxo.txHash,
outputIndex = utxo.outputIndex,
amount = lovelace,
address = address,
)
Utxo(
txHash = utxo.txHash,
outputIndex = utxo.outputIndex,
amount = lovelace,
address = address,
)
}
Result.success(utxos)
}
result.response?.contains("Empty") == true -> {
// Empty response means no UTXOs - return empty list
Result.success(emptyList())
}
else -> {
Result.failure(parseError(result.response))
}
Result.success(utxos)
} else {
Result.failure(parseError(result.response))
}
}
}
override suspend fun submitTx(signedTxCbor: String): Result<String> =
withRetry("submitTx") {
withContext(Dispatchers.IO) {
@ -176,6 +190,7 @@ class KoiosCardanoClient @Inject constructor() : CardanoClient {
throttleRequest()
val result = backendService.addressService.getAddressInfo(address)
Timber.tag(TAG).d("getBalance result: isSuccessful=${result.isSuccessful}, response=${result.response?.take(200)}")
if (result.isSuccessful) {
val info = result.value
val assets = info.amount