IntersectMBO / IntersectMBO/evolution-sdk
Koios getUtxosByOutRef: fails for outputs of Plutus transactions, and drops all but one transaction
- 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
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