If you have written an ERC20, LSP7 looks at first like the same contract with unfamiliar method names. It isn't. The interesting differences are not about how a balance moves — they are about what is allowed to happen after it moves.
The transfer that nobody hears
An ERC20 transfer is a bookkeeping entry. It decrements one balance, increments another, emits an event, and returns. The recipient is never consulted and never notified. If the recipient is a contract that was not written to expect that token, nothing objects — the tokens simply arrive and sit there.
This is the origin of a whole genre of loss. Send $USDC to a contract with no withdrawal function and it is gone. Not stolen, not burned: gone, in the specific sense that the ledger says an address owns them and no code exists that can ever move them again.
LSP7's transfer takes two more arguments:
function transfer(
address from,
address to,
uint256 amount,
bool force,
bytes memory data
) external;
force is the interesting one. When it is false, the transfer reverts if
the recipient is a contract that does not implement the LSP1 universal receiver.
The default is to refuse to send tokens somewhere that cannot acknowledge them.
ERC20 optimises for the transfer succeeding. LSP7 optimises for the transfer being received. Most of the time those are the same thing, and the exceptions are exactly the expensive ones.
Tokens that can talk back
The other half is LSP1. An LSP7 transfer calls universalReceiver on both sides,
handing over a type id and the data bytes the sender attached:
function universalReceiver(bytes32 typeId, bytes memory receivedData)
external payable returns (bytes memory);
A Universal Profile routes that call to a delegate, which means a profile can react to being paid. It can update a registry of assets it holds, forward the tokens, reject a category outright, or trigger something offchain through an event. None of that is reachable from an ERC20 transfer, because an ERC20 transfer tells the recipient nothing.
This is also why a UP knows which tokens it owns without an indexer scanning
every Transfer event ever emitted. The profile was told at the time.
The comparison that matters
| Concern | ERC20 | LSP7 |
|---|---|---|
| Recipient notified | No | Yes, via LSP1 |
| Guard against dead addresses | None | force = false reverts |
| Data attached to a transfer | No | bytes data |
| Metadata | name / symbol / decimals |
ERC725Y key-value, updatable |
| Delegated spending | approve / allowance |
authorizeOperator / operators |
| Divisibility | Always divisible | Can be non-divisible |
Metadata deserves more than a table row. An ERC20's identity is three immutable
getters; anything richer — an icon, a description, links — lives in whatever
offchain list a given interface happens to trust. LSP7 stores metadata in
ERC725Y under LSP4Metadata, as a pointer to a JSON document with a verification
hash. The token carries its own description, and that description can be updated
by whoever holds the right to do so, without redeploying anything.
Operators are not just renamed allowances
authorizeOperator takes a third argument, operatorNotificationData, and
notifies the operator that it was authorised. An ERC20 approve is a silent
write to a mapping; the spender finds out by polling. It is a small difference
that removes a class of "did the approval land yet" polling loops.
What it costs
Being honest about the trade: LSP7 is not ERC20, and pretending otherwise breaks things. Every exchange, bridge, price oracle, wallet and analytics tool that hardcodes the ERC20 interface will not see an LSP7 token. On LUKSO that is fine, because the ecosystem was built around it. Anywhere else it is a real cost, paid in integrations that need an adapter or simply do not exist.
That is why an app spanning several chains ends up supporting both rather than picking a winner. LSP7 and LSP8 where the chain is LUKSO; ERC20 and ERC721 everywhere else. Not indecision — the standards are answers to the questions their own ecosystems were asking.
The one-line version: if you want a token that is a number in a mapping, ERC20 is finished and universally understood. If you want a token that participates in what happens when it arrives, that is what LSP7 was for.