> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goldsky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ERC-1155 transfers

> Create a table containing ERC-1155 transfers for several, or all token contracts.

This guide streams ERC-1155 transfers into your own database, including batch transfers expanded to one row per token, so single and batch movements land in the same table.

This guide is part of a series of tutorials on how you can stream transfer data into your data warehouse with Turbo pipelines. Here we focus on ERC-1155 transfers; see the other guides for other transfer types:

* [Native transfers](/turbo-pipelines/guides/token-transfers/native-transfers)
* [ERC-20 transfers](/turbo-pipelines/guides/token-transfers/ERC-20-transfers)
* [ERC-721 transfers](/turbo-pipelines/guides/token-transfers/ERC-721-transfers)

## What you'll need

1. A Goldsky account and the CLI installed

<Accordion title="Install Goldsky's CLI and log in">
  1) Install the Goldsky CLI:

     **For macOS/Linux:**

     ```shell theme={"dark"}
     curl https://goldsky.com | sh
     ```

     **For Windows:**

     ```shell theme={"dark"}
     npm install -g @goldskycom/cli
     ```

     <Note>Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.</Note>
  2) Log into your Project by running:
     ```shell theme={"dark"}
     goldsky login
     ```
     This opens your browser to sign in (Google, GitHub, SSO, or email). Once you authenticate, the CLI is logged in automatically — there's no API key to copy or paste.
     <Note>On a headless or remote machine (or in CI), create an API key on your [Project Settings](https://app.goldsky.com/dashboard/settings) page and pass it directly with `goldsky login --token <API_KEY>`. Use `goldsky login --no-browser` to print the login URL instead of opening a browser.</Note>
  3) Now that you are logged in, run `goldsky` to get started:
     ```shell theme={"dark"}
     goldsky
     ```
</Accordion>

2. A basic understanding of [Turbo pipelines](/turbo-pipelines/introduction)
3. A destination sink to write your data to. In this example, we will use [the PostgreSQL sink](/turbo-pipelines/sinks/postgres)

## Introduction

There are two ways to get ERC-1155 transfers flowing:

1. Use the readily available ERC-1155 dataset for the chain you are interested in: this is the easiest and quickest method to get you streaming token transfers into your sink of choice with minimum code.
2. Build the ERC-1155 transfers pipeline from scratch using raw logs: this method takes more code and time to implement, but it's a great way to learn how the decoding functions work in case you want to build more customized pipelines.

Let's explore both methods in more detail.

## Using the ERC-1155 transfers source dataset

Every EVM chain has its own ERC-1155 dataset available for you to use as a source in your pipelines. You can check this by running the `goldsky dataset list` command and finding the EVM chain of your choice.
For this example, let's use the `apex` chain and create a simple pipeline definition that writes its ERC-1155 dataset into a PostgreSQL instance:

```yaml apex-erc1155-transfers.yaml expandable theme={"dark"}
name: apex-erc1155-pipeline
resource_size: s

sources:
  apex_erc1155_transfers:
    type: dataset
    dataset_name: apex.erc1155_transfers
    version: 1.3.0
    start_at: earliest

sinks:
  postgres_apex_erc1155_transfers:
    type: postgres
    from: apex_erc1155_transfers
    schema: public
    table: apex_erc1155_transfers
    secret_name: <YOUR_SECRET>
    primary_key: id
```

<Note>
  If you copy and use this configuration file, make sure to update:

  1. Your `secret_name`. If you already [created a secret](/platform/secrets), you can find it via the CLI command `goldsky secret list`.
  2. The schema and table you want the data written to; by default it writes to `public.apex_erc1155_transfers`.
</Note>

<Warning>
  If you use ClickHouse as the sink for this dataset, add a `schema_override` to avoid data precision errors for big numbers:

  ```yaml theme={"dark"}
    clickhouse_apex_erc1155_transfers:
      type: clickhouse
      from: apex_erc1155_transfers
      table: apex_erc1155_transfers
      secret_name: <YOUR_CLICKHOUSE_SECRET>
      schema_override:
        amount: UInt256
        token_id: UInt256
  ```
</Warning>

Deploy the pipeline by running:

```bash theme={"dark"}
goldsky turbo apply apex-erc1155-transfers.yaml
```

That's it! You should soon start seeing ERC-1155 token transfers in your database.

## Building ERC-1155 transfers from scratch using logs

The ERC-1155 dataset we used as a source in the previous method encapsulates all the decoding logic explained in this section.
Read on if you are interested in learning how it's implemented, in case you want to extend or modify this logic yourself.

To build the token transfers pipeline from scratch, use the `raw_logs` dataset for the chain in combination with the [decoding functions](/turbo-pipelines/reference/sql-functions) and the ABI of a specific ERC-1155 contract.

In this example, we will stream all the transfer events of all the ERC-1155 tokens on the [Scroll chain](https://scroll.io/). We dynamically fetch the ABI of the [Rubyscore\_Scroll](https://scrollscan.com/token/0xdc3d8318fbaec2de49281843f5bba22e78338146) token from the Scrollscan API and use it to decode the same events for every ERC-1155 token on the chain. Any other ERC-1155-compliant token's ABI would also work.

ERC-1155 combines the features of ERC-20 and ERC-721 contracts and adds a few of its own. Each transfer has both a token ID and a value representing the quantity being transferred. For tokens intended to represent NFTs the value is `1`, but this depends on how the contract is implemented.

ERC-1155 also introduces new event signatures for transfers: `TransferSingle(address,address,address,uint256,uint256)` and `TransferBatch(address,address,address,uint256[],uint256[])`, which lets a contract transfer multiple tokens at once to a single recipient.
That batching causes us some trouble, because we want one row per transfer in the database. To keep the SQL simple, we handle single and batch transfers in separate transforms: a SQL transform for `TransferSingle`, and a [TypeScript transform](/turbo-pipelines/transforms/typescript) that expands each `TransferBatch` event into one row per token. A final SQL transform then combines both streams.

### Pipeline definition

```yaml scroll-erc1155-transfers.yaml expandable theme={"dark"}
name: scroll-erc1155-transfers
resource_size: s

sources:
  my_scroll_mainnet_raw_logs:
    type: dataset
    dataset_name: scroll_mainnet.raw_logs
    version: 1.0.0
    start_at: earliest

transforms:
  # Fetch the ABI from Scrollscan and decode matching ERC-1155 transfer logs
  scroll_decoded:
    type: sql
    primary_key: id
    sql: |
      SELECT
        *,
        _gs_log_decode(
          _gs_fetch_abi('https://api.scrollscan.com/api?module=contract&action=getabi&address=0xdc3d8318fbaec2de49281843f5bba22e78338146&apikey=YOUR_KEY', 'etherscan'),
          topics,
          data
        ) AS decoded
      FROM my_scroll_mainnet_raw_logs
      WHERE topics LIKE '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62%'
         OR topics LIKE '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb%'

  # Unnest the values from the decoded struct, drop rows the decoder couldn't handle
  scroll_clean:
    type: sql
    primary_key: id
    sql: |
      SELECT
        *,
        decoded.event_params AS event_params,
        decoded.event_signature AS event_name
      FROM scroll_decoded
      WHERE decoded IS NOT NULL

  # TransferSingle(operator, from, to, id, value): one row per event
  erc1155_transfer_single:
    type: sql
    primary_key: id
    sql: |
      SELECT
        id,
        lower(address) AS contract_address,
        lower(event_params[2]) AS sender,
        lower(event_params[3]) AS recipient,
        event_params[4] AS token_id,
        event_params[5] AS amount,
        block_number,
        block_hash,
        log_index,
        transaction_hash,
        transaction_index
      FROM scroll_clean
      WHERE topics LIKE '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62%'

  # TransferBatch(operator, from, to, ids[], values[]): expand to one row per token
  erc1155_transfer_batch:
    type: script
    primary_key: id
    language: typescript
    from: scroll_clean
    schema:
      id: string
      contract_address: string
      sender: string
      recipient: string
      token_id: string
      amount: string
      block_number: uint64
      block_hash: string
      log_index: uint64
      transaction_hash: string
      transaction_index: uint64
    script: |
      function invoke(data: any) {
        // Only expand TransferBatch events; TransferSingle is handled in SQL
        if (!data.topics || !data.topics.startsWith('0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb')) {
          return [];
        }
        // event_params (1-indexed in SQL) arrives 0-indexed here:
        // [0] operator, [1] from, [2] to, [3] ids, [4] values.
        // Array-typed params are strings like "[1 2 3]" — split them out.
        const parse = (raw: string) =>
          raw.replace(/[\[\]]/g, ' ').split(/[\s,]+/).filter((s: string) => s.length > 0);
        const tokenIds = parse(data.event_params[3]);
        const amounts = parse(data.event_params[4]);

        const rows = [];
        for (let i = 0; i < tokenIds.length; i++) {
          // Skip zero-amount entries so the union only carries real transfers
          if (amounts[i] === '0') continue;
          rows.push({
            // One batch event covers many tokens: suffix the index to keep ids unique
            id: data.id + '_' + i,
            contract_address: data.address.toLowerCase(),
            sender: data.event_params[1].toLowerCase(),
            recipient: data.event_params[2].toLowerCase(),
            token_id: tokenIds[i],
            amount: amounts[i],
            block_number: data.block_number,
            block_hash: data.block_hash,
            log_index: data.log_index,
            transaction_hash: data.transaction_hash,
            transaction_index: data.transaction_index,
          });
        }
        return rows;
      }

  # Combine single and batch transfers into one stream
  scroll_1155_transfers:
    type: sql
    primary_key: id
    sql: |
      SELECT * FROM erc1155_transfer_single
      UNION ALL
      SELECT * FROM erc1155_transfer_batch

sinks:
  scroll_1155_sink:
    type: postgres
    from: scroll_1155_transfers
    schema: public
    table: erc1155_transfers
    secret_name: <YOUR_SECRET>
    primary_key: id
```

<Note>
  If you copy and use this configuration file, make sure to update:

  1. Your `secret_name`. If you already [created a secret](/platform/secrets), you can find it via the CLI command `goldsky secret list`.
  2. The schema and table you want the data written to; by default it writes to `public.erc1155_transfers`.
  3. `token_id` and `amount` are kept as strings because ERC-1155 values can be as large as an unsigned 256-bit integer. Cast them in a downstream transform, or use your sink's `schema_override`, if your database has a suitable numeric type; see [256-bit integers in the Postgres sink](/turbo-pipelines/sinks/postgres#256-bit-integers-u256--i256).
</Note>

Let's walk through the transforms.

### Decoding transforms

```sql Transform: scroll_decoded theme={"dark"}
SELECT
  *,
  _gs_log_decode(
    _gs_fetch_abi('https://api.scrollscan.com/api?module=contract&action=getabi&address=0xdc3d8318fbaec2de49281843f5bba22e78338146&apikey=YOUR_KEY', 'etherscan'),
    topics,
    data
  ) AS decoded
FROM my_scroll_mainnet_raw_logs
WHERE topics LIKE '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62%'
   OR topics LIKE '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb%'
```

As explained in the [decoding contract events guide](/turbo-pipelines/guides/decoding-contract-events), we use `_gs_fetch_abi` to get the ABI from Scrollscan and pass it as the first argument to `_gs_log_decode`, which decodes each log's topics and data into a `decoded` struct. Filtering on the two transfer topics up front keeps the decoder from doing wasted work on unrelated logs: the first topic hash is `TransferSingle(address,address,address,uint256,uint256)`, the second is `TransferBatch(address,address,address,uint256[],uint256[])`.

```sql Transform: scroll_clean theme={"dark"}
SELECT
  *,
  decoded.event_params AS event_params,
  decoded.event_signature AS event_name
FROM scroll_decoded
WHERE decoded IS NOT NULL
```

In this second transform, we pull `event_params` and `event_signature` out of the decode result, and filter on `decoded IS NOT NULL` to leave out logs the decoder couldn't match against the ABI.

### Single transfers

```sql Transform: erc1155_transfer_single theme={"dark"}
SELECT
  id,
  lower(address) AS contract_address,
  lower(event_params[2]) AS sender,
  lower(event_params[3]) AS recipient,
  event_params[4] AS token_id,
  event_params[5] AS amount,
  ...
FROM scroll_clean
WHERE topics LIKE '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62%'
```

`TransferSingle` has the parameters `(operator, from, to, id, value)`, and `event_params` is 1-indexed in SQL, so the sender is `event_params[2]`, the recipient `event_params[3]`, the token ID `event_params[4]`, and the amount `event_params[5]`. Note that the indexes differ from the [ERC-721 guide](/turbo-pipelines/guides/token-transfers/ERC-721-transfers) because the event signature is different.

### Batch transfers

For `TransferBatch`, parameters 4 and 5 are *arrays* of token IDs and amounts, decoded into strings like `[1 2 3]`. We want one database row per token, so the `erc1155_transfer_batch` transform is a [TypeScript transform](/turbo-pipelines/transforms/typescript) that parses both arrays and returns an array of row objects; returning an array from `invoke` expands one input row into many output rows. It also:

* suffixes the row index onto the event `id` (one batch event produces many rows, and the sink's `primary_key` must stay unique), and
* skips zero-amount entries, matching the behavior of the curated datasets.

### Combining single and batch transfers

```sql Transform: scroll_1155_transfers theme={"dark"}
SELECT * FROM erc1155_transfer_single
UNION ALL
SELECT * FROM erc1155_transfer_batch
```

The final transform unions both streams into a single table for the sink.

### Deploying the pipeline

Deploy the pipeline and start sinking ERC-1155 transfer data into your database:

```bash theme={"dark"}
goldsky turbo apply scroll-erc1155-transfers.yaml
```

After some time, you should see transfer data streaming into your sink. Here's an example transfer record:

| id                      | contract\_address                          | sender            | recipient                                  | token\_id | amount | block\_number |
| ----------------------- | ------------------------------------------ | ----------------- | ------------------------------------------ | --------- | ------ | ------------- |
| log\_0x360fcd...7c25\_7 | 0x7de37842bcf314c83afe83a8dab87f85ca3a2cee | 0x00000000...0000 | 0x16f6aff7a2d84b802b2ddf0f0aed49033b69f4f9 | 6         | 1      | 105651        |

<Note>
  You can speed up a backfill by increasing the pipeline's `resource_size` in the YAML and re-applying it with `goldsky turbo apply`; see [operating pipelines](/turbo-pipelines/guides/operating-pipelines).
</Note>

## Conclusion

In this guide, we learned how Turbo simplifies streaming ERC-1155 transfer events into your database.

We first looked at the easy path: using the chain's readily available ERC-1155 dataset as the pipeline source. We then went deep on building the same stream from raw logs: decoding with `_gs_fetch_abi` and `_gs_log_decode`, handling `TransferSingle` in SQL, and expanding `TransferBatch` events into per-token rows with a TypeScript transform.

Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.


## Related topics

- [Curated dataset schemas: ERC-20, ERC-721, ERC-1155](/turbo-pipelines/reference/schema/curated-schemas.md)
- [ERC-721 transfers](/turbo-pipelines/guides/token-transfers/ERC-721-transfers.md)
- [ERC-20 transfers](/turbo-pipelines/guides/token-transfers/ERC-20-transfers.md)
- [Native transfers](/turbo-pipelines/guides/token-transfers/native-transfers.md)
- [Supported networks](/chains/supported-networks.md)
