IntersectMBO / IntersectMBO/evolution-sdk

Koios getUtxosByOutRef: fails for outputs of Plutus transactions, and drops all but one transaction

Open
#540 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
22
Forks
30
Avg merge
5h 29m
Merged PRs (30d)
12

Description

## Summary

In `KoiosEffect.getUtxosByOutRef` (0.5.13; unchanged on `main` at a98c483):

1. **Outputs of a transaction that ran a Plutus script can't be fetched.** The lookup requests `/tx_info` for the creating transaction and decodes the whole row with `TxInfoSchema`. For those transactions Koios returns `null` in three `plutus_contracts[]` fields the schema requires — `address`, `bytecode` and `input.datum` — so decoding fails and the call rejects.
2. **Refs spanning several transactions return outputs from only one.** `const [result] = yield* ...` keeps the first `tx_info` row. Koios does return one row per hash; the others are dropped without an error.

## Reproduce (preprod)

```ts
import { TransactionHash, TransactionInput } from "@evolution-sdk/evolution"
import { Koios } from "@evolution-sdk/evolution/sdk/provider/Koios"

const koios = new Koios("https://preprod.koios.rest/api/v1")
const ref = (hash: string, index: number) =>
new TransactionInput.TransactionInput({ transactionId: TransactionHash.fromHex(hash), index: BigInt(index) })

// 1. An output of a transaction that ran Plutus scripts
try {
await koios.getUtxosByOutRef([ref("01b6275f8fd25c60cf9887fe38ee3a340b3f3b2eca15ae0c80c54c573016dcc6", 0)])
} catch (e) {
console.log((e as Error).message) // Koios getUtxosByOutRef failed
}

// 2. Outputs of two ordinary transactions
const utxos = await koios.getUtxosByOutRef([
ref("37120eda148486250fd331b8c3be6ea134a111b5ccf4f951f659a0f71c4c7c06", 0),
ref("ba269ae6f25b2ba505d3da251bbed9904f10466db05ed87c163edec66145eb3e", 0),
])
console.log(utxos.length) // 1
```

## Expected

1. The output is returned.
2. Both outputs are returned.

## Actual

1. Rejects with `Koios getUtxosByOutRef failed`. The underlying `ParseError` points at `plutus_contracts[0].address: Expected string, actual null`.
2. Logs `1`.

## Notes

- Decoding with `{ errors: "all" }` shows the three fields fail independently, so relaxing only `address` is not enough. On the wallet I tested, 10 of the 13 transactions that created its UTxOs failed this way, matching the API failures one for one.
- `/utxo_info` with `_utxo_refs` (and `_extended: true`) returns exactly the requested outputs without decoding whole transactions: 17 refs across 13 transactions gave 17 rows, including outputs of the failing ones. It would avoid both problems.
- The `ParseError` is attached as the `ProviderError`'s cause, but the Promise API rejects with Effect's `FiberFailure`, where `err.cause` is `undefined`, so Promise callers only see the generic message. That is how this showed up downstream: `@x402/cardano`'s facilitator reports `exact_cardano_facilitator_chain_lookup_failed` / `Koios getUtxosByOutRef failed` when a payment spends an output of such a transaction.

Investigated with AI assistance (Claude Code); the reproduction above was run against preprod on 0.5.13.

Contributor guide

Open the contributing guide

Research direction

Start at KoiosEffect.getUtxosByOutRef and inspect its current /tx_info lookup and TxInfoSchema decoding. Compare that path with the issue's /utxo_info request using _utxo_refs and _extended: true, then run the preprod reproductions. Done means Plutus-created outputs are returned and refs from multiple transactions produce every requested output.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.