Token Transfers Not Tracked by Transfer() Events

Hi, I am currently conducting an analysis of Lido StETH holders and their balances over time. To do this quickly and cheaply I am using etherscans method of subtracting outbound tokens from inbound tokens to get the balance of an address at any point in time.

This has been successful in principal, however it seems that there is sometimes some unaccounted for transfers (I suspect from mints?) that are causing balances to return as negative.

Here is one such example:

At block 16546200: Etherscan value ~-4183336466029845598375,
balanceOf() Value: 2798160878585542078071

Strangely though in the contract code it seems that mints do in fact emit a transfer() from NULL ā†’ Msg.Sender on successful mints.

if anybody has any information that could help me fill in the gaps here it would be greatly appreciated.

Hey,

sorry, but have you accounted for stETH shares mechanics?

This definitely could be it. Do you happen to know if the rebase emits an event or transaction?

The best approach in my opinion would be to use our Subgraph.

In a single request you can get shares of an address with protocol totals (total shares and pooled ETH) at any block. Then, you just need to calculate the balance.

{
  shares (id: "0x123", block: {number: 123}) {
    shares
  }
  totals (id: "", block: {number: 123}) {
    totalShares
    totalPooledEther
  }
}

Empty string is correct here for the totals id

You will get:

{
  "data": {
    "shares": {
      "shares": "123"
    },
    "totals": {
      "totalShares": "123",
      "totalPooledEther": "123"
    }
  }
}

To get the balance:

const balance = shares * totalPooledEther / totalShares

Make sure to parse as big numbers eg use BigInt in JS

If you are going to do this manually, you can use the Subgraph Playground.

If automating, I highly recommend using graph-client to make requests.

4 Likes

Can you explain better.
Iā€™m trying to understand you honestly i wish iā€™d

1 Like

I think you need to explicitly handle the token rebase. There are events that you can listen to for this.

For example, try this oracle update transaction:

cast run 0xfcf4097cc6b61801e5c9f3a11f16e1d7f7fa76921d182e8b0c4ff88c1a6b5b3f

ā”‚   ā”‚   ā”‚   ā”œā”€ [20303] 0x442af784A788A5bd6F42A01Ebe9F287a871243fb::handlePostTokenRebase(1708516811 [1.708e9], 86400 [8.64e4], 8463587803628308184539892 [8.463e24], 9798615734807496829711841 [9.798e24], 8453784041596901024578147 [8.453e24], 9788194557525724274018277 [9.788e24], 89139152434320508116 [8.913e19])
ā”‚   ā”‚   ā”‚   ā”‚   ā”œā”€ [1763] 0xb8FFC3Cd6e7Cf5a098A1c92F48009765B24088Dc::getApp(0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f, 0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93)
ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā”œā”€ [820] 0x2b33CF282f867A7FF693A66e11B0FcC5552e4425::getApp(0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f, 0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93) [delegatecall]
ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā””ā”€ ā† 0x000000000000000000000000a29b819654ce6224a222bb5f586920105e2d7e0e
ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā””ā”€ ā† 0x000000000000000000000000a29b819654ce6224a222bb5f586920105e2d7e0e
ā”‚   ā”‚   ā”‚   ā”‚   ā”œā”€ [16893] 0xa29b819654cE6224A222bb5f586920105E2D7E0E::handlePostTokenRebase(1708516811 [1.708e9], 86400 [8.64e4], 8463587803628308184539892 [8.463e24], 9798615734807496829711841 [9.798e24], 8453784041596901024578147 [8.453e24], 9788194557525724274018277 [9.788e24], 89139152434320508116 [8.913e19]) [delegatecall]
ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā”œā”€ emit PostTotalShares(: 9788194557525724274018277 [9.788e24], : 9798615734807496829711841 [9.798e24], : 86400 [8.64e4], : 8453784041596901024578147 [8.453e24])
ā”‚   ā”‚   ā”‚   ā”‚   ā”‚   ā””ā”€ ā† ()
ā”‚   ā”‚   ā”‚   ā”‚   ā””ā”€ ā† ()
ā”‚   ā”‚   ā”‚   ā”œā”€  emit topic 0: 0xff08c3ef606d198e316ef5b822193c489965899eb4e3c248cea1a4626c3eda50
ā”‚   ā”‚   ā”‚   ā”‚       topic 1: 0x0000000000000000000000000000000000000000000000000000000065d5e5cb
ā”‚   ā”‚   ā”‚   ā”‚           data: 0x000000000000000000000000000000000000000000000000000000000001518000000000000000000000000000000000000000000007003c03a1adebbe75a2f4000000000000000000000000000000000000000000081af005fa5c4433d035e100000000000000000000000000000000000000000006fe288d1a34e9d8b3ba630000000000000000000000000000000000000000000818bb1717484203c6c7e5000000000000000000000000000000000000000000000004d50de2dc0b1204d4