Overview

If you’re building a product that indexes ETH balances on Blast, it’s important that you understand how Blast’s account model differs from other EVM chains.

Instead of representing ETH balances as integer values, Blast represents them in terms of shares. Each account has some number of shares associated with it and that number is multiplied by a global share price to determine its ETH balance. In this way, Blast account balances are capable of automatically rebasing as new yield is reported and the global share price increases.

For the most accurate indexing of account balances, we recommend saving individual account shares along with the global share price instead of saving account balances directly. At read time an account’s balance can then be derived by multiplying its shares with the global share price.

The rest of this document describes how to accurately index every account’s ETH balance on Blast using this method.

If you only need to index a small number of account balances and would prefer to avoid this custom indexing logic, you can opt out of rebasing on those accounts (see yield modes below) and index their balances as you would normally.

Yield Modes

Blast accounts support three different yield modes, giving the user / builder control over how they want to handle rebasing when new yield is reported:

Yield ModeDescription
AUTOMATICETH balance automatically rebases (increasing only)
VOID ETH balance never changes in response to yield; no yield is earned
CLAIMABLEYield accumulates separately and can be claimed to any recipient address

Defaults

By default, all accounts on Blast are set to AUTOMATIC yield mode, which means their ETH balances will rebase automatically.

However, when a smart contract is deployed to an address, the account is switched to VOID mode. In practice, this means that smart contract accounts default to VOID mode and EOAs default to AUTOMATIC mode.

We say “in practice” because it is possible (but very uncommon) for ETH to be held by an account before a smart contract is deployed to it.

Both smart contracts and EOAs can change their yield modes (e.g. a smart contract opting in or an EOA opting out) using the Blast yield predeploy at address  0x4300000000000000000000000000000000000002.

See our article on Receiving and claiming ETH Yield for details.

Blast’s Account Model

Blast uses a modified version of op-geth (blast-geth) to represent balances in terms of shares and enable all accounts to update as yield is reported. More specifically, blast-geth modifies the underlying StateAccount struct that represents account state to accomplish this:

op-geth (Upstream)


    type StateAccount struct {
        Nonce    uint64
        Root     common.Hash
        CodeHash []byte
-       Balance  *big.Int



    }

blast-geth


    type StateAccount struct {
        Nonce     uint64
        Root      common.Hash
        CodeHash  []byte
+       Flags     uint8
+       Fixed     *big.Int
+       Shares    *big.Int
+       Remainder *big.Int
    }

The desired yield mode is stored in the Flags field and the Fixed, Shares, and Remainder fields store the data required to calculate account balances in each of the three yield modes as follows:

In AUTOMATIC mode, balances increase automatically as yield is reported to the L2:

balance = account.shares * sharePrice + account.remainder
claimable = 0

In this mode, account.remainder will be a dusty value to enable wei-level precision.

It will always be the case that account.remainder < sharePrice (otherwise, account.shares would just be higher).

While modifying the type of the state trie is a significant change, there is no difference from the perspective of a single transaction; Blast’s BALANCE opcode behaves the same as it normally does, so smart contracts don’t need to consider their account’s shares.

To remain EVM compatible, Blast’s share system guarantees wei-level precision, allowing accounts with rebasing balances to transfer precise amounts of wei to accounts with non-rebasing balances and vice versa.

Global Share Price

The global share price that determines how much ETH each share is worth is stored in the SharesBase predeploy at address 0x4300000000000000000000000000000000000000.

This contract tracks the share price, the total number of shares, and the total amount of ETH that has yet to be reflected in the share price. You can read the current ETH share price by calling SharesBase::price() as demonstrated below using cast call or the eth_call RPC:

cast call 0x4300000000000000000000000000000000000000 "price()(uint256)" \
  --block 2097152 --rpc-url https://rpc.blast.io \
  | awk '{ printf "%.9f gwei/share\n", $1 / 1e9 }'
In these examples we pipe the output into awk and jq for styling purposes to show the units of the return value.

You can also index the NewPrice(uint256 price) event emitted by this contract to get the new global share price whenever an update occurs. Generally, this update happens every day at midnight UTC, but this schedule is not guaranteed in code and may be subject to change.

Indexing Account Shares

As mentioned above, for the most accurate results it is recommended that you index account shares instead of account balances directly.

For the most part, this works the same way as indexing balances on ETH mainnet: we first identify which accounts were touched in the transactions within a given block and then re-fetch the balances of those accounts on that block.

Usually, these ETH balances are re-fetched using the eth_getBalance RPC method, but on Blast we need to be able to read and save the account’s shares value instead. Blast nodes expose a new RPC method, eth_getBalanceValues, to support indexing the flags, shares, remainder, and fixed fields for accounts.

eth_getBalanceValues

This RPC method exposes the Blast-specific fields of the StateAccount struct.

Request

address
string
required

Same as eth_getBalance

blockNumber
string
required

Same as eth_getBalance; hex or “latest”, “pending”, etc

id
number
required

Unique request identifier

Request Example

curl -s -X POST \
  -H "Content-Type: application/json" \
  --url <rpc-url>
  --data '{
        "jsonrpc":"2.0",
        "method":"eth_getBalanceValues",
        "params":["0x4200000000000000000000000000000000000000","latest"],
        "id":1
    }'   

Response

jsonrpc
string

String representing RPC version number

result
object

Object containing fixed, flags, remainder, and shares

fixed
string

Hex number string, e.g. "0x1"

flags
string

Hex number string representing one of three yield modes:

  • "0x0": AUTOMATIC
  • "0x1": VOID
  • "0x2": CLAIMABLE
remainder
string

Hex number string, e.g. "0x1"

shares
string

Hex number string, e.g. "0x1"

id
number

Unique request identifier

Response Example

{
  "jsonrpc": "2.0",
  "result":  {
    "fixed": "0x0",
    "flags": "0x1",
    "remainder": "0x0",
    "shares": "0x0"
  },
  "id": 1
}

Edge Cases

On Ethereum mainnet, selfdestruct allows ETH to be sent to a beneficiary address without triggering any code at that address. As a result, the beneficiary account’s balance changes without there being any call made. Indexers must handle this edge case to ensure perfect accounting.

Blast introduces a similar edge case that indexers should be aware of. Accounts set to CLAIMABLE yield mode can claim accumulated yield to an arbitrary beneficiary address. This claim operation works the same as selfdestruct in that it can increase an arbitrary account’s balance without a call.

While this edge case is exceptionally rare and not particularly important to handle, we include it here for completeness.