This is the transaction from the home page, in full. The card there shows the eight transfers your
ledger sees; here is what they hide.
The transaction: 0xf732d5cc…9ff4b2e9e
Ethereum, block 25515871. Aave v3 and Euler v2, through a solver. Public, not ours, not a
customer’s. Open it in another tab and check me as you go.
It emitted 36 logs. Fourteen move a token, across 17 addresses, and most of that is noise: solver
plumbing, routing hops, other people’s business sharing the block. Discard everything the wallet is
not a party to and eight transfers remain.
Eight is a number a person can hold, so this is not a story about volume. The wallet did one thing,
once. The trouble starts after the noise is gone.
Standard crypto ledgers key on the address, finds the 8 related transfers, and tabulates them with
direction, token, amount, and an address on the other end. The cash cancels: USDC in equals USDC out,
RLUSD in equals RLUSD out, to the wei. Now the ball’s in your court, how do you tag them?
Related transfers
The wallet is a party to every transfer below, so a ledger keyed on the address records them all. It is not wrong about any of them.
| from | to | tokenvariableDebtEthRLUSD | amount-730,797.955311123689495674 |
|---|
| 0x0000…0000 | 0xb721…fa38 | variableDebtEthUSDC | 780,208.473253 |
| 0x09bd…faba | 0xb721…fa38 | USDC | 778,238.290021 |
| 0xb721…fa38 | 0x9bD5…2Cbb | USDC | 186,534.550447 |
| 0xaF53…9BB2 | 0xb721…fa38 | RLUSD | 204,524.934814662449203000 |
| 0xb721…fa38 | 0x9bD5…2Cbb | USDC | 591,703.739574 |
| 0xaF53…9BB2 | 0xb721…fa38 | RLUSD | 526,273.020496461240292674 |
| 0xb721…fa38 | 0x09bd…faba | RLUSD | 730,797.955311123689495674 |
| 0xb721…fa38 | 0x0000…0000 | variableDebtEthRLUSD | 730,794.568432444353918468 |
Each row lands in a queue with no account attached, and someone has to pick one. That is not a knock
on anybody’s product; it is what the data model requires, because a transfer carries an amount and a
counterparty and nothing else.
So your controller looks up an address, finds a Euler USDC vault, and writes a rule: USDC out to
that address is a Euler deposit. Now look at two of the rows: same direction, token, counterparty,
contract, block. One is a debt repayment; the other is a collateral deposit, on a different
account. Opposite sides of the balance sheet, and nothing in either row tells them apart. Whatever
rule the controller writes is wrong on one of them, and no one finds out, because there is nothing to
check it against. (Two more rows are the same trap reversed: identical RLUSD inflows, one a
withdrawal, one a borrow, an asset and a liability in the same row.)
So tagging here is not tedious. It is impossible without digging into the internals of both the Euler and
Aave protocols, and understanding how they work.
So we dig in, so you do not have to. The account a transfer cannot give you is recoverable, but only
at the source: we read each protocol at the smart-contract level, work out what every transaction
actually did, and encode it as a parser. The journal below is generated programmatically, not tagged
by hand. It is the output, the golden our parser is regression-tested against. Six actions, twelve
splits.
What we book
Twelve entries, from six things that actually happened. Four of them land on two addresses the wallet-keyed ledger never looked at.
| type | protocol | position | symbolvariableDebtEthRLUSD | amount-730,797.955311123689495674 |
|---|
| aaveBorrow |
| liability | Aave V3 | — | USDC | -778,238.290021 |
| asset | — | — | USDC | 778,238.290021 |
| eulerRepay(a3d) |
| asset | — | — | USDC | -186,534.550447 |
| liability | Euler | 0xb721…FA3D | USDC | 186,534.550447 |
| eulerWithdraw(a3d) |
| asset | Euler | 0xb721…FA3D | RLUSD | -204,524.934814662449203000 |
| asset | — | — | RLUSD | 204,524.934814662449203000 |
| eulerDeposit(a3e) |
| asset | — | — | USDC | -591,703.739574 |
| asset | Euler | 0xb721…FA3E | USDC | 591,703.739574 |
| eulerBorrow(a3e) |
| liability | Euler | 0xb721…FA3E | RLUSD | -526,273.020496461240292674 |
| asset | — | — | RLUSD | 526,273.020496461240292674 |
| aaveRepay |
| asset | — | — | RLUSD | -730,797.955311123689495674 |
| liability | Aave V3 | — | RLUSD | 730,797.955311123689495674 |
cashassetliability
Sort the twelve by account. Six are the wallet’s own cash, one per currency, and they net to zero.
Those are the six your ledger already had. The other six are positions, and not one is a transfer, so
not one was in the table above. Four sit on Euler sub-accounts your tool has never heard of.
The amounts were never the hard part; every figure here is already in the transfer table at the top.
What a transfer does not carry is which account owes what. The amounts survive the trip. The accounts
do not.
The journal is a book built forward from the transactions, and on its own it is still a claim. So we
check it the other way: read each account’s balance straight from the chain, and reconcile the two,
account by account. A book built from transfers has nothing to reconcile against; a chain balance has
nothing to reconcile with. Holding both ends is the whole thing.
Account values are checked against the chain.
| account | book | on chain | difference |
|---|
| Aave V3USDC | -778,238.290021 | -778,238.33021 | -0.040189 |
| walletUSDC | 0 | 0 | 0✓ |
| EulerUSDC0xb721…FA3D | 186,534.550447 | 186,534.167881 | -0.382566 |
| EulerRLUSD0xb721…FA3D | -204,524.934814662449203 | -204,524.80838235 | 0.126432312449203 |
| walletRLUSD | 0 | 0 | 0✓ |
| EulerUSDC0xb721…FA3E | 591,703.739574 | 591,704.072339 | 0.332765 |
| EulerRLUSD0xb721…FA3E | -526,273.020496461240292674 | -526,273.1553649286 | -0.134868467359707326 |
| Aave V3RLUSD | 730,797.955311123689495674 | 730,797.940324909 | -0.014986214689495674 |
They meet, account by account. The two cash accounts net to exactly zero. The six positions do not,
and that is the point: each carries a block of accrued interest, a number no transfer moved, because
interest emits no event. The reconciliation is what surfaces it, named rather than buried.
We take reconciliation apart on its own page.
More of the accounting that breaks other tools: