I am working on a project where we need to get the "from" and "to" address for every transaction across a number of cryptocurrencies. In account-based currencies, this is pretty straightforward, but in UTXO-based it's not. Are there known algorithms that "normalize" between the two?
No, Bitcoin transactions cannot be cleanly projected on a single "from address" or a single "to address". Bitcoin can facilitate many payments in a single transaction and there may be multiple senders and usually are multiple receivers.
Even in a simple transaction where one user pays another user, the sender may use multiple inputs, and almost always will assign a change output to themselves. Thus, there is one sender (or more) and two receivers.
Nothing stops multiple users to create a transaction together. E.g. in a PayJoin transaction, the receiver contributes an input to a transaction that pays themselves, thus making it two senders, and one or two receivers (depending on whether there is change).
Two users may also join to make separate payments in one transaction. In a so-called CoinJoin transaction, they each are contributing their own inputs and assigning some funds to receivers and sending change back to themselves. So you might for example have two senders and four receivers.
The challenge gets a whole new dimension when you think about how custodial services work. When you e.g. send funds from an online wallet service or brokerage, the service will often batch dozens of payments/withdrawals together and pay them all from their omnibus wallet. The initiators of the payments and withdrawals do not control any of the addresses that fund the transaction, but are "senders" in the sense that they mandated the payments. The custodian is the actual sender, but may not know who any of the receivers are and is only executing payments on behalf of customers. E.g. sending to any of the "from" addresses of the transaction would likely credit some random user of the service which is neither the service or any of the senders.
There is no such thing as a "from" address in a bitcoin transaction, and you can usually not distinguish whether an output goes back to the sender or to a receiver.
I would suggest that you assume a general case of multiple senders and multiple receivers for each transaction in your model, and track account-based cryptocurrencies as the special case of a single sender and single receiver. Note that even for account-based currencies, a lot of smart contracts also allow sending to multiple recipients in one transaction.
Bitcoin transactions can have multiple inputs and outputs of different types, it does not make sense to tag one of the input as FROM and one of the outputs as TO.
You will have to make exceptions in your project for Bitcoin else it will be misleading.