Onchain.Transfer (onchain v0.5.0)

Copy Markdown View Source

Transfer event parser for ERC-20, ERC-721, and ERC-1155 token standards.

Parses raw Ethereum logs into normalized %Transfer{} structs — the "follow the money" primitive for wallet analytics. Every token movement on Ethereum emits a Transfer event; this module decodes all three standards.

Does

Does Not

  • Index or store transfers (see rexex for durable indexing)
  • Track balances over time (compose with Onchain.ERC20 for snapshots)
  • Handle non-standard transfer events (custom names, non-indexed params)

Token Standard Detection

ERC-20 and ERC-721 share the same Transfer(address,address,uint256) topic hash. Distinguished by topic count:

  • 3 topics [topic0, from, to] + value in data → ERC-20
  • 4 topics [topic0, from, to, tokenId] + empty/no data → ERC-721

ERC-1155 uses separate TransferSingle and TransferBatch signatures.

Error Format

Functions

FunctionPurpose
parse_log/1Single raw log → Transfer struct(s)
parse_log!/1Same, raises on error
parse_logs/1List of raw logs → Transfer structs (skips non-Transfer)
parse_logs!/1Same, raises on error
fetch/2eth_get_logs + parse_logs convenience
fetch!/2Same, raises on error
transfer_topics/0The 3 topic0 hashes for filter building

API Functions

FunctionArityDescriptionParam Kinds
fetch!2Fetch transfer logs from chain and parse into structs. Raises on error.filter: value, opts: value
fetch2Fetch transfer logs from chain and parse into structs.filter: value, opts: value
parse_logs!1Parse a list of raw logs, skipping non-Transfer events. Raises on error.logs: value
parse_logs1Parse a list of raw logs, skipping non-Transfer events.logs: value
parse_log!1Parse a single raw log map into Transfer struct(s). Raises on error.log: value
parse_log1Parse a single raw log map into Transfer struct(s).log: value
transfer_topics0Returns the 3 topic0 hashes for ERC-20/721/1155 Transfer events.-

Summary

Functions

Fetch transfer logs from chain and parse into structs.

Fetch transfer logs from chain and parse into structs. Raises on error.

Parse a single raw log map into Transfer struct(s).

Parse a single raw log map into Transfer struct(s). Raises on error.

Parse a list of raw logs, skipping non-Transfer events.

Parse a list of raw logs, skipping non-Transfer events. Raises on error.

Returns the 3 topic0 hashes for ERC-20/721/1155 Transfer events.

Types

t()

@type t() :: %Onchain.Transfer{
  amount: non_neg_integer() | nil,
  block_number: non_neg_integer(),
  from: String.t(),
  log_index: non_neg_integer(),
  operator: String.t() | nil,
  to: String.t(),
  token: String.t(),
  token_id: non_neg_integer() | nil,
  token_standard: :erc20 | :erc721 | :erc1155,
  transaction_hash: String.t()
}

Functions

fetch(filter, opts \\ [])

@spec fetch(
  map(),
  keyword()
) :: {:ok, [t()]} | {:error, term()}

Fetch transfer logs from chain and parse into structs.

Parameters

  • filter - Filter map for eth_get_logs (e.g. %{from_block: 18_000_000, to_block: 18_000_100, address: "0x..."}) (value)
  • opts - Options: :rpc_url, :timeout (default: [], value)

Returns

Parsed transfer structs from matching logs ({:ok, [t()]} | {:error, term})

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout",
      kind: :value
    },
    filter: %{
      description: "Filter map for eth_get_logs (e.g. %{from_block: 18_000_000, to_block: 18_000_100, address: \"0x...\"})",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, [t()]} | {:error, term}",
    description: "Parsed transfer structs from matching logs"
  }
}

fetch!(filter, opts \\ [])

@spec fetch!(
  map(),
  keyword()
) :: [t()]

Fetch transfer logs from chain and parse into structs. Raises on error.

Parameters

  • filter - Filter map (see fetch/2) (value)
  • opts - Options: :rpc_url, :timeout (default: [], value)

Returns

Parsed transfer structs ([t()])

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout",
      kind: :value
    },
    filter: %{description: "Filter map (see fetch/2)", kind: :value}
  },
  returns: %{type: "[t()]", description: "Parsed transfer structs"}
}

parse_log(log)

@spec parse_log(map()) :: {:ok, t()} | {:ok, [t()]} | {:error, term()}

Parse a single raw log map into Transfer struct(s).

Parameters

  • log - Raw log map with :topics, :data, :address, :block_number, :transaction_hash, :log_index (value)

Returns

Single struct for ERC-20/721/1155-Single, list for 1155-Batch ({:ok, t()} | {:ok, [t()]} | {:error, term})

# descripex:contract
%{
  params: %{
    log: %{
      description: "Raw log map with :topics, :data, :address, :block_number, :transaction_hash, :log_index",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, t()} | {:ok, [t()]} | {:error, term}",
    description: "Single struct for ERC-20/721/1155-Single, list for 1155-Batch"
  }
}

parse_log!(log)

@spec parse_log!(map()) :: t() | [t()]

Parse a single raw log map into Transfer struct(s). Raises on error.

Parameters

  • log - Raw log map (see parse_log/1) (value)

Returns

Transfer struct(s) (t() | [t()])

# descripex:contract
%{
  params: %{log: %{description: "Raw log map (see parse_log/1)", kind: :value}},
  returns: %{type: "t() | [t()]", description: "Transfer struct(s)"}
}

parse_logs(logs)

@spec parse_logs([map()]) :: {:ok, [t()]}

Parse a list of raw logs, skipping non-Transfer events.

Parameters

  • logs - List of raw log maps from eth_get_logs (value)

Returns

Flat list of Transfer structs (batches expanded, non-transfers skipped) ({:ok, [t()]})

# descripex:contract
%{
  params: %{
    logs: %{description: "List of raw log maps from eth_get_logs", kind: :value}
  },
  returns: %{
    type: "{:ok, [t()]}",
    description: "Flat list of Transfer structs (batches expanded, non-transfers skipped)"
  }
}

parse_logs!(logs)

@spec parse_logs!([map()]) :: [t()]

Parse a list of raw logs, skipping non-Transfer events. Raises on error.

Parameters

  • logs - List of raw log maps (value)

Returns

Flat list of Transfer structs ([t()])

# descripex:contract
%{
  params: %{logs: %{description: "List of raw log maps", kind: :value}},
  returns: %{type: "[t()]", description: "Flat list of Transfer structs"}
}

transfer_topics()

@spec transfer_topics() :: [String.t()]

Returns the 3 topic0 hashes for ERC-20/721/1155 Transfer events.

Returns

List of 0x-prefixed keccak256 hashes: [Transfer, TransferSingle, TransferBatch] ([String.t()])

# descripex:contract
%{
  returns: %{
    type: "[String.t()]",
    description: "List of 0x-prefixed keccak256 hashes: [Transfer, TransferSingle, TransferBatch]"
  }
}