--path .` For more, read the [step-by-step guide](/subgraphs/deploying-subgraphs).
* Via instant subgraphs, where you can pass through a contract address and the ABI for that contract. This is a quick-start option that automatically generates the underlying subgraph configuration files on your behalf, making it easy to extract blockchain event data and serve it as an API endpoint without complex setup. Use the `--from-abi` flag in the command above instead of `--path`. For more, read the [low-code subgraphs guide](/subgraphs/guides/create-a-low-code-subgraph).
Saga EVM Pegasus Mainnet is available at the chain slug `saga-evm`.
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Sanko
Source: https://docs.goldsky.com/chains/sanko
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Scroll
Source: https://docs.goldsky.com/chains/scroll
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Sei
Source: https://docs.goldsky.com/chains/sei
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Settlus
Source: https://docs.goldsky.com/chains/settlus
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Shape Network
Source: https://docs.goldsky.com/chains/shape
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Shrapnel
Source: https://docs.goldsky.com/chains/shrapnel
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# SKALE
Source: https://docs.goldsky.com/chains/skale-calypso
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# SNAXchain
Source: https://docs.goldsky.com/chains/snaxchain
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Solana
Source: https://docs.goldsky.com/chains/solana
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Somnia
Source: https://docs.goldsky.com/chains/somnia
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Soneium
Source: https://docs.goldsky.com/chains/soneium
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Sonic
Source: https://docs.goldsky.com/chains/sonic
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Sophon
Source: https://docs.goldsky.com/chains/sophon
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Stable
Source: https://docs.goldsky.com/chains/stable
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Starknet
Source: https://docs.goldsky.com/chains/starknet
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Stellar
Source: https://docs.goldsky.com/chains/stellar
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Turbo
MAINNET SUPPORTED
TESTNET SUPPORTED
Turbo pipelines provide high-performance streaming data pipelines with sub-second latency. Deploy a pipeline to start streaming Stellar data to your preferred destination.
### Quick config
```yaml stellar-transactions.yaml theme={null}
name: my-stellar-transactions
resource_size: s
sources:
stellar_transactions:
type: dataset
dataset_name: stellar_mainnet.transactions
version: 1.2.0
start_at: latest
sinks:
my_sink:
type: postgres
from: stellar_transactions
schema: public
table: stellar_transactions
secret_name: MY_POSTGRES_SECRET
primary_key: transaction_hash
```
Deploy with:
```bash theme={null}
goldsky turbo apply stellar-transactions.yaml
```
### Available chain slugs
Mainnet: `stellar_mainnet` | Testnet: `stellar_testnet`
Goldsky provides real-time (under 5 seconds) streaming of Stellar datasets, including all historical data, for both mainnet and testnet. Datasets include ledgers, transactions, operations, events, transfers, ledger entries, and balances.
The Stellar testnet is frequently reset (typically about once every third month). We recommend deploying testnet pipelines with `start_at: latest` to ensure they always index from the most recent testnet version.
For available datasets, schemas, version details, and pipeline examples, see the [Stellar Turbo Sources](/turbo-pipelines/sources/stellar) guide.
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Sui
Source: https://docs.goldsky.com/chains/sui
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
### Working with Sui datasets
Mirror pipelines allow users to replicate data into their own infrastructure (any of the [supported sinks](/mirror/sinks/supported-sinks)) in real time. The supported data sources are the following direct indexing datasets: checkpoints, packages, transactions, epochs and events.
For a complete overview of how to deploy Mirror pipelines, including a video walkthrough, check the [Create a Pipeline](/mirror/create-a-pipeline). Below, we will look at a few of the different ways by which you can deploy Mirror pipelines.
Remember to first create a [Secret](/mirror/manage-secrets) in order for Mirror to be able to write the data into the database of your choice.
Pipelines can be deployed on Goldsky in 3 ways:
* **Using [Goldsky Flow](/mirror/create-a-pipeline#goldsky-flow) in the dashboard:**
* Drop a `Data Source` card. Select `Sui` as the chain. Then `Enriched Transactions` as the onchain dataset to use.
* Add a `Sink` card and select your pre-configured sink.
* Deploy pipeline.
* **Using the [interactive CLI](/mirror/create-a-pipeline#creating-mirror-pipelines-with-the-cli):**
* Enter command `goldsky pipeline create `.
* This will kick off a guided flow with the first step to choose the dataset type. Choose `Direct Indexing`.
* Next, select `Sui` as chain with the enter key, then `Raw Transactions` as the dataset by pressing space on the selected dataset which you can select using the up and down arrow keys. We will process historical data so select `Process all historical data` by pressing the enter key. This is the same as the yaml config setting of `start_at: earliest`. If you prefer to only ingest data starting when your pipeline is deployed, select `Process data from the time this pipeline is created` instead, this is equivalent to the yaml config setting `start_at: latest`.
* When asked to choose another source, choose `No` by pressing enter.
* Add your pre-configured sink. In this example case, we chose PostgreSQL and as a next step we selected the database schema.
* Pipeline will be deployed automatically.
* **Using a [pipeline configuration file](/mirror/create-a-pipeline#non-interactive-pipeline-configuration):**
* This makes it easier to set up complex pipelines involving multiple sources, multiple sinks, and more complex, SQL-based transformations. For the full reference documentation on, click [here](/mirror/reference/config-file/pipeline).
* As in the previous steps, we'll be deploying a pipeline to stream Sui transactions. Unlike the other methods, we have added a transformation to only select specific data attributes from the whole dataset. This is the configuration file:
```yaml sui-transactions.yaml theme={null}
apiVersion: 3
name: sui-raw-transactions
sources:
sui_transactions:
dataset_name: sui.transactions
version: 1.0.0
type: dataset
transforms:
subset_transform:
primary_key: id
sql: |
SELECT
id,
transaction_digest,
transaction_kind,
effects_json,
transaction_json
FROM sui_transactions
sinks:
postgres_sui_transactions:
type: postgres
table: sui_transactions
schema: public
secret_name:
description: 'Postgres sink for: sui_transactions'
from: subset_transform
```
Add your corresponding secret name and run `goldsky pipeline apply sui-transactions.yaml --status ACTIVE` to deploy the pipeline.
## Turbo
## Edge
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Superseed
Source: https://docs.goldsky.com/chains/superseed
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Supported networks
Source: https://docs.goldsky.com/chains/supported-networks
## Subgraphs
Goldsky currently supports the following chains on Subgraphs.
| Name | Type | Slug |
| ---------------------------- | ------- | ------------------------------- |
| 0G | Mainnet | `0g` |
| 0G Galileo Testnet | Testnet | `0g-galileo-testnet` |
| 5ire | Mainnet | `5ire` |
| Abstract | Mainnet | `abstract` |
| Abstract Testnet | Testnet | `abstract-testnet` |
| Aleph Zero | Mainnet | `alephzero-evm` |
| Aleph Zero | Testnet | `alephzero-testnet` |
| Anomaly Andromeda | Testnet | `anomaly-andromeda-testnet` |
| Apechain | Mainnet | `apechain-mainnet` |
| Apechain Curtis | Testnet | `apechain-curtis` |
| Arbitrum One | Mainnet | `arbitrum-one` |
| Arbitrum Nova | Mainnet | `arbitrum-nova` |
| Arbitrum Blueberry | Testnet | `arb-blueberry` |
| Arbitrum Sepolia | Testnet | `arbitrum-sepolia` |
| Arc Testnet | Testnet | `arc-testnet` |
| Astar | Mainnet | `astar` |
| Astar Zkyoto | Testnet | `astar-zkyoto` |
| Astar zkEvm | Mainnet | `astar-zkevm` |
| Avalanche | Mainnet | `avalanche` |
| Avalanche Testnet | Testnet | `avalanche-testnet` |
| Base | Mainnet | `base` |
| Base Sepolia | Testnet | `base-sepolia` |
| Basecamp | Testnet | `basecamp` |
| Berachain Bepolia | Testnet | `berachain-bepolia` |
| Berachain Mainnet | Mainnet | `berachain-mainnet` |
| Blast | Mainnet | `blast` |
| Blast Sepolia | Testnet | `blast-sepolia` |
| BNB Chain | Mainnet | `bsc` |
| BNB Chain Chapel | Testnet | `chapel` |
| Block Chain Testnet | Testnet | `block-chain-testnet` |
| Block Chain Testkek | Testnet | `block-chain-testkek` |
| Boba Mainnet | Mainnet | `boba-eth` |
| Boba BNB | Mainnet | `boba-bnb` |
| Boba BNB Testnet | Testnet | `boba-bnb-testnet` |
| Boba Sepolia | Testnet | `boba-sepolia` |
| Build on Bitcoin | Mainnet | `bob` |
| Build on Bitcoin Sepolia | Testnet | `bob-sepolia` |
| BTTC | Mainnet | `bttc-mainnet` |
| Camp | Mainnet | `camp` |
| Camp Testnet V2 | Testnet | `camp-testnet-v2` |
| Celo | Mainnet | `celo` |
| Cheesy | Testnet | `cheesy-testnet` |
| Chiliz | Mainnet | `chiliz` |
| Citrea Devnet | Devnet | `citrea-devnet` |
| Citrea Testnet Tangerine | Testnet | `citrea-testnet-tangerine` |
| Citrea | Mainnet | `citrea` |
| Conflux eSpace | Mainnet | `conflux-espace` |
| Connext Sepolia | Testnet | `connext-sepolia` |
| Corn Maizenet | Mainnet | `corn-maizenet` |
| Corn Testnet | Testnet | `corn-testnet` |
| Cronos zkEVM | Mainnet | `cronos-zkevm` |
| Cronos zkEVM Sepolia | Sepolia | `cronos-zkevm-sepolia` |
| Curio Devnet | Devnet | `curio-devnet` |
| Cyber | Mainnet | `cyber` |
| DATA Network | Mainnet | `data_mainnet` |
| DATA Network Aeneid | Testnet | `data_testnet` |
| Degen L3 | Mainnet | `degen` |
| Edgeless | Mainnet | `edgeless` |
| Edgeless Testnet | Testnet | `edgeless-testnet` |
| EDU Chain | Mainnet | `edu-chain` |
| Embr | Mainnet | `embr-mainnet` |
| Embr Testnet | Testnet | `embr-anvil` |
| Ethereal | Mainnet | `ethereal-mainnet` |
| Ethereal Testnet | Testnet | `ethereal-testnet` |
| Ethereum | Mainnet | `mainnet` |
| Ethereum Holesky | Testnet | `holesky` |
| Ethereum Sepolia | Testnet | `sepolia` |
| Etherlink | Mainnet | `etherlink` |
| Etherlink Shadownet | Testnet | `etherlink-shadownet` |
| Everclear | Mainnet | `everclear` |
| Fantom | Mainnet | `fantom` |
| Fantom Testnet | Testnet | `fantom-testnet` |
| Filecoin | Mainnet | `filecoin` |
| Filecoin Testnet | Testnet | `filecoin-testnet` |
| Flare | Mainnet | `flare` |
| Flare Coston2 | Testnet | `flare-coston2` |
| Fluent Devnet | Devnet | `fluent-public-devnet` |
| Fluent Testnet | Testnet | `fluent-testnet` |
| Fraxtal | Testnet | `frax-testnet` |
| Fraxtal | Mainnet | `frax-mainnet` |
| Gensyn | Mainnet | `gensyn-mainnet` |
| Gensyn Testnet | Testnet | `gensyn-testnet` |
| Gnosis | Mainnet | `xdai` |
| Gnosis Chiado | Testnet | `chiado` |
| Gravity | Mainnet | `gravity` |
| Hedera | Mainnet | `hedera-mainnet` |
| Horizen | Mainnet | `horizen` |
| Horizen Testnet | Testnet | `horizen-testnet` |
| HyperEVM | Mainnet | `hyperevm` |
| HyperEVM Testnet | Testnet | `hyperevm-testnet` |
| Immutable zkEVM | Mainnet | `imtbl-zkevm` |
| Immutable zkEVM Testnet | Testnet | `imtbl-zkevm-testnet` |
| Incentiv Testnet | Testnet | `incentiv-testnet` |
| Injective | Mainnet | `inevm-mainnet` |
| Ink | Mainnet | `ink` |
| Ink Sepolia | Testnet | `ink-sepolia` |
| IOTA EVM Mainnet | Mainnet | `iota` |
| IOTA EVM Testnet | Testnet | `iota-testnet` |
| Kava | Mainnet | `kava` |
| Kava Testnet | Testnet | `kava-testnet` |
| Kaia | Mainnet | `kaia` |
| Kaia Kairos | Testnet | `kaia-kairos` |
| KiteAI | Mainnet | `kite-ai` |
| KiteAI Testnet | Testnet | `kite-ai-testnet` |
| Kroma | Mainnet | `kroma` |
| L3X | Mainnet | `l3x` |
| L3X Testnet | Testnet | `l3x-testnet` |
| LayerEdge Testnet | Testnet | `layeredge-testnet` |
| Linea | Mainnet | `linea` |
| Linea Sepolia | Testnet | `linea-sepolia` |
| Lisk | Mainnet | `lisk` |
| Lisk Sepolia | Testnet | `lisk-sepolia-testnet` |
| LitVM Liteforge Testnet | Testnet | `liteforge` |
| Lumia | Mainnet | `lumia` |
| Lumia Beam | Testnet | `lumia-beam` |
| Lumibit Testnet | Testnet | `lumibit-testnet` |
| Merlin | Mainnet | `merlin` |
| Manta Pacific | Mainnet | `manta-pacific-mainnet` |
| Manta Pacific Sepolia | Testnet | `manta-pacific-sepolia` |
| Mantle | Mainnet | `mantle` |
| Mantle Sepolia | Testnet | `mantle-sepolia` |
| Mantra | Mainnet | `mantra` |
| Mantra Dukong | Testnet | `mantra-dukong` |
| Meowchain Testnet | Testnet | `meowchain-testnet` |
| MegaETH | Mainnet | `megaeth-mainnet` |
| MegaETH Testnet | Testnet | `megaeth-testnet-v2` |
| Metis | Mainnet | `metis` |
| Mezo | Mainnet | `mezo` |
| Mezo Testnet | Testnet | `mezo-testnet` |
| Mint | Mainnet | `mint` |
| Mint Sepolia Testnet | Testnet | `mint-sepolia-testnet` |
| Moca Testnet | Testnet | `mocachain-testnet` |
| Mitosis Badnet | Testnet | `mitosis-badnet` |
| Mitosis Mainnet | Mainnet | `mitosis-mainnet` |
| Mode | Mainnet | `mode-mainnet` |
| Mode Testnet | Testnet | `mode-testnet` |
| Monad | Mainnet | `monad` |
| Monad Testnet | Testnet | `monad-testnet` |
| Moonbase | Mainnet | `mbase` |
| Moonbeam | Mainnet | `moonbeam` |
| Moonriver | Mainnet | `moonriver` |
| Morph | Mainnet | `morph` |
| Morph Holesky Testnet | Testnet | `morph-testnet` |
| Morph Hoodi Testnet | Testnet | `morph-hoodi` |
| Nebula Testnet | Testnet | `nebula-testnet` |
| Neura Testnet | Testnet | `neura-testnet` |
| Oasis Emerald | Mainnet | `oasis-emerald-mainnet` |
| Oasis Emerald Testnet | Testnet | `oasis-emerald-testnet` |
| Oasis Sapphire | Mainnet | `oasis-sapphire-mainnet` |
| Oasis Sapphire Testnet | Testnet | `oasis-sapphire-testnet` |
| Open Campus Codex | Testnet | `open-campus-codex` |
| Optimism | Mainnet | `optimism` |
| Optimism Sepolia | Testnet | `optimism-sepolia` |
| Optimism BNB | Mainnet | `opbnb` |
| Optimism BNB Testnet | Testnet | `opbnb-testnet` |
| OP Celestia Raspberry | Testnet | `opcelestia-raspberry` |
| Perennial | Mainnet | `perennial` |
| Perennial Testnet | Testnet | `perennial-testnet-op-base-tia` |
| Pharos | Mainnet | `pharos` |
| Pharos Atlantic Testnet | Testnet | `pharos-atlantic-testnet` |
| Plasma | Testnet | `plasma-testnet` |
| Plasma | Mainnet | `plasma-mainnet` |
| Plume | Mainnet | `plume-mainnet` |
| Plume Testnet | Testnet | `plume-sepolia` |
| Polygon | Mainnet | `matic` |
| Polygon Amoy | Testnet | `polygon-amoy` |
| Polygon Blackberry | Testnet | `polygon-blackberry` |
| Polygon zkEVM | Mainnet | `polygon-zkevm` |
| Public Goods Network | Mainnet | `publicgoods` |
| Public Goods Network Testnet | Testnet | `publicgoods-testnet` |
| PulseChain | Mainnet | `pulsechain` |
| RARI Chain | Testnet | `rari` |
| Redbelly | Mainnet | `redbelly` |
| Reya | Mainnet | `reya` |
| Reya Cronos | Testnet | `reya-cronos` |
| Rise Sepolia | Testnet | `rise-sepolia` |
| Robinhood Chain | Mainnet | `robinhood-mainnet` |
| Robinhood Chain Testnet | Testnet | `robinhood-testnet` |
| Ronin | Mainnet | `ronin` |
| Rootstock | Mainnet | `rootstock` |
| Ruby Testnet | Testnet | `ruby-testnet` |
| Saga EVM | Mainnet | `saga-evm` |
| Sanko Testnet | Testnet | `sanko-testnet` |
| Scroll | Mainnet | `scroll` |
| Scroll Sepolia | Testnet | `scroll-sepolia` |
| Sei | Mainnet | `sei` |
| Sei Testnet | Testnet | `sei-testnet` |
| Shape | Mainnet | `shape` |
| Shape Sepolia | Testnet | `shape-sepolia` |
| Shimmer | Mainnet | `shimmer` |
| Shimmer Testnet | Testnet | `shimmer-testnet` |
| Skale Calypso | Mainnet | `skale-calypso` |
| Skale Calypso Testnet | Testnet | `skale-calypso-testnet` |
| Skale Europa | Mainnet | `skale-europa` |
| Skale Europa Testnet | Testnet | `skale-europa-testnet` |
| Skale Nebula Testnet | Testnet | `skale-nebula-testnet` |
| SNFT | Testnet | `SNFT` |
| Sonic | Mainnet | `sonic` |
| Sonic Blaze Testnet | Testnet | `sonic-blaze-testnet` |
| Soneium Minato | Testnet | `soneium-minato` |
| Sophon | Mainnet | `sophon` |
| Sophon Testnet | Testnet | `sophon-testnet` |
| Sovrun Devnet | Devnet | `sovrun-devnet` |
| Sovrun Caldera | Testnet | `sovrun-caldera` |
| Sovrun Testnet | Testnet | `sovrun-testnet` |
| Spheron Devnet | Devnet | `spheron-devnet` |
| Stable | Mainnet | `stable` |
| Stable Testnet | Testnet | `stable-testnet` |
| Superseed | Mainnet | `superseed` |
| Superseed Sepolia | Testnet | `superseed-sepolia` |
| Swellchain | Mainnet | `swell` |
| Swellchain Testnet | Testnet | `swell-testnet` |
| TAC | Mainnet | `tac` |
| TAC Saint Petersburg | Testnet | `tac-spb` |
| TAC Turin | Testnet | `tac-turin` |
| Taiko | Mainnet | `taiko` |
| Taiko Hoodi Testnet | Testnet | `taiko-hoodi-testnet` |
| Tangible Unreal | Testnet | `tangible-unreal` |
| Tangible Real | Testnet | `real` |
| Telos | Mainnet | `telos` |
| Telos Testnet | Testnet | `telos-testnet` |
| Tempo | Mainnet | `tempo` |
| Tempo Andantino | Testnet | `tempo-andantino` |
| Tenet | Mainnet | `tenet` |
| Thundercore | Mainnet | `thundercore` |
| Thundercore Testnet | Testnet | `thundercore-testnet` |
| Unichain | Mainnet | `unichain` |
| Unichain Sepolia | Testnet | `unichain-testnet` |
| Vana | Mainnet | `vana` |
| Vana Moksha | Testnet | `moksha` |
| Verify | Testnet | `verify-testnet` |
| Viction | Mainnet | `viction` |
| Viction Testnet | Testnet | `viction-testnet` |
| Volmex Testnet | Testnet | `volmex-testnet` |
| World Chain | Mainnet | `worldchain` |
| World Chain Sepolia | Testnet | `worldchain-sepolia` |
| X Layer | Mainnet | `x1` |
| Xai | Mainnet | `xai` |
| Xai Sepolia | Testnet | `xai-sepolia` |
| XDC | Mainnet | `xdc` |
| XRPL EVM | Mainnet | `xrpl-evm` |
| XRPL EVM Testnet | Testnet | `xrplevm-testnet` |
| ZetaChain | Mainnet | `zetachain-mainnet` |
| ZetaChain Testnet | Testnet | `zetachain-testnet` |
| zkLink Nova | Mainnet | `zklink-nova` |
| zkLink Nova Testnet | Testnet | `zklink-nova-testnet` |
| zkSync Era | Mainnet | `zksync-era` |
| zkSync Era Sepolia | Testnet | `zksync-era-sepolia` |
| Zero | Mainnet | `zero` |
| Zero | Testnet | `zero-sepolia` |
| Zircuit | Mainnet | `zircuit` |
| Zircuit Garfield Testnet | Testnet | `zircuit-garfield-testnet` |
| Zora | Mainnet | `zora` |
| Zora Sepolia | Testnet | `zora-sepolia` |
If you want to work with another chain, please contact us at [sales@goldsky.com](mailto:sales@goldsky.com?subject=Supported%20Chains) to discuss your use case.
## Mirror
Goldsky currently supports the following chains on Mirror.
### EVM chains
For EVM chains we support the following 4 datasets:
| Dataset | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Blocks | Metadata for each block on the chain including hashes, transaction count, difficulty, and gas used. |
| Logs | Raw logs for events emitted from contracts. Contains the contract address, data, topics, and metadata for blocks and transactions. |
| Enriched Transactions | Transaction data including input, value, from and to address, and metadata for the block, gas, and receipts. |
| Traces | Traces of all function calls made on the chain including metadata for block, trace, transaction, and gas. |
### Fast Scan
Some datasets have support for [Fast Scan](/mirror/sources/direct-indexing#backfill-vs-fast-scan) which allows you to more quickly backfill filtered data. If a chain has partial support for Fast Scan, the dataset that doesn't support fast scan will have an asterisk `*` next to it.
Here's a breakdown of the EVM chains we support and their corresponding datasets:
| | Blocks | Enriched Transactions | Logs | Traces | Fast Scan |
| --------------------------- | ------ | --------------------- | ---- | ------ | --------- |
| 0G | ✓ | ✓ | ✓ | ✗ | ✗ |
| 0G Galileo Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Abstract | ✓ | ✓ | ✓ | ✗ | ✓ |
| Align Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| Apechain | ✓ | ✓ | ✓ | ✗ | ✗ |
| Apechain Curtis | ✓ | ✓ | ✓ | ✗ | ✓ |
| Arc Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Proof of Play Apex | ✓ | ✓ | ✓ | ✗ | ✓ |
| Arbitrum Nova | ✓ | ✓ | ✓ | ✗ | ✓ |
| Arbitrum One | ✓ | ✓ | ✓ | ✓ | ✓ |
| Arbitrum Sepolia | ✓ | ✓ | ✓ | ✗ | ✓ |
| Arena-Z | ✓ | ✓ | ✓ | ✓ | ✗ |
| Arena-Z Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Arweave \* | ✓ | ✓ | N/A | N/A | ✗ |
| Automata | ✓ | ✓ | ✓ | ✓ | ✗ |
| Automata Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Avalanche | ✓ | ✓ | ✓ | ✓ | ✓ |
| B3 | ✓ | ✓ | ✓ | ✓ | ✗ |
| B3 Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Base | ✓ | ✓ | ✓ | ✓ | ✓ |
| Base Sepolia | ✓ | ✓ | ✓ | ✓ | ✓ |
| Berachain Bepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Berachain Mainnet | ✓\* | ✓ | ✓ | ✓\* | ✓ |
| Bitcoin | ✓ | ✓ | ✗ | ✗ | ✗ |
| Blast | ✓ | ✓ | ✓ | ✓ | ✓ |
| Build on Bitcoin | ✓ | ✓\* | ✓ | ✓ | ✓ |
| Binance Smart Chain | ✓ | ✓ | ✓ | ✓ | ✓ |
| Camp Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| Celo | ✓ | ✓ | ✓ | ✓ | ✓ |
| Celo Dango Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Codex | ✓ | ✓ | ✓ | ✓ | ✗ |
| Conflux eSpace | ✓ | ✓ | ✓ | ✗ | ✓ |
| Corn Maizenet | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Cronos zkEVM | ✓ | ✓ | ✓ | ✗ | ✗ |
| Cronos zkEVM Sepolia | ✓ | ✓ | ✓ | ✗ | ✗ |
| Cyber | ✓ | ✓ | ✓ | ✓ | ✓ |
| Cyber Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| DATA Network | ✓ | ✓ | ✓ | ✓ | ✓ |
| DATA Network Aeneid Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| Degen | ✓ | ✓ | ✓ | ✓ | ✗ |
| Ethena Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Ethereum | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ethereum Holesky | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ethereum Sepolia | ✓ | ✓ | ✓ | ✓ | ✓ |
| Etherlink | ✓ | ✓ | ✓ | ✓ | ✗ |
| Etherlink Shadownet | ✓ | ✗ | ✗ | ✗ | ✗ |
| Ethernity | ✓ | ✓ | ✓ | ✓ | ✗ |
| Ethernity Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Fantom | ✓ | ✓ | ✓ | ✓ | ✓ |
| Flare | ✓ | ✓ | ✓ | ✗ | ✓ |
| Flare Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Fluent Devnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Forma | ✓ | ✓ | ✓ | ✓ | ✓ |
| Frax | ✓ | ✓ | ✓ | ✓ | ✓ |
| Gensyn | ✓ | ✓ | ✓ | ✗ | ✗ |
| Gensyn Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Gnosis | ✓ | ✓ | ✓ | ✓ | ✓ |
| Gravity | ✓ | ✓ | ✓ | ✓ | ✗ |
| Ham | ✓ | ✓ | ✓ | ✓ | ✗ |
| HashKey | ✓ | ✓ | ✓ | ✗ | ✗ |
| HyperEVM | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Immutable Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Immutable zkEVM | ✓ | ✓ | ✓ | ✗ | ✗ |
| Ink | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Ink Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| IOTA EVM | ✓ | ✓ | ✓ | ✓ | ✗ |
| Kaia | ✓ | ✓ | ✓ | ✗ | ✗ |
| Kroma | ✓ | ✓ | ✓ | ✓ | ✗ |
| Linea | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Lisk | ✓ | ✓ | ✓ | ✓ | ✓ |
| Lisk Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Lith Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| LitVM Liteforge Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Lyra | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Lyra Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| MegaETH | ✓ | ✓ | ✓ | ✓\* | ✓ |
| MegaETH Testnet | ✓ | ✓ | ✓ | ✗ | ✓ |
| Metal | ✓ | ✓ | ✓ | ✓ | ✗ |
| Metal Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Mezo | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mezo Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Midnight Devnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Mint | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mint Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Moca Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Mode | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mode Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| Monad | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Monad Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Morph | ✓ | ✓ | ✓ | ✗ | ✓ |
| Neura Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Oasys Homeverse | ✓ | ✓ | ✓ | ✗ | ✓ |
| Optimism | ✓ | ✓ | ✓ | ✓ | ✓ |
| Optimism Sepolia | ✓ | ✓ | ✓ | ✓ | ✓ |
| Orderly | ✓ | ✓ | ✓ | ✓ | ✗ |
| Orderly Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Plasma | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Plasma Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Plume | ✓ | ✓ | ✓ | ✗ | ✗ |
| Pharos | ✓ | ✓ | ✓ | ✓ | ✓ |
| Pharos Testnet | ✓ | ✓ | ✓ | ✓ | ✓ |
| Polygon | ✓ | ✓\* | ✓ | ✓ | ✓ |
| Polynomial | ✓ | ✓ | ✓ | ✓ | ✗ |
| Proof of Play Barret | ✓ | ✓ | ✓ | ✓ | ✗ |
| Proof of Play Boss | ✓ | ✓ | ✓ | ✓ | ✗ |
| Proof of Play Cloud | ✓ | ✓ | ✓ | ✓ | ✗ |
| Public Good Network | ✓ | ✓ | ✓ | ✓ | ✓ |
| Race | ✓ | ✓ | ✓ | ✓ | ✗ |
| Rari | ✓ | ✓ | ✓ | ✓ | ✓ |
| Redstone | ✓ | ✓ | ✓ | ✓ | ✓ |
| Reya | ✓ | ✓ | ✓ | ✗ | ✓ |
| Rise Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Robinhood Chain | ✓ | ✓ | ✓ | ✓ | ✓ |
| Robinhood Chain Testnet | ✓ | ✓ | ✓ | ✓ | ✗ |
| Rootstock | ✓ | ✓ | ✓ | ✗ | ✓ |
| Ruby Testnet | ✓ | ✓ | ✓ | ✗ | ✓ |
| Scroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| Scroll Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
| Sei | ✓ | ✓ | ✓ | ✓\* | ✓ |
| Settlus | ✓ | ✓ | ✓ | ✓ | ✗ |
| Shape | ✓ | ✓ | ✓ | ✓ | ✓ |
| Shape Sepolia | ✓ | ✓ | ✓ | ✓ | ✓ |
| Shrapnel | ✓ | ✓ | ✓ | ✗ | ✓ |
| SNAXchain | ✓ | ✓ | ✓ | ✓ | ✗ |
| Soneium | ✓ | ✓ | ✓ | ✗ | ✗ |
| Soneium Minato | ✓ | ✓ | ✓ | ✗ | ✗ |
| Sonic | ✓ | ✓ | ✓ | ✗ | ✗ |
| Sophon | ✓ | ✓ | ✓ | ✓ | ✗ |
| Sophon Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Stable | ✓ | ✓ | ✓ | ✗ | ✓ |
| Superseed | ✓ | ✓ | ✓ | ✓ | ✗ |
| Superseed Sepolia | ✓ | ✓ | ✓ | ✓ | ✓ |
| Swan | ✓ | ✓ | ✓ | ✓ | ✗ |
| Swellchain | ✓ | ✓ | ✓ | ✗ | ✗ |
| Swellchain Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| TAC | ✓ | ✓ | ✓ | ✗ | ✗ |
| TAC Turin Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Taiko Hoodi Testnet | ✓ | ✓ | ✓ | ✗ | ✗ |
| Tempo | ✓ | ✓ | ✓ | ✗ | ✗ |
| Tempo Andantino | ✓ | ✓ | ✓ | ✗ | ✗ |
| TRON | ✓ | ✓ | ✓ | ✗ | ✓ |
| Unichain | ✓ | ✓ | ✓ | ✓ | ✓ |
| Unichain Sepolia | ✓ | ✓ | ✓ | ✗ | ✗ |
| Viction | ✓ | ✓ | ✓ | ✗ | ✗ |
| World Chain | ✓\* | ✓\* | ✓ | ✓\* | ✓ |
| X Layer | ✓ | ✓ | ✓ | ✗ | ✓ |
| XPLA | ✓ | ✓ | ✓ | ✗ | ✗ |
| XR Sepolia | ✓ | ✓ | ✓ | ✗ | ✗ |
| Xterio | ✓ | ✓ | ✓ | ✓ | ✗ |
| Zero | ✓ | ✓ | ✓ | ✓ | ✗ |
| Zero Sepolia | ✓ | ✓ | ✓ | ✗ | ✗ |
| Zetachain | ✓ | ✓ | ✓ | ✓ | ✓ |
| zkSync Era | ✓ | ✓ | ✓ | ✓ | ✓ |
| Zora | ✓ | ✓\* | ✓ | ✓ | ✓ |
| Zora Sepolia | ✓ | ✓ | ✓ | ✓ | ✗ |
\* The Arweave dataset includes bundled/L2 data.
### Non-EVM chains
#### Beacon
| Dataset | Description |
| ------------------------------------------ | ----------------------------------------------------------------------------------- |
| Attestations | Attestations (votes) from validators for the block. |
| Attester Slashing | Metadata for attester slashing. |
| Blocks | Metadata for each block on the chain including hashes, deposit count, and gas used. |
| BLS Signature to Execution Address Changes | BLS Signature to Execution Address Changes. |
| Deposits | Metadata for deposits. |
| Proposer Slashing | Metadata for proposer slashing. |
| Voluntary Exits | Metadata for voluntary exits. |
| Withdrawls | Metadata for withdrawls. |
#### Fogo
| Dataset | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------- |
| Transactions with Instructions | Enriched transaction data including instructions, accounts, balance changes, and metadata for the block. |
| Rewards | Records of rewards distributed to validators for securing and validating the network. |
| Blocks | Metadata for each block on the chain including hashes, transaction count, slot and leader rewards. |
#### IOTA
| Dataset | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Checkpoints | A checkpoint is a periodic, finalized snapshot of the blockchain's state in the Movement VM, batching transactions to ensure consistency and scalability across the network. |
| Epochs | An epoch is a defined time period in the Movement VM during which a fixed set of validators processes transactions and manages governance, with transitions enabling validator rotation and network updates. |
| Events | Events in the Movement VM are structured data emissions from smart contracts, recorded on the blockchain to log significant actions or state changes for external monitoring and interaction. |
| Move Calls | Move calls are a function invocation within a Move smart contract, executed by the Movement VM to perform specific operations or state transitions on the blockchain. |
| Transactions | A transaction in the Movement VM is a signed instruction executed by the Move smart contract to modify the blockchain's state, such as transferring assets or invoking contract functions. |
#### Movement
| Dataset | Description |
| --------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Account Transactions | All raw onchain transactions involving account-level actions (e.g., transaction version, account address). |
| Block Metadata Transactions | Metadata about blocks and block-level transactions (e.g., block height, epoch, version). |
| Fungible Asset Balances | Real-time balances of fungible tokens across accounts. |
| Current Token Data | Latest metadata for tokens - includes name, description, supply, etc. |
| Current Token Ownerships | Snapshot of token ownership across the chain. |
| Events | All emitted contract event logs - useful for indexing arbitrary contract behavior. |
| Fungible Asset Activities | Track activity for fungible tokens - owner address, amount, and type. |
| Fungible Asset Balances | Historical balance tracking for fungible assets (not just the current state). |
| Fungible Asset Metadata | Static metadata for fungible tokens - like decimals, symbol, and name. |
| Signatures | Cryptographic signature data from transactions, useful for validating sender authenticity. |
| Token Activities | Detailed logs of token movements and interactions across tokens and NFTs. |
#### NEAR
NEAR datasets are no longer available through Mirror. Use [Turbo pipelines](/turbo-pipelines/sources/near) for NEAR data.
#### Solana
Solana datasets are no longer available through Mirror. Use [Turbo pipelines](/turbo-pipelines/sources/solana) for Solana data.
#### Starknet
| Dataset | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------ |
| Blocks | Metadata for each block on the chain including hashes, transaction count, difficulty, and gas used. |
| Events | Consists of raw event data from the blockchain, documenting various on-chain activities and triggers. |
| Messages | Messaging data from the Starknet blockchain, used for L2 & L1 communication. |
| Transactions | Transaction data including input, value, from and to address, and metadata for the block, gas, and receipts. |
#### Stellar
Stellar datasets are no longer available through Mirror. Use [Turbo pipelines](/turbo-pipelines/sources/stellar) for Stellar data.
#### Sui
| Dataset | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Checkpoints | Contains raw data of blockchain checkpoints capturing the state of the ledger at specific intervals. |
| Epochs | Includes raw data detailing the various epochs in the blockchain, which mark significant periods or phases in the network's operation |
| Events | Consists of raw event data from the blockchain, documenting various on-chain activities and triggers |
| Packages | Contains raw data about the deployed smart contract packages on the blockchain |
| Transactions | Transaction data including effects, events, senders, recipients, balance and object changes, and other metadata. |
### Curated Datasets
Beyond onchain datasets, the Goldsky team continuosly curates and publishes derived datasets that serve a specific audience or use case. Here's the list:
#### Token Transfers
You can expect every EVM chain to have the following datasets available:
| Dataset | Description |
| --------- | ------------------------------------------------- |
| ERC\_20 | Every transfer event for all fungible tokens. |
| ERC\_721 | Every transfer event for all non-fungible tokens. |
| ERC\_1155 | Every transfer event for all ERC-1155 tokens. |
#### Polymarket datasets
| Dataset | Description |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Order Filled | This event is emitted when a single Polymarket order is partially or completely filled. For example: a 50c YES buy for 100 YES matched against a 50c YES sell for 100 YES will emit 2 Orderi Filled events, from the perspective of the YES buy and of the YES sell. This is useful for granular tracking of trading activity and history. |
| Orders Matched | This event is emitted when a Polymarket taker order is matched against a set of Polymarket maker(limit) orders. For example: a 50c YES buy for 200 YES matched against 2 50c YES sells for 100 YES each will emit a single Orders Matched event. Orders Matched gives a more high level view of trading activity as it only tracks taker activity. |
| User Balances | This event keeps track of all user outcome token positions. |
| User Positions | Keeps track of outcome token positions along with pnl specific data including average price and realized pnl. |
Additional chains, including roll-ups, can be indexed on demand. Contact us at [sales@goldsky.com](mailto:sales@goldsky.com) to learn more.
## Turbo
[Turbo pipelines](/turbo-pipelines/introduction) support every EVM chain listed under [Mirror](#mirror) above, with the same datasets and schemas. In addition, Turbo is the only product that supports these non-EVM sources:
* [Solana](/turbo-pipelines/sources/solana) — full history from genesis
* [Bitcoin](/turbo-pipelines/sources/bitcoin)
* [Stellar](/turbo-pipelines/sources/stellar)
* [NEAR](/turbo-pipelines/sources/near)
For a feature-by-feature comparison with Mirror, see [Mirror vs. Turbo pipelines](/mirror-vs-turbo).
## Edge RPC
Goldsky Edge RPC provides high-performance RPC endpoints for the following networks.
| Name | Type | Chain ID |
| ----------------------- | ------- | ---------- |
| Abstract | Mainnet | `2741` |
| Arbitrum One | Mainnet | `42161` |
| Arbitrum Sepolia | Testnet | `421614` |
| Avalanche | Mainnet | `43114` |
| Base | Mainnet | `8453` |
| Base Sepolia | Testnet | `84532` |
| Berachain | Mainnet | `80094` |
| Blast | Mainnet | `81457` |
| Bsc | Mainnet | `56` |
| Corn Maizenet | Mainnet | `21000000` |
| Cyber | Mainnet | `7560` |
| Ethereum | Mainnet | `1` |
| Ethereum Sepolia | Testnet | `11155111` |
| Flare | Mainnet | `14` |
| Fuse Mainnet | Mainnet | `122` |
| Gnosis | Mainnet | `100` |
| Gravity | Mainnet | `1625` |
| HyperEVM | Mainnet | `999` |
| HyperEVM Testnet | Testnet | `998` |
| Kaia Mainnet | Mainnet | `8217` |
| Kava Mainnet | Mainnet | `2222` |
| Linea | Mainnet | `59144` |
| Mantle Mainnet | Mainnet | `5000` |
| Mantle Sepolia | Testnet | `5003` |
| Monad | Mainnet | `143` |
| Monad Testnet | Testnet | `10143` |
| Moonbeam Mainnet | Mainnet | `1284` |
| opBNB Mainnet | Mainnet | `204` |
| Optimism | Mainnet | `10` |
| Optimism Sepolia | Testnet | `11155420` |
| Polygon | Mainnet | `137` |
| Polygon Amoy Testnet | Testnet | `80002` |
| Polygon zkEVM | Mainnet | `1101` |
| Robinhood Chain Testnet | Testnet | `46630` |
| Robinhood Mainnet | Mainnet | `4663` |
| Scroll Mainnet | Mainnet | `534352` |
| Sei | Mainnet | `1329` |
| Somnia Testnet | Testnet | `50312` |
| Sonic | Mainnet | `146` |
| Swell | Mainnet | `1923` |
| TAC | Mainnet | `239` |
| Tempo Mainnet | Mainnet | `4217` |
| Tempo Moderato Testnet | Testnet | `42431` |
| Unichain | Mainnet | `130` |
| Unichain Testnet | Testnet | `1301` |
| zkSync | Mainnet | `324` |
| Zora | Mainnet | `7777777` |
## Compose
Goldsky Compose runs TypeScript tasks against the chains below via Goldsky Edge RPC. The **Gas sponsored** column indicates whether one of compose's bundler providers (Alchemy, Pimlico, or Gelato) covers that chain — when ✓, smart-wallet tasks can sponsor user gas; when ✗, tasks must use a funded EOA.
| Name | Type | Chain ID | Gas sponsored |
| ---------------- | ------- | ---------- | ------------- |
| Abstract | Mainnet | `2741` | ✗ |
| Arbitrum One | Mainnet | `42161` | ✓ |
| Arbitrum Sepolia | Testnet | `421614` | ✓ |
| Avalanche | Mainnet | `43114` | ✓ |
| Base | Mainnet | `8453` | ✓ |
| Base Sepolia | Testnet | `84532` | ✓ |
| Berachain | Mainnet | `80094` | ✓ |
| Blast | Mainnet | `81457` | ✓ |
| Bsc | Mainnet | `56` | ✓ |
| Cyber | Mainnet | `7560` | ✓ |
| Ethereum | Mainnet | `1` | ✓ |
| Flare | Mainnet | `14` | ✗ |
| Fuse Mainnet | Mainnet | `122` | ✓ |
| Gnosis | Mainnet | `100` | ✓ |
| Gravity | Mainnet | `1625` | ✗ |
| HyperEVM | Mainnet | `999` | ✓ |
| HyperEVM Testnet | Testnet | `998` | ✓ |
| Kaia Mainnet | Mainnet | `8217` | ✓ |
| Kava Mainnet | Mainnet | `2222` | ✗ |
| Linea | Mainnet | `59144` | ✓ |
| Monad | Mainnet | `143` | ✓ |
| Monad Testnet | Testnet | `10143` | ✓ |
| Moonbeam Mainnet | Mainnet | `1284` | ✓ |
| opBNB Mainnet | Mainnet | `204` | ✓ |
| Optimism | Mainnet | `10` | ✓ |
| Optimism Sepolia | Testnet | `11155420` | ✓ |
| Polygon | Mainnet | `137` | ✓ |
| Polygon zkEVM | Mainnet | `1101` | ✓ |
| Scroll Mainnet | Mainnet | `534352` | ✓ |
| Sei | Mainnet | `1329` | ✓ |
| Somnia Testnet | Testnet | `50312` | ✓ |
| Sonic | Mainnet | `146` | ✓ |
| DATA Network | Mainnet | `1514` | ✓ |
| Swell | Mainnet | `1923` | ✗ |
| TAC | Mainnet | `239` | ✗ |
| Tempo Mainnet | Mainnet | `4217` | ✓ |
| Unichain | Mainnet | `130` | ✓ |
| Unichain Testnet | Testnet | `1301` | ✓ |
| zkSync | Mainnet | `324` | ✗ |
| Zora | Mainnet | `7777777` | ✓ |
# Swan Chain
Source: https://docs.goldsky.com/chains/swan
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Swellchain
Source: https://docs.goldsky.com/chains/swellchain
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# TAC
Source: https://docs.goldsky.com/chains/tac
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Taiko
Source: https://docs.goldsky.com/chains/taiko
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# The Binary Holdings
Source: https://docs.goldsky.com/chains/tbh
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Telos EVM
Source: https://docs.goldsky.com/chains/telos-evm
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Tempo
Source: https://docs.goldsky.com/chains/tempo
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Tenet
Source: https://docs.goldsky.com/chains/tenet
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# ThunderCore
Source: https://docs.goldsky.com/chains/thundercore
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# TRON
Source: https://docs.goldsky.com/chains/tron
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Unichain
Source: https://docs.goldsky.com/chains/unichain
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Vana
Source: https://docs.goldsky.com/chains/vana
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Viction
Source: https://docs.goldsky.com/chains/viction
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Volmex Finance
Source: https://docs.goldsky.com/chains/volmex
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# World Chain
Source: https://docs.goldsky.com/chains/worldchain
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# XDC Network
Source: https://docs.goldsky.com/chains/xdc
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# X Layer
Source: https://docs.goldsky.com/chains/xlayer
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# XPLA
Source: https://docs.goldsky.com/chains/xpla
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# XRADERS
Source: https://docs.goldsky.com/chains/xr
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# XRPL EVM Sidechain
Source: https://docs.goldsky.com/chains/xrpl-evm
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Xterio
Source: https://docs.goldsky.com/chains/xterio
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Zero
Source: https://docs.goldsky.com/chains/zero
## Overview
### Partnership
Goldsky has partnered with to make our product available to the ecosystem and provide dedicated support for . Below in the overview of each product, the "Partner Sponsored" tag indicates that usage of that product is fully covered by the chain, if approved by the team. Where this perk is available, please reach out to the developer relations team for an access code to the private signup form.
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# ZetaChain
Source: https://docs.goldsky.com/chains/zetachain
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Zircuit
Source: https://docs.goldsky.com/chains/zircuit
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# zkLink Nova
Source: https://docs.goldsky.com/chains/zklink-nova
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# zkSync Era
Source: https://docs.goldsky.com/chains/zksync-era
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# Zora
Source: https://docs.goldsky.com/chains/zora
## Overview
## Getting started
To use Goldsky, you'll need to create an account, install the CLI, and log in. If you want to use Turbo or Compose, you'll also need to install their respective CLI extensions.
1. Install the Goldsky CLI:
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
2. Go to your [Project Settings](https://app.goldsky.com/dashboard/settings) page and create an API key.
3. Back in your Goldsky CLI, log into your Project by running the command `goldsky login` and paste your API key.
4. Now that you are logged in, run `goldsky` to get started:
```shell theme={null}
goldsky
```
If you already have the Goldsky CLI installed, install the Turbo extension by running:
```bash theme={null}
goldsky turbo
```
This will automatically install the Turbo extension. Verify the installation:
```bash theme={null}
goldsky turbo list
```
Make sure to update the CLI to the latest version before running Turbo commands: `curl https://goldsky.com | sh`
For a complete reference of all Turbo CLI commands, see the [CLI Reference](/turbo-pipelines/cli-reference) guide.
If you already have the Goldsky CLI installed, install the Compose extension by running:
```bash theme={null}
goldsky compose install
```
To update to the latest version:
```bash theme={null}
goldsky compose update
```
For more details, see the [Compose quickstart](/compose/quick-start) guide.
## Subgraphs
## Mirror
## Turbo
## Edge RPC
## Compose
## Getting support
Can't find what you're looking for? Reach out to us at [support@goldsky.com](mailto:support@goldsky.com) for help.
# App Configuration
Source: https://docs.goldsky.com/compose/app-configuration
Configure your Compose app manifest including app properties, tasks, secrets, environment variables, and runtime release channels.
The Compose App manifest is a YAML file that defines your app's configuration. This is where you configure a list of tasks and other app-level concerns.
Each task can be individually configured for things like retry behavior and HTTP trigger behavior, cron schedules and more.
## App properties
| Property | Type | Required | Description |
| ------------- | -------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Yes | Unique app identifier used for API calls. Must start and end with a letter or number, and contain only letters, numbers, underscores, and hyphens. |
| `api_version` | string | For deploy | Pins your app to a Compose runtime version. Required by `goldsky compose deploy`; optional for local dev. Use `"stable"` if unsure — see [Release channels](#release-channels) below. |
| `secrets` | array(string) | No | Names of secrets you've set for this app (see [Secrets](./secrets)) |
| `env` | [ENV](#env-variables) | No | Environment-specific variables |
| `tasks` | [Tasks\[\]](#task-configuration) | Yes | List of tasks in your app (must contain at least one task) |
### Release channels
`api_version` pins your app to a Compose runtime version. You can use one of the release channels, or pin to a specific version:
* `"stable"` — the current stable runtime. This is the recommended value for production apps.
* `"preview"` — the latest pre-release runtime. Use this if you want early access to features that haven't made it into `stable` yet. Expect more frequent changes.
* A specific semver version (e.g. `"0.3.0"`) — pin to an exact runtime version. Useful when you want full control over when your app picks up runtime changes. Do not include a `v` prefix. See the [changelog](https://goldsky.com/changelog) for available versions.
If your manifest's `api_version` is older than the CLI, `goldsky compose deploy` will warn you and prompt before continuing. Pass `-f` to skip the prompt.
## Basic example
```yaml theme={null}
name: "my-app"
api_version: "stable"
secrets:
- MY_SECRET
env:
local:
MY_VAR: "foo"
cloud:
MY_VAR: "bar"
tasks:
- name: "price_fetcher"
path: "./tasks/fetch_price.ts"
triggers:
- type: "cron"
expression: "* * * * *" # Run every minute
- type: "http"
authentication: "auth_token"
retry_config:
max_attempts: 3
initial_interval_ms: 1000
backoff_factor: 2
- name: "data_processor"
path: "./tasks/process_data.ts"
```
## ENV variables
You can set env variables in your manifest, which are injected into your task's [env context property](./context/env). Env configuration is namespaced by environment, and only the values for the current environment are injected. Currently there are two environments: `local`, used when running your compose app locally, and `cloud`, used when your app is running after `goldsky compose deploy` — see the [deploy guide](./deploy-monitor) for more.
Values must be strings. For anything sensitive (API keys, private keys), use [secrets](./secrets) instead.
### Example
```yaml theme={null}
name: "my-app"
env:
local:
MY_VAR: "foo"
ANOTHER_VAR: "bar"
cloud:
MY_VAR: "foo-prod"
ANOTHER_VAR: "bar-prod"
```
## Task configuration
Compose apps are made up of tasks, each task in the `tasks` array defines an executable unit of work and references a typescript file, see [Task Authoring](./tasks) for
more details. Tasks can be triggered by HTTP requests from your app,
blockchain events, cron schedules, etc. See more about triggers below. Tasks can also trigger other tasks via the [callTask context function](./context/call-task)
within the task code.
```yaml theme={null}
name: "my-app"
api_version: "stable"
tasks:
- name: "unique_task_name" # Required: Unique identifier
path: "./tasks/my_task.ts" # Required: Path to task file
triggers:
- type: "http"
authentication: "auth_token"
- type: "cron"
expression: "*/5 * * * *" # Every 5 minutes
retry_config: # Optional: Task-level retry settings
max_attempts: 5
initial_interval_ms: 2000
backoff_factor: 1.5
```
### Name validation
**App names** must start and end with a letter or number, and contain only letters, numbers, underscores, and hyphens (e.g. `my-app-1`). The pattern is `/^[a-zA-Z0-9]([a-zA-Z0-9_\-]*[a-zA-Z0-9])?$/`.
**Task names** must start with a letter or number, and contain only letters, numbers, underscores, hyphens, and dots (e.g. `fetch_prices`, `oracle.update`). The pattern is `/^[a-zA-Z0-9][a-zA-Z0-9_.\-]*$/`. A leading underscore is no longer allowed.
Names that canonicalize the same (lowercased, with runs of `-`/`_` collapsed to `-`) cannot coexist in a project. `my-app`, `My_App`, and `my__app` are treated as the same name, so the second deploy gets a conflict error.
### Task properties
| Property | Type | Required | Description |
| -------------- | ------------- | -------- | ----------------------------------------------------------------------------------------- |
| `name` | string | Yes | Unique task identifier used for API calls |
| `path` | string | Yes | File path to the task module |
| `triggers` | array(object) | No | List of triggers that will run the task. Each trigger type can only appear once per task. |
| `retry_config` | object | No | Task-level retry behavior |
### Triggers
Triggers are what runs your Compose tasks, these can be HTTP calls, onchain events, or cron jobs. For more information, see [Triggers](./task-triggers).
#### Example
```yaml theme={null}
triggers:
- type: "cron"
expression: "* * * * *" # Run every minute (standard cron expression)
- type: "http"
authentication: "auth_token"
```
If you provide no triggers in your manifest then tasks can only be executed by other tasks.
### Retry configuration
```yaml theme={null}
retry_config:
max_attempts: 3 # Maximum number of retry attempts
initial_interval_ms: 1000 # Initial delay before first retry
backoff_factor: 2 # Exponential backoff multiplier
```
When specifying `retry_config`, all three fields (`max_attempts`, `initial_interval_ms`, `backoff_factor`) are required. You cannot provide only some of them.
**How retries work:**
* If a task fails, Compose waits `initial_interval_ms` before retrying
* Each subsequent retry interval is multiplied by `backoff_factor`
* Example with above config: 1000ms → 2000ms → 4000ms
* After `max_attempts` failures, the task is marked as permanently failed
* When `retry_config` is omitted, tasks default to `max_attempts: 3`, `initial_interval_ms: 1000`, and `backoff_factor: 2`.
## Manifest examples
### Multi-chain oracle
```yaml theme={null}
name: "multi-chain-oracle"
api_version: "stable"
tasks:
- name: "fetch_prices"
path: "./tasks/fetch_crypto_prices.ts"
retry_config:
max_attempts: 5
initial_interval_ms: 2000
backoff_factor: 1.5
- name: "update_ethereum_oracle"
path: "./tasks/update_oracle.ts"
triggers:
- type: "cron"
expression: "* * * * *" # Every minute
retry_config:
max_attempts: 3
initial_interval_ms: 5000
backoff_factor: 2
- name: "update_polygon_oracle"
path: "./tasks/update_oracle.ts"
triggers:
- type: "cron"
expression: "* * * * *" # Every minute
```
### Event processing pipeline
```yaml theme={null}
name: "event-processor"
api_version: "stable"
tasks:
- name: "event_processing"
path: "./tasks/process_events.ts"
triggers:
- type: "onchain_event"
network: "base"
contract: "0xb74de3F91e04d0920ff26Ac28956272E8d67404D"
events:
- "Transfer(address,address,uint256)"
retry_config:
max_attempts: 10
initial_interval_ms: 1000
backoff_factor: 1.2
```
# App Lifecycle
Source: https://docs.goldsky.com/compose/app-lifecycle
Inspect, pause, resume, delete, and tail logs from a deployed Compose app using the Goldsky CLI.
Once your Compose app is deployed, a set of CLI commands let you inspect, pause, resume, delete, tail logs, and review its deploy history, task runs, collections, and deployed source. The per-app commands (`status`, `pause`, `resume`, `logs`, `delete`, `history`, `runs`, `collections`, `source`, `download`) accept either `-n, --name ` to target by name, or `-m, --manifest ` (default `compose.yaml`) to read the name from your manifest. `list` is project-wide and accepts neither.
Every command also accepts `--json` so you can pipe output into scripts or agents.
## Check status
```bash theme={null}
goldsky compose status
```
Shows the app's name, current runtime status (e.g. `RUNNING`, `PAUSED`, `STARTING`, `ERROR`), and timestamps for when it was created and last updated.
```bash theme={null}
# status for a specific app, as JSON
goldsky compose status -n my-app --json
```
## List apps
```bash theme={null}
goldsky compose list
```
Prints a table of every Compose app in your project.
```bash theme={null}
# list as JSON for scripting
goldsky compose list --json
```
## Pause and resume
```bash theme={null}
goldsky compose pause
goldsky compose resume
```
While paused, cron triggers stop firing and HTTP triggers return an error. State is preserved — `resume` picks up where the app left off. This is useful for:
* Triaging a misbehaving app without deleting it
* Holding execution while you roll out a dependency change
* Temporarily stopping a cron while you investigate its effects
## View logs
```bash theme={null}
goldsky compose logs
```
By default `logs` prints the most recent 100 lines. You can tail live, filter by level or text, and bound the output by time or line count.
```bash theme={null}
# tail live
goldsky compose logs -f
# live, errors and warnings only
goldsky compose logs -f --level error,warn
# last hour
goldsky compose logs --since 1h
# search for a substring across the last 500 lines
goldsky compose logs --tail 500 --search "timeout"
# tail live for 5 minutes, then exit
goldsky compose logs -f --timeout 5m
```
Options cheat-sheet:
| Flag | Description |
| ---------------------- | --------------------------------------------------- |
| `-f, --follow` | Stream logs live |
| `--tail ` | Number of lines to fetch (default: `100`) |
| `--level ` | Comma-separated levels (e.g. `error,warn`) |
| `--search ` | Filter lines by text |
| `--since ` | Logs since a relative time (e.g. `1h`, `30m`, `7d`) |
| `--max-lines ` | Exit after N lines |
| `--timeout ` | Exit after a duration (e.g. `5m`) |
| `--json` | Emit newline-delimited JSON |
## Inspect deploy history and task runs
Use `goldsky compose history` to see what was deployed and when:
```bash theme={null}
goldsky compose history -n my-app
# include failed deploys in the list
goldsky compose history -n my-app --include-failures
```
When a task run fails, narrow the `runs` list with `--task`, `--status`, or `--since` to find it, then fetch the full run detail by id:
```bash theme={null}
# recent runs of a specific task
goldsky compose runs -n my-app --task price_fetcher --limit 10
# failing runs in the last hour
goldsky compose runs -n my-app --status error --since 1h
# full detail for one run
goldsky compose runs -n my-app
```
Pipe `--json` output into `jq` to drive scripts or alerts:
```bash theme={null}
goldsky compose runs -n my-app --status error --json | jq '.runs[].runId'
```
For all flags, see the [CLI reference](./cli-reference#inspecting-a-deployed-app).
## Query collections
List every collection in your app's hosted database, then query one with a JSON filter:
```bash theme={null}
goldsky compose collections list -n my-app
goldsky compose collections query prices -n my-app --filter '{"symbol": "BTC"}' --limit 10
```
For all flags, see the [CLI reference](./cli-reference#inspecting-a-deployed-app).
## View and download deployed source
Print the deployed source for your app, or for a single task:
```bash theme={null}
# list the deployed files
goldsky compose source -n my-app
# print one task's source
goldsky compose source -n my-app bitcoin_oracle
```
Download a runnable copy of the whole app as a zip:
```bash theme={null}
goldsky compose download -n my-app
```
The same source is available in the dashboard's Code tab, with a Download app button that produces the same archive. See [Deploying and monitoring](./deploy-monitor#viewing-and-downloading-deployed-source) for details.
For all flags, see the [CLI reference](./cli-reference#inspecting-a-deployed-app).
## Delete an app
```bash theme={null}
goldsky compose delete
```
By default `delete` is interactive: it asks you to type the app name to confirm, and separately asks whether you also want to delete the app's hosted Postgres database.
```bash theme={null}
# non-interactive (CI-safe) — skip all prompts
goldsky compose delete -f
# non-interactive, also drop the Postgres database
goldsky compose delete -f --delete-database
```
See [Deploying and monitoring](./deploy-monitor#deleting-a-compose-app) for how database deletion interacts with active pipelines.
Deleting an app is permanent. Deleting its database is permanent too, and cannot be undone. Back up anything you care about first.
## Next Steps
Learn about deploying your app to the cloud.
View the full CLI command reference.
# Compose CLI Reference
Source: https://docs.goldsky.com/compose/cli-reference
Reference for the Compose CLI manifest schema, commands, and configuration options.
## Manifest
The Compose App's manifest is a YAML file with the following schema. The manifest holds all the relevant information about the Compose App and its tasks.
All compose commands will reference your manifest and pick up the configuration there. See full manifest configuration docs [here](./app-configuration).
### Example
```yaml theme={null}
name: "my_app"
api_version: "stable"
secrets:
- MY_SECRET
env:
local:
MY_VAR: "foo"
cloud:
MY_VAR: "bar"
tasks:
- name: "price_fetcher"
path: "./tasks/fetch_price.ts"
triggers:
- type: "cron"
expression: "* * * * *" # Run every minute
- type: "http"
authentication: "auth_token"
retry_config:
max_attempts: 3
initial_interval_ms: 1000
backoff_factor: 2
- name: "data_processor"
path: "./tasks/process_data.ts"
```
## App targeting
Most commands that operate on a deployed app accept either `-n, --name ` to target by name directly, or `-m, --manifest ` (default `compose.yaml`) to read the name from a manifest. If neither is provided and no `compose.yaml` is present, the command errors. A few commands (`deploy`, `wallet create`, `wallet list`) only support `-m, --manifest`.
## Scripting and automation
Most Compose commands can run non-interactively, which lets you drive deploys and inspections from CI, scripts, and agents.
### Authentication
Every command that talks to the platform needs an auth token. The token is resolved in this order: the `-t, --token ` flag, then the `GOLDSKY_API_TOKEN` environment variable, then the token written by `goldsky login`. If none is present, the command errors:
```text theme={null}
Please run goldsky login, set GOLDSKY_API_TOKEN, or pass --token to the command.
```
### JSON output
Pass `--json` to any command that talks to the platform for machine-readable output: `deploy`, `callTask`, `status`, `list`, `history`, `runs`, `collections list`, `collections query`, `source`, `download`, `pause`, `resume`, `delete`, `logs`, `secret list`, `wallet create`, `wallet list`, `writeContract`, and `deployContract`. Note that `secret set` and `secret delete` do not accept it.
In `--json` mode stdout carries only the result document; progress bars and other decoration are suppressed. Failures go to stderr as a JSON envelope and the command exits with code 1:
```json theme={null}
{ "error": true, "code": "VALIDATION_FAILED", "message": "..." }
```
Error codes include `VALIDATION_FAILED`, `SECRET_MISSING`, `DEPLOY_FAILED`, `ALREADY_DEPLOYED`, `NOT_FOUND`, `WRITE_CONTRACT_FAILED`, and `UNKNOWN`.
### Non-interactive behavior
In a non-TTY (CI, scripts, agents), interactive prompts are skipped or error out:
* `goldsky compose init ` accepts the project name as an argument and skips the prompt. Without a name it errors in non-interactive mode.
* `goldsky compose deploy` aborts on a major `api_version` mismatch unless you pass `--force`. A minor mismatch defaults to continuing.
* `goldsky compose clean` and `goldsky compose delete` require `-f, --force` in non-interactive mode.
### Example: scripted deploy
```bash theme={null}
export GOLDSKY_API_TOKEN=gs_tok_...
goldsky compose deploy --json | jq -r .dashboard_url
```
## Commands
### init \[name]
Prompts you for a project name and scaffolds a folder of that name containing a working Compose app (a Bitcoin price oracle example you can run right away). Pass the name as an argument to skip the prompt; the name is required in non-interactive mode.
```bash theme={null}
goldsky compose init my-app
```
### start
Starts your app locally. Preserves execution state from previous runs, useful for testing retry behavior and other production scenarios. By default the server binds port 4000; if that port is taken it walks up to 4009 and uses the first free port.
```bash theme={null}
goldsky compose start
```
Options:
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `--fork-chains` Fork all chains referenced in contract interactions locally for testing — see [forking](./environments#forking-for-local-compose-development)
* `--impersonate ` Impersonate wallet addresses on the local fork (e.g. `"wallet1=0xAddr1,wallet2=0xAddr2"`). Requires `--fork-chains`. See [Impersonated wallets](./context/evm/wallets#impersonated-wallets)
* `-p, --port ` Bind exactly this port; fails fast if it is already in use (no walking)
### deploy
Deploys the app to the cloud. On success it prints the dashboard URL, which is also available as `dashboard_url` when you pass `--json`. See [monitoring](./deploy-monitor) for more info. In a non-TTY, a major `api_version` mismatch aborts the deploy unless you pass `--force`; see [Scripting and automation](#scripting-and-automation).
```bash theme={null}
goldsky compose deploy
```
Options:
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `-t, --token ` Authentication token for deployment
* `-f, --force` Skip version compatibility prompts
* `--sync-env` Upload secrets from your local `.env` to the cloud before deploying, in one step. See [Secrets](./secrets).
* `--json` Emit the deploy result as JSON (includes `dashboard_url`)
Your manifest must include an `api_version` field to deploy. All secrets referenced in the manifest must be set before deploying (see [Secrets](./secrets)).
### callTask
Call a task by name with a JSON payload. By default the task runs on your deployed app; pass `--env local` (or `-p, --port`) to hit a locally running server instead.
```bash theme={null}
# call a task on the deployed app (default)
goldsky compose callTask my_task '{"foo": "bar"}'
# call a task on a locally running app
goldsky compose callTask my_task '{"foo": "bar"}' --env local
```
Options:
* `--env ` Where to run the task (default: `cloud`). Passing `--port` implies local
* `-p, --port ` Local port to call. Implies `--env local`; an error if combined with an explicit `--env cloud`
* `-n, --name ` App name (alternative to `-m`)
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `-t, --token ` Authentication token
* `--json` Emit the response as JSON
When calling a local server, the port is resolved in this order: the `--port` flag, then the `.compose/.port` file, then 4000.
Both the task name and the JSON payload are required. The payload must be valid JSON: `{foo: "bar"}` is invalid, use `{"foo": "bar"}` (or `'{}'` if the task takes no input) instead.
### codegen
Parse ABIs in the `src/contracts/` folder and generate TypeScript classes for them.
See [contracts](./context/evm/contracts) for more details.
```bash theme={null}
goldsky compose codegen
```
### deployContract
Compile a Solidity contract and deploy it to a chain, with gas sponsored from your app's wallet. The contract is compiled locally (no `forge` or `solc` install required) and deployed through a CREATE2 deterministic deployment proxy, so deploying the same contract from the same app produces the same address on every chain. The deployed address is printed before the transaction is sent. The block it was deployed in is printed on success, and the ABI is written to `src/contracts/.json` so you can run [`codegen`](#codegen) against it.
Imports are resolved from `node_modules/`, so OpenZeppelin and other published contracts work after a normal `npm install`. The wallet this deploys from is the same on-chain identity as `evm.wallet({ name })` in your task code for a given app and wallet name. `deployContract` and `writeContract` both work before your app's first deploy, so you can deploy a contract and use its address before anything is running.
```bash theme={null}
goldsky compose deployContract src/contracts/MyToken.sol \
--chain-id 84532 \
--constructor-args 0xRecipient 1000000
```
Options:
* `--chain-id ` Chain to deploy to, e.g. `8453` for Base or `84532` for Base Sepolia (required)
* `--constructor-args ` Constructor arguments, space-separated (one value per argument)
* `--verify` Verify the contract's source on the chain's block explorer after deploying. Opt-in because publishing source is irreversible and verification adds roughly 30 to 90 seconds
* `--wallet ` Named wallet to deploy from (default: `default`). Shares identity with `evm.wallet({ name })` in task code
* `--force` Deploy even if `msg.sender` usage is detected in the constructor (see note below)
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `-t, --token ` Authentication token
* `--api-server ` Override the API server URL
* `--json` Emit the deploy result as JSON
Because deployment routes through the CREATE2 proxy, `msg.sender` inside your constructor is the proxy address, not your wallet. If your constructor uses `msg.sender` (for example `Ownable(msg.sender)`), pass the address as an explicit constructor argument instead. The CLI warns and stops when it detects this; `--force` bypasses the check.
Constructor and function arguments use forge-style syntax: space-separated, one value per argument (for example `--constructor-args 0xRecipient 1000000`). Arrays are written `[a,b]`, tuples are written `(a,b)`, and negative numbers need a leading space (for example `" -5"`).
Re-deploying the identical contract from the same app fails with `CONTRACT_ALREADY_DEPLOYED` because the CREATE2 address already has code. Passing constructor arguments to a contract that has no constructor is an error. Gas spent by `deployContract` and `writeContract` is billed on your normal Goldsky bill, the same as gas used by your deployed tasks.
### writeContract
Send a gas-sponsored transaction to a contract on any supported chain, using your app's wallet. Encodes calldata from a function signature and arguments, or accepts raw calldata. This is the same wallet identity your task code uses via `evm.wallet()`, so you can use it to trigger events, call admin functions, or make transfers without managing gas or keys. `writeContract` works before your app's first deploy. Gas spent by `writeContract` is billed on your normal Goldsky bill, the same as gas used by your deployed tasks.
```bash theme={null}
goldsky compose writeContract \
--chain-id 84532 \
--to 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
--function "transfer(address,uint256)" \
--args 0xRecipient 1000000
```
Options:
* `--chain-id ` Chain to transact on (required)
* `--to ` Target contract address (required)
* `--function ` Function signature, e.g. `"transfer(address,uint256)"`
* `--args ` Function arguments, space-separated (one value per argument)
* `--data ` Raw calldata hex, as an alternative to `--function` + `--args`
* `--value ` Value to send with the transaction. Accepts plain wei or an `ether`/`gwei` suffix: `1000`, `1ether`, `0.5ether`, `100gwei`
* `--wallet ` Named wallet to send from (default: `default`)
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `-t, --token ` Authentication token
* `--api-server ` Override the API server URL
* `--json` Emit the transaction result as JSON
### clean
Deletes your local stage database (`.compose/stage.db`), wiping all local execution state and collection data. Prompts for confirmation unless `-f` is passed. In a non-TTY it aborts with `Use --force for non-interactive cleanup.` unless `-f` is passed.
```bash theme={null}
goldsky compose clean
```
Options:
* `-f, --force` Skip the confirmation prompt
* `-c, --config ` Path to manifest file (default: `compose.yaml`)
### update \[version]
Update the Compose CLI. With no argument it updates to the latest version; pass a version to install a specific release.
```bash theme={null}
# update to the latest version
goldsky compose update
# install a specific version
goldsky compose update 0.8.1
```
Options:
* `--preview` Install the latest preview build from `main` instead of the current stable release
See [Release channels](./app-configuration#release-channels) for the difference between `stable` and `preview`.
## App lifecycle
For full examples, see [App lifecycle](./app-lifecycle).
### status
Show the status of a deployed app.
```bash theme={null}
goldsky compose status
```
Options:
* `-n, --name ` App name (alternative to `-m`)
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `-t, --token ` Authentication token
* `--json` Emit JSON output (for scripts and agents)
### list
List all deployed apps in your project.
```bash theme={null}
goldsky compose list
```
Options:
* `-t, --token ` Authentication token
* `--json` Emit JSON output
### pause
Pause a deployed app. Cron triggers stop firing and HTTP triggers return an error until the app is resumed.
```bash theme={null}
goldsky compose pause
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
* `--json`
### resume
Resume a paused app.
```bash theme={null}
goldsky compose resume
```
Options: same as `pause`.
### delete
Delete a deployed app. Interactively prompts you to type the app name to confirm, then asks whether to also delete the app's hosted Postgres database (unless `--delete-database` is already set). Pass `--force` to skip both prompts — required for non-interactive use. In non-TTY environments without `--force`, the command errors out.
```bash theme={null}
goldsky compose delete
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
* `-f, --force` Skip confirmation prompts (required for non-interactive use, e.g. CI)
* `--delete-database` Also delete the app's hosted Postgres database
* `--json`
Deleting an app (and especially its database) is permanent. See [Deleting a Compose app](./deploy-monitor#deleting-a-compose-app) for the full behavior including pipeline safety checks.
### logs
View or tail logs from a deployed app.
```bash theme={null}
# show the last 100 log lines
goldsky compose logs
# tail logs live, filtered to errors and warnings
goldsky compose logs -f --level error,warn
# show logs from the last hour
goldsky compose logs --since 1h
# show 500 lines and exit
goldsky compose logs --max-lines 500
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
* `-f, --follow` Stream logs live
* `--tail ` Number of lines to fetch (default: `100`)
* `--level ` Comma-separated log levels (e.g. `error,warn`)
* `--search ` Filter log lines by text
* `--since ` Only show logs since a relative time (e.g. `1h`, `30m`, `7d`)
* `--max-lines ` Exit after N lines (useful with `-f`)
* `--timeout ` Exit after a duration (useful with `-f`)
* `--json` Emit newline-delimited JSON
## Inspecting a deployed app
These commands read back information about a deployed app. They accept the standard targeting flags (`-n, --name` or `-m, --manifest`) and `--json` unless noted.
### history
Show the deploy history for an app.
```bash theme={null}
goldsky compose history
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `--limit ` Number of records to show (default: 20, server caps at 100)
* `--offset ` Number of records to skip (default: 0)
* `--include-failures` Include failed deploys in the list
* `--json` Emit JSON output
When the list is truncated, the output prints the visible range, for example: `Showing 6-7 of 42 records. Use --limit and --offset to page.`
### runs \[runId]
List task runs for an app, or show one run's detail when you pass a run id.
```bash theme={null}
# list recent runs
goldsky compose runs
# show one run's detail
goldsky compose runs 01HV8X2K1QZ9F4N1V3M2P7B6
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `--limit ` Number of runs to show
* `--offset ` Number of runs to skip
* `--task ` Filter to runs of a single task
* `--status ` Filter by run status
* `--since ` Only show runs since a relative time (e.g. `1h`, `30m`, `7d`)
* `--until ` Only show runs before a relative time
* `--json` Emit JSON output
### collections list
List the collection names for an app.
```bash theme={null}
goldsky compose collections list
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `--json` Emit JSON output
### collections query \[collectionName]
Query documents in a collection. The collection name is required.
```bash theme={null}
goldsky compose collections query prices --filter '{"symbol": "BTC"}' --limit 10
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `--filter ` Filter documents by a JSON predicate
* `--limit ` Number of documents to return (default: 100, maximum: 1000)
* `--offset ` Number of documents to skip
* `--json` Emit JSON output
### source \[taskName]
Print the deployed source for an app. With no argument it lists the deployed files; with a task name it prints that task's source. It never writes to disk.
```bash theme={null}
# list deployed files
goldsky compose source
# print one task's source
goldsky compose source price_fetcher
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `--json` Emit JSON output
### download
Download the deployed source archive as `.zip` by default. Use `-o, --output ` to choose a destination; the command refuses to overwrite an existing file. A downloaded app runs locally as-is: `compose start` detects the pre-bundled task files and runs them without re-bundling.
```bash theme={null}
goldsky compose download
goldsky compose download -o ./my-app.zip
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token ` Authentication token
* `-o, --output ` Output path (default: `.zip`)
* `--json` Emit JSON output
## Wallet management
### wallet create
Create a named wallet for this app and print its address. Works before the app's first deploy, so you can create a wallet, fund it, or pass its address as a constructor argument to `deployContract` before anything is running.
```bash theme={null}
# create a cloud wallet (default)
goldsky compose wallet create my_wallet
# create a local wallet in your stage DB
goldsky compose wallet create my_wallet --env local
```
Options:
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`) — the app name is read from here
* `--env ` Where to create the wallet (default: `cloud`)
* `-t, --token `
* `--json` Emit the wallet as JSON
The wallet address is printed to stdout so you can pipe it into other commands or scripts.
### wallet list
List wallets that have been created for this app. Returns an empty list for an app that has never deployed.
```bash theme={null}
goldsky compose wallet list
goldsky compose wallet list --env local
```
Options:
* `-m, --manifest ` Path to manifest file (default: `compose.yaml`)
* `--env ` (default: `cloud`)
* `-t, --token `
* `--json` Emit the wallets as JSON
## Secrets
See [Secrets](./secrets) for the full workflow.
### secret set
Set or update a secret. See [Secrets](./secrets) for full details.
```bash theme={null}
# set a cloud secret (default)
goldsky compose secret set MY_SECRET --value my-secret-value
# set a local secret (writes to .env)
goldsky compose secret set MY_SECRET --value my-secret-value --env local
# set a cloud secret and redeploy the app to pick it up
goldsky compose secret set MY_SECRET --value my-secret-value --redeploy
```
Options:
* `--value ` Secret value (required)
* `--env ` Where to store the secret (default: `cloud`)
* `--redeploy` After setting, redeploy the app so the new value takes effect
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
Secret names must be SCREAMING\_SNAKE\_CASE. A running app only picks up secret changes after a redeploy: pass `--redeploy` or run `goldsky compose deploy` yourself.
### secret delete
Delete a secret.
```bash theme={null}
goldsky compose secret delete MY_SECRET
goldsky compose secret delete MY_SECRET --env local
```
Options:
* `--env ` Where to delete from (default: `cloud`)
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
### secret list
List the names of cloud secrets set for this app. Secret values are never shown; see [How secrets are stored](./secrets#how-secrets-are-stored).
```bash theme={null}
goldsky compose secret list
```
Options:
* `-n, --name ` / `-m, --manifest `
* `-t, --token `
* `--json` Emit the secret names as JSON
# GitHub repo
Source: https://docs.goldsky.com/compose/compose-github
Browse Compose example apps and reference projects on GitHub.
# Task to Task Execution (callTask)
Source: https://docs.goldsky.com/compose/context/call-task
Invoke another task in the same Compose app with callTask, including arguments, retries, and return values.
## Cross task execution
A common pattern in compose apps is to trigger tasks from other tasks. This can be done either by creating an [HTTP trigger](../task-triggers)
and calling it with [fetch()](./fetch), or by using `callTask()`. Typically you'll want to use `callTask()`.
`callTask` invokes another task **in the same compose app**. It awaits the invoked task's `main()` function and resolves with its return value. If the invoked task throws, the error is re-thrown in the caller.
```typescript theme={null}
callTask, T = unknown>(
taskName: string,
args: Args,
retryConfig?: {
max_attempts: number;
initial_interval_ms: number;
backoff_factor: number;
},
): Promise
```
* `taskName` — the `name` of a task declared in `compose.yaml`.
* `args` — a JSON-serializable payload passed to the invoked task's `main()` as its payload argument.
* `retryConfig` — optional. Overrides the invoked task's retry settings for this call.
### Basic Example
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ callTask }: TaskContext) {
const result = await callTask("my_task", { some: "payload" });
return result;
}
```
### Examples
#### Call task in a loop
```typescript highlight={18-28} theme={null}
import { TaskContext } from "compose";
type MarketData = {
questionId: string;
resolved: boolean;
startTime: number;
endTime: number;
};
export async function main({ collection, callTask }: TaskContext) {
const marketsCollection = await collection("markets");
const unresolvedMarkets = await marketsCollection.findMany({ resolved: false });
const SYMBOL = "ETH-USD";
const marketDataPromises = unresolvedMarkets.map(async (market) => {
try {
const response = await callTask<{ error?: string }>("readMarketData", {
questionId: market.questionId,
symbol: SYMBOL,
startTime: market.startTime,
endTime: market.endTime,
});
return {
market,
marketData: response,
};
} catch (error) {
console.log(`Failed to read candle data for market ${market.questionId}: ${error}`);
return null;
}
});
const marketDataResults = await Promise.all(marketDataPromises);
return {
marketDataResults,
};
}
```
## Next Steps
Trigger tasks from cron, the CLI and via HTTP
You can use any sandbox compatible typescript packages with any package manager.
# State Management (collections)
Source: https://docs.goldsky.com/compose/context/collections
Persist and query state across Compose tasks and runs with MongoDB-like collection operations backed by SQLite locally and Postgres in the cloud.
Manage persistent state across tasks and task runs using MongoDB-like collection operations. Most applications have some notion of stored state — collections are where that state lives, so any task can read, write, search, and filter it at any time.
When running locally, collections use SQLite (stored in `.compose/stage.db`). When deployed, each Compose app gets its own fully isolated Postgres database. The API is identical in both environments.
The hosted Postgres is yours to use for application state via collections, but Compose also uses the same database internally to power durable execution (clean resumes after crashes and rolling deploys), transaction reorg monitoring, and wallet bookkeeping. Internal state lives in reserved namespaces — see [Reserved collection names](#reserved-collection-names) below.
## Examples
```typescript theme={null}
import { TaskContext } from "compose";
type Dog = {
breed: string;
color: string;
};
export async function main({ collection, fetch }: TaskContext) {
const dogsCollection = await collection("dogs", [
{ path: "color", type: "text" },
]);
// Get existing state
const brownDogs = await dogsCollection.findMany({ color: "brown" });
// Get some data
const newData = await fetch("https://api.dogs.com/v1/dogs/labrador");
// Update state
await dogsCollection.insertOne(newData);
return {
success: true,
};
}
```
## Full Interface
```typescript theme={null}
export type ScalarIndexType = "text" | "numeric" | "boolean" | "timestamptz";
export interface CollectionIndexSpec {
path: string;
type: ScalarIndexType;
unique?: boolean;
}
export interface FindOptions {
limit?: number;
offset?: number;
}
// Filter helpers for comparison operators
export type FilterHelper =
| "$gt"
| "$gte"
| "$lt"
| "$lte"
| "$in"
| "$ne"
| "$nin"
| "$exists";
export type HelperValue = Partial<
Record
>;
export type FilterValue = string | number | boolean | HelperValue;
export type Filter = Record;
export type WithId = T & { id: string };
export interface Collection {
readonly name: string;
insertOne(doc: TDoc, opts?: { id?: string }): Promise<{ id: string }>;
findOne(filter: Filter): Promise | null>;
findMany(filter: Filter, options?: FindOptions): Promise>>;
getById(id: string): Promise | null>;
/**
* @param opts.upsert - Defaults to true. Set to false to throw if document doesn't exist.
*/
setById(
id: string,
doc: TDoc,
opts?: { upsert?: boolean },
): Promise<{ id: string; upserted?: boolean; matched?: number }>;
deleteById(id: string): Promise<{ deletedCount: number }>;
drop(): Promise;
}
```
The `collection` function is available on `TaskContext`:
```typescript theme={null}
collection: (
name: string,
indexes?: CollectionIndexSpec[],
) => Promise>;
```
## Filter operators
The `Filter` type supports comparison operators beyond simple equality:
```typescript theme={null}
// Equality (default)
await dogs.findMany({ color: "brown" });
// Comparison operators
await dogs.findMany({ age: { $gt: 3 } }); // greater than
await dogs.findMany({ age: { $gte: 3 } }); // greater than or equal
await dogs.findMany({ age: { $lt: 10 } }); // less than
await dogs.findMany({ age: { $lte: 10 } }); // less than or equal
await dogs.findMany({ color: { $ne: "brown" } }); // not equal
// Set membership
await dogs.findMany({ breed: { $in: ["labrador", "golden"] } }); // in set
await dogs.findMany({ breed: { $nin: ["chihuahua"] } }); // not in set
// Field existence
await dogs.findMany({ nickname: { $exists: true } });
```
## `setById` return value
`setById` defaults to upsert behavior. The return value tells you whether the write created a new row or updated an existing one:
* `upserted: false`, `matched: 0` — the document did not exist, so a new row was inserted.
* `upserted: true`, `matched: 1` — an existing document with that `id` was updated.
Pass `{ upsert: false }` to disable insert-on-missing; `setById` will then throw if no document exists with that `id`.
## `drop()` vs `deleteById()`
`collection.drop()` runs `DROP TABLE IF EXISTS` against the backing database — it removes the collection's table entirely, not just its rows. Subsequent calls like `insertOne` do **not** automatically recreate the table; you must call `ctx.collection(name, indexes)` again to recreate it (along with its indexes).
For routine cleanup that preserves the table and its indexes, prefer `deleteById` (or iterate with `findMany` + `deleteById`). Reach for `drop()` only when you truly want to discard the collection.
## Reserved collection names
The following names are reserved for internal use and cannot be used as collection names: `wallets`, `stage`, `monitored_transactions`, `runs`, `context_functions`. Attempting to create or drop a collection with a reserved name throws an error.
## Next Steps
Interact with EVM blockchains and smart contracts.
Set and access environment variables.
# Environment variables (env)
Source: https://docs.goldsky.com/compose/context/env
Expose configuration values to Compose tasks via the env field and secrets, with separate local and cloud environments.
There are two ways to expose values to your tasks: the `env` field in your [App Configuration](../app-configuration) and
[Secrets](../secrets). Both show up on the `env` property of your task context at runtime.
Only the values for the current environment are injected — when running locally, only `env.local` values are used; when deployed, only `env.cloud` values are used. They are never merged together.
If a secret has the same name as an env variable, the secret value takes precedence.
## Example
Declare environment variables and secrets in your compose.yaml:
```yaml theme={null}
# compose.yaml
name: "my-app"
env:
local:
MY_VAR: "foo"
ANOTHER_VAR: "bar"
cloud:
MY_VAR: "boo"
ANOTHER_VAR: "baz"
secrets:
- MY_SECRET
tasks:
- name: "task1"
path: "./task1.ts"
```
Secrets are listed by name only — set their values locally in a `.env` file or in the cloud with `goldsky compose secrets set`. See [Secrets](../secrets) for details.
Reference them in your tasks via the `env` property of the task context:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ env }: TaskContext) {
console.log(env.MY_VAR);
console.log(env.ANOTHER_VAR);
console.log(env.MY_SECRET);
}
```
Trigger tasks from other tasks, creating composable workflows.
You can use any sandbox compatible typescript packages with any package manager.
# evm Chains
Source: https://docs.goldsky.com/compose/context/evm/chains
Use built-in or custom EVM chains in Compose tasks, powered by Goldsky Edge RPC.
Compose comes with built-in chain support powered by our own "Edge RPC" product for reliable, low-latency RPC access. See the full list of networks available out of the box on the [Edge RPC supported networks](/chains/supported-networks#edge-rpc) page. Need a chain that isn't listed? Contact us at [support@goldsky.com](mailto:support@goldsky.com) and we can look into adding support.
You can access any built-in chain with `evm.chains.` — the chain keys match the exports from [`viem/chains`](https://viem.sh/docs/chains/introduction) (for example, Ethereum mainnet is `evm.chains.mainnet`, not `evm.chains.ethereum`). Compose also supports [custom chain configurations](#using-custom-chains) for any EVM network not covered by Edge RPC. See [Wallets](./wallets) and [Smart Contracts](./contracts) for more details on how chains are used.
## Using built-in chains
This is the easiest and most reliable option for most scenarios.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm }: TaskContext) {
// the "chains" object is typed and holds all our natively supported chains
// native chains are pre-configured to use Goldsky's Edge RPC
console.log(evm.chains.base);
console.log(evm.chains.mainnet); // Ethereum mainnet
}
```
## Using custom chains
To use a custom chain you just need to create an object that fulfills our `Chain` interface.
```typescript theme={null}
import { TaskContext, Chain } from "compose";
export async function main({ evm, env }: TaskContext) {
// custom chain spec
const myCustomChain: Chain = {
id: 480,
name: "My Custom Chain",
testnet: false,
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: [`https://mycustomchain-mainnet.g.alchemy.com/v2/${env.MY_CUSTOM_CHAIN_API}`] },
public: { http: ["https://mycustomchain-mainnet.g.alchemy.com/public"] },
},
blockExplorers: {
default: { name: "myCustomChainScan", url: "https://myCustomChainScan.org" },
},
};
// you can now use the myCustomChain const anywhere you'd use a built-in chain (see Wallets and Smart Contracts for details)
console.log(myCustomChain);
}
```
The `Chain` type is available from the `TaskContext` interface. Here it is for reference:
```typescript theme={null}
export type Chain = {
id: number;
name: string;
testnet: boolean;
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
rpcUrls: {
public: { http: string[] };
default: { http: string[] };
};
blockExplorers: {
default: { name: string; url: string };
};
contracts?: Record;
};
```
# evm Smart Contracts
Source: https://docs.goldsky.com/compose/context/evm/contracts
Interact with smart contracts from Compose tasks using type-safe classes generated from your ABIs.
Compose allows you to interact with smart contracts with type safety and flexible wallet options. See [wallets](./wallets) for details on the different types of wallets.
## Code generation
To interact with smart contracts with full type safety, place your ABI JSON files in the `src/contracts/` folder. Compose automatically generates TypeScript classes when you run `compose start` or `compose deploy`.
You can also manually trigger code generation:
```bash theme={null}
compose codegen
```
The generated file is written to `.compose/generated/index.js`. On `compose deploy`, the CLI bundles this file alongside your tasks so the same typed classes are available in the cloud.
If you deploy your contract with [`compose deployContract`](../../cli-reference#deploycontract), the CLI writes its ABI to `src/contracts/` for you, so you can run `compose codegen` immediately afterward.
### Generated classes
For each ABI file (e.g., `src/contracts/BitcoinOracleContract.json`), Compose generates a typed class that you can access via `evm.contracts`:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet({ name: "my-wallet" });
// Access the generated contract class via evm.contracts
const bitcoinOracleContract = new evm.contracts.BitcoinOracleContract(
env.ORACLE_ADDRESS as `0x${string}`,
evm.chains.base,
wallet
);
// Convert timestamp and price to bytes32 format
const timestamp = Date.now();
const bitcoinPrice = 65000;
const timestampAsBytes32 = `0x${timestamp.toString(16).padStart(64, "0")}`;
const priceAsBytes32 = `0x${Math.round(bitcoinPrice * 100).toString(16).padStart(64, "0")}`;
// Call a state-changing method — returns { hash, receipt, userOpHash? }
const { hash } = await bitcoinOracleContract.setPrice(
timestampAsBytes32,
priceAsBytes32
);
// Call a view method — returns the decoded value directly
const result = await bitcoinOracleContract.getLatestPrice();
}
```
Contract methods are called directly on the instance without `.write` or `.read` suffixes. View/pure functions return their decoded value directly, while state-changing functions return `{ hash, receipt, userOpHash? }` (the `userOpHash` is present only when gas sponsoring routes the transaction through a bundler).
## Decoding event logs
Each generated contract class includes a static `decodeEventLog()` method for decoding onchain events with full type safety. This is commonly used in tasks triggered by [onchain events](../../task-triggers#chain-event-triggers):
```typescript theme={null}
import { TaskContext, OnchainEvent } from "compose";
export async function main({ evm }: TaskContext, payload: OnchainEvent) {
// Decode using a generated contract class — returns a typed union of all events in the ABI
const decoded = await evm.contracts.MyContract.decodeEventLog(payload);
if (decoded.eventName === "Transfer") {
console.log("Transfer:", decoded.args);
}
// Or use the top-level decodeEventLog with any ABI
const myAbi = [/* ... */];
const decoded2 = await evm.decodeEventLog(myAbi, payload);
}
```
You can also use `decodeEventLog` with a generic type parameter for a single known event type:
```typescript theme={null}
const decoded = await evm.contracts.MyContract.decodeEventLog(payload);
```
## Direct wallet methods
You can also interact with contracts directly through wallet methods without generated classes. This is useful for one-off calls or contracts you don't want to check an ABI into the repo for:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet({ name: "my-wallet" });
// Write to a contract — returns { hash, receipt, userOpHash? }
const { hash, receipt } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts]
);
// Read from a contract — returns the decoded value directly
const balance = await wallet.readContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"balanceOf(address) returns (uint256)",
[wallet.address]
);
}
```
See the [wallets documentation](./wallets) for the full `writeContract`, `readContract`, and `sendTransaction` signatures, gas handling, confirmations, and reorg protection options.
# evm Overview
Source: https://docs.goldsky.com/compose/context/evm/overview
Overview of the Compose evm context for chains, wallets, contracts, and reorg-aware transactions.
## Blockchain interactions
One of the key functions of Compose apps is off-chain logic orchestrating and reacting to on-chain functionality. The `evm` context
provides tools for blockchain actions with strong guarantees — `evm.chains` for chain configuration, `evm.wallet()` for wallets,
and `evm.contracts` for type-safe contract interactions. See the individual sections below for full detail on each.
1. [Chains](./chains)
2. [Wallets](./wallets)
3. [Smart Contracts](./contracts)
4. [Reorg Handling](./reorgs)
# evm Reorg Handling
Source: https://docs.goldsky.com/compose/context/evm/reorgs
Wait for confirmations and handle mempool eviction or reorgs automatically when sending transactions from Compose tasks.
Compose has built-in functionality for handling mempool eviction and re-orgs, making it easy to get strong guarantees about your transactions
without lots of bespoke code. Both `wallet.writeContract()` and `wallet.sendTransaction()` support confirmation and reorg handling.
## Confirmations
By default all transactions via `wallet.writeContract()` or state-changing methods on [contracts](./contracts) will wait for one confirmation before
resolving the method's promise. This means that the transaction will be seen in at least one block by the time your task execution advances. You
can configure this further with the optional `TransactionConfirmation` logic. If your transaction doesn't make it into the number of blocks specified
the context function call will fail and retry logic will kick in. If it still doesn't make it into the desired number of blocks after retries are exhausted
then the promise will reject.
Here's an example for both `wallet.writeContract()` and on a contract class method:
```typescript highlight={18} theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet();
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
// this will not resolve the promise until the transaction has been seen in 5 blocks
// if the transaction doesn't make it into five blocks (maybe is evicted from mempool, etc),
// then your retry config will determine whether to retry the transaction
confirmations: 5,
},
{
max_attempts: 5,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
}
```
## Reorgs
Reorgs could revert your transaction after the promise is resolved and the confirmations are all seen. For example, if you have 5 confirmations
but later on a 100 block reorg occurs, you can configure your transaction with various retry "behaviors" to handle that situation. Like with confirmations,
reorg handling can be used both with `wallet.writeContract()` as well as any state-changing methods on a [smart contract class](./contracts).
There are several behaviors that we support currently:
| behavior | description | other params |
| -------- | ----------------------------------------------------------------------------------------- | --------------------------- |
| replay | re-executes transaction with fresh gas parameters and nonce (recalculated at replay time) | n/a |
| log | just logs the reorg to your standard app logs | logLevel (optional) |
| task | allows you to pass a compose task to call for custom handling | task (name of compose task) |
### Examples
```typescript highlight={17-24,42-49,67-74} theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet();
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
// Replay on reorg
const { hash } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 5,
onReorg: {
action: {
// this will replay the transaction with a fresh nonce and gas if the transaction is reorged off chain after the 5 confirmations specified below
// it will then start watching again for the number of blocks specified in the "depth" property
type: "replay",
},
// we'll watch this transaction in the background for 200 blocks and replay it if the transaction receipt disappears
depth: 200,
},
},
{
max_attempts: 5,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
// Log on reorg
const { hash: hash2 } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 5,
onReorg: {
action: {
// this will just log in your normal app logs if the transaction is reorged off chain after the 5 confirmations specified below
type: "log",
logLevel: "warn", // defaults to "error"
},
// we'll watch this transaction in the background for 200 blocks and log if the transaction receipt disappears
depth: 200,
},
},
{
max_attempts: 5,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
// Custom task on reorg
const { hash: hash3 } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 5,
onReorg: {
action: {
// this will send the transaction as a payload to the specified task if the transaction is reorged off chain after the 5 confirmations specified below
type: "task",
task: "reorg-reconciler", // the name of your compose task with custom re-org handling logic
},
// we'll watch this transaction in the background for 200 blocks and call your "reorg-reconciler" if the transaction receipt disappears
depth: 200,
},
},
{
max_attempts: 5,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
}
```
Monitored transactions are checked for reorgs every 5 minutes. When a reorg is detected and the `replay` action is configured, the transaction is re-submitted with fresh gas parameters and nonce (not the original values, since the post-reorg state may differ).
## Full TransactionConfirmation type
```typescript theme={null}
export type ReplayOnReorg = {
type: "replay";
};
export type LogOnReorg = {
type: "log";
logLevel?: "error" | "info" | "warn"; // defaults to "error"
};
export type CustomReorgAction = {
type: "task";
// your task will be sent with a payload the full transaction minus gas and nonce
task: string;
};
export type OnReorgOptions = ReplayOnReorg | LogOnReorg | CustomReorgAction;
export type OnReorgConfig = {
action: OnReorgOptions;
depth: number;
};
export interface TransactionConfirmation {
// this the number of block confirmations before we resolve the promise
// i.e. "wait 5 blocks before proceeding to the next step in my task"
confirmations?: number;
onReorg?: OnReorgConfig;
}
```
# evm Wallets
Source: https://docs.goldsky.com/compose/context/evm/wallets
Create and use Compose smart wallets, EOA private-key wallets, and impersonated wallets for blockchain transactions.
There are several types of wallets and wallet behaviors that compose supports.
## Smart wallets
If you just need to interact with smart contracts, then the easiest thing to do is just build one of our smart wallets. You'll be able to see
all of your wallets in the dashboard at `https://app.goldsky.com/{projectId}/dashboard/compose/{appName}`.
### Create a smart wallet
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
// this is idempotent so the wallet is only created the first time this is called and is "retrieved" after that.
// passing no args will create a "default" wallet for your app
const wallet = await evm.wallet();
console.log(wallet.address);
// you can create multiple named wallets too. This will generate multiple saved smart wallets that can be referenced by name in different
// tasks and task runs
const walletOne = await evm.wallet({ name: "wallet-one" });
const walletTwo = await evm.wallet({ name: "wallet-two" });
// private key wallet
const privateKeyWallet = await evm.wallet({ privateKey: env.MY_KEY });
// now you can use these wallet to make transactions (see below for details)
}
```
### Using wallets
Once you have a wallet created you can use it to write to smart contracts, see the full [smart contract docs](./contracts) for more details
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env, fetch }: TaskContext) {
const wallet = await evm.wallet();
const response = await fetch<{ bitcoin: { usd: number } }>(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd"
);
// Convert timestamp and price to bytes32 format
const timestamp = Date.now();
const bitcoinPrice = response.bitcoin.usd;
const timestampAsBytes32 = `0x${timestamp.toString(16).padStart(64, "0")}`;
const priceAsBytes32 = `0x${Math.round(bitcoinPrice * 100).toString(16).padStart(64, "0")}`;
const bitcoinOracleContract = new evm.contracts.BitcoinOracleContract(
env.ORACLE_ADDRESS,
evm.chains.base,
wallet
);
const { hash } = await bitcoinOracleContract.setPrice(
timestampAsBytes32,
priceAsBytes32
);
}
```
You can also make or simulate transactions with methods on the Wallet class:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet();
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash, receipt, userOpHash } = await wallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 3, // this will not resolve the promise until the transaction has been seen in 3 blocks
onReorg: {
// this will replay the transaction with new nonce and new gas if it's reorged later on after the three confirmations have passed
// see "Reorg Handling" for more info
action: {
type: "replay",
},
depth: 200,
},
}
);
// userOpHash is set for gas-sponsored transactions (ERC-4337 UserOperation hash)
// useful for debugging on bundler explorers
if (userOpHash) {
console.log(`UserOp hash: ${userOpHash}`);
}
}
```
### sendTransaction
For lower-level control, use `sendTransaction` to send a transaction with pre-encoded calldata. This is useful when you need to encode the transaction data yourself, pass specific gas parameters, or interact with contracts in ways that `writeContract` doesn't cover.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet();
// encode your calldata however you like
const data = "0x..." as `0x${string}`;
const { hash, receipt } = await wallet.sendTransaction(
{
to: env.CONTRACT_ADDRESS as `0x${string}`,
data,
chain: evm.chains.ethereum,
},
{ confirmations: 3 }
);
console.log(`Transaction confirmed: ${hash}, status: ${receipt.status}`);
}
```
You can also pass explicit gas parameters for full control over fees and gas limits:
```typescript theme={null}
const { hash, receipt } = await wallet.sendTransaction(
{
to: env.CONTRACT_ADDRESS as `0x${string}`,
data,
chain: evm.chains.ethereum,
value: 0n, // ETH value to send with the transaction
gas: 500000n, // gas limit
maxFeePerGas: 30000000000n, // EIP-1559 max fee per gas
maxPriorityFeePerGas: 1000000000n, // EIP-1559 priority fee
nonce: 42, // explicit nonce (EOA wallets only)
},
{
confirmations: 5,
onReorg: {
depth: 200,
action: { type: "replay" },
},
}
);
```
The `nonce` parameter is only supported with EOA (private key) wallets. Smart wallets manage nonces internally.
### getBalance
Check the native token balance of any wallet:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm }: TaskContext) {
const wallet = await evm.wallet();
// Returns balance in wei as a string
const balance = await wallet.getBalance(evm.chains.ethereum);
console.log(`Balance: ${balance} wei`);
}
```
### Wallet properties
Every wallet exposes `name` and `address` as read-only properties:
```typescript theme={null}
const wallet = await evm.wallet({ name: "my-wallet" });
console.log(wallet.name); // "my-wallet"
console.log(wallet.address); // "0x..."
```
## EOA wallets
You can also use EOAs that you already own, this allows you to self-fund gas, send and receive tokens in your tasks, and interact with smart contracts
in which an EOA you already own has privileges on particular contract methods. Currently we support storing your EOA private key in Compose's secret
management system, but in the future you'll be able to use private keys that are secured within TEEs. EOA wallets never pass their private keys outside of
the task process and they sign requests passed in unsigned from the host process. When tasks run in TEEs they'll be able to use private keys very
securely within the TEE, never exposing it to any part of the stack outside of the TEE. This will empower Compose to run the most security sensitive
use cases.
First, you'll need to store the private key in Goldsky's secret management system and reference it in your compose.yaml file. You can see details on
how to do that in the [Secrets docs](../../secrets). Once you have your private key secret stored, you can use it to create a wallet:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const privateKeyWallet = await evm.wallet({ privateKey: env.MY_PRIVATE_KEY_SECRET });
// now you can use the wallet the same as any other wallet
}
```
## Impersonated wallets
When testing locally with `--fork-chains`, you may need to call contract methods that are restricted to a specific address — for example, an owner-only admin function or a method guarded by an access control list. Normally you'd need the private key for that address, but with the `--impersonate` flag you can act as any address on the local TEVM fork using only its public address.
This is useful when:
* You want to test privileged contract methods without giving your local environment access to the private key
* You need to debug interactions with a contract where only a specific address has permission to call certain methods
* You're testing against a forked mainnet contract and want to simulate actions from an address you control on-chain but don't want to expose the key locally
### Usage
Pass `--impersonate` alongside `--fork-chains` when starting your app. The flag takes a comma-separated list of `walletName=address` mappings:
```bash theme={null}
goldsky compose start --fork-chains --impersonate "my-wallet=0x1234...abcd"
```
You can impersonate multiple wallets at once:
```bash theme={null}
goldsky compose start --fork-chains --impersonate "admin=0xAdminAddr,treasury=0xTreasuryAddr"
```
Your task code stays exactly the same — just reference the wallet by name as usual. The wallet's `.address` will resolve to the impersonated address, and all contract interactions (reads, writes, and simulations) will execute as that address on the local fork.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm }: TaskContext) {
// When started with --impersonate "admin=0xOwnerAddress",
// this wallet's .address will be 0xOwnerAddress
const wallet = await evm.wallet({ name: "admin" });
console.log(wallet.address); // 0xOwnerAddress
// This call executes as the impersonated address on the fork,
// so owner-only methods will succeed
const { hash } = await wallet.writeContract(
evm.chains.base,
"0xMyContract..." as `0x${string}`,
"adminFunction(uint256)",
[42n]
);
}
```
Impersonation also works with private-key wallets. If a private-key wallet has a `name` that matches an entry in the impersonate map, the impersonated address takes precedence and the private key is effectively ignored on the fork:
```typescript theme={null}
const wallet = await evm.wallet({
name: "my-pk-wallet",
privateKey: env.MY_KEY,
});
// With --impersonate "my-pk-wallet=0xTargetAddr",
// wallet.address is 0xTargetAddr, not the address derived from MY_KEY
```
`--impersonate` requires `--fork-chains` and only works in local development. All impersonated transactions execute on your local TEVM fork — nothing is sent to the actual chain. When you deploy to cloud, wallets resolve normally.
## Gas sponsoring
By default, smart wallets (wallets created without a private key) use gas sponsoring so you don't have to think about managing gas funding.
Smart wallets use EIP-7702 delegation for account abstraction, with gas costs handled through ERC-4337 UserOperations.
You pay the gas bill as part of your normal monthly Goldsky bill, avoiding the complex budgetary and tax issues of purchasing gas tokens.
When you don't use gas sponsoring, you'll need to get your wallet address from the compose dashboard at `https://app.goldsky.com/{projectId}/dashboard/compose/{appName}`
and then transfer gas tokens to that wallet through a wallet or an exchange.
### Gas sponsoring with EOA (private key) wallets
You can also opt into gas sponsoring for EOA wallets by passing `sponsorGas: true` when creating the wallet. Compose delegates the EOA via EIP-7702 on first use and routes transactions through ERC-4337 UserOperations, just like smart wallets — you keep full control of the key and signing, but you don't have to fund the wallet with native gas tokens.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
const wallet = await evm.wallet({
privateKey: env.MY_PRIVATE_KEY,
sponsorGas: true,
});
// transactions now go through the sponsored UserOp flow
// you pay for gas on your monthly Goldsky bill instead of from this EOA
}
```
Sponsored EOA transactions only run in deployed apps. Running with `sponsorGas: true` locally will throw a clear error that tells you to either remove `sponsorGas`, fund the wallet manually, or re-run with `--fork-chains` to exercise the sponsored flow against a forked chain. In `--fork-chains` mode the transaction is executed against the local fork (where gas is free), and you'll see a warning reminding you the sponsored flow only takes effect once deployed to cloud.
### Gas usage in the dashboard
Every sponsored or self-paid transaction emits a run event with `evm.gas_used` and `evm.total_cost_wei` attributes (both decimal strings in wei) so you can audit per-transaction gas spend from the Compose dashboard at `https://app.goldsky.com/{projectId}/dashboard/compose/{appName}`. On OP Stack L2s (Lisk, Base, Optimism, and friends) `evm.total_cost_wei` includes the L1 data fee, which typically dominates the total cost — reading `gasUsed × effectiveGasPrice` from the receipt alone will under-report by roughly two orders of magnitude.
## Gas pricing
For non-sponsored transactions, Compose uses automatic gas estimation with sensible defaults. If you need precise control over gas parameters, use [`sendTransaction`](#sendtransaction) instead of `writeContract` and specify `maxFeePerGas`, `maxPriorityFeePerGas`, and `gas` explicitly.
`writeContract` automatically simulates the transaction before submitting it, catching revert errors early. `sendTransaction` does not simulate — it submits directly.
#### Override default gas sponsoring behavior
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
// disable gas sponsoring on a smart wallet (default is true for smart wallets)
const smartWallet = await evm.wallet({ name: "self-funded", sponsorGas: false });
// enable gas sponsoring on an EOA wallet (default is false for EOA wallets)
const sponsoredEoa = await evm.wallet({
privateKey: env.MY_PRIVATE_KEY,
sponsorGas: true,
});
}
```
## Full wallet interface
```typescript theme={null}
export interface WalletConfig {
name?: string; // defaults to "default"
privateKey?: string;
sponsorGas?: boolean; // defaults to true if no privateKey and false if privateKey
}
export interface IWallet {
readonly name: string;
readonly address: `0x${string}`;
writeContract(
chain: Chain,
contractAddress: `0x${string}`,
functionSig: string,
args: unknown[],
confirmation?: TransactionConfirmation,
retryConfig?: ContextFunctionRetryConfig
): Promise<{
hash: string;
receipt: TransactionReceipt;
userOpHash?: string; // set for gas-sponsored transactions (ERC-4337)
}>;
readContract(
chain: Chain,
contractAddress: `0x${string}`,
functionSig: string,
args: unknown[],
retryConfig?: ContextFunctionRetryConfig
): Promise;
sendTransaction(
config: {
to: `0x${string}`;
data: `0x${string}`;
chain: Chain;
value?: bigint;
maxFeePerGas?: bigint;
maxPriorityFeePerGas?: bigint;
gas?: bigint;
nonce?: number;
},
confirmation?: TransactionConfirmation,
retryConfig?: ContextFunctionRetryConfig
): Promise<{
hash: string;
receipt: TransactionReceipt;
userOpHash?: string; // set for gas-sponsored transactions (ERC-4337)
}>;
simulate(
chain: Chain,
contractAddress: `0x${string}`,
functionSig: string,
args: unknown[],
retryConfig?: ContextFunctionRetryConfig
): Promise;
getBalance(
chain: Chain,
retryConfig?: ContextFunctionRetryConfig
): Promise; // native token balance in wei
}
```
# HTTP Requests (fetch)
Source: https://docs.goldsky.com/compose/context/fetch
Make auditable HTTP requests from Compose tasks with built-in caching, retries, and OpenTelemetry spans.
## Auditable HTTP Requests with "fetch"
Make HTTP requests that are recorded as spans on the task run. This is how Compose apps interact with
off-chain systems — every `ctx.fetch` call, its URL, and its response are captured in the run's event
log, so the behavior of your app is auditable after the fact. For example, if you have an oracle that
is advertised as being driven by an aggregate of several well-known price feeds, users of dApps built
with it can audit the calls made to determine their prices, ensuring the price logic is fair and works
as advertised.
`ctx.fetch` wraps the native `fetch` and adds a few Compose-specific behaviors: the response is
parsed as JSON (with a text fallback), each call is recorded as an OpenTelemetry span, successful
results are cached by a deterministic idempotency key so replays after a crash don't re-hit the
remote server, and the request runs on a dedicated HTTP client pool isolated from Compose's
internal traffic.
```typescript theme={null}
fetch(
url: string,
fetchConfigOrRetryConfig?: FetchConfig | ContextFunctionRetryConfig,
retryConfig?: ContextFunctionRetryConfig
): Promise
```
Where `FetchConfig` is:
```typescript theme={null}
export interface FetchConfig {
method?: string;
headers?: Record;
body?: Record | string;
}
```
And `ContextFunctionRetryConfig` is:
```typescript theme={null}
type ContextFunctionRetryConfig = {
max_attempts: number;
initial_interval_ms: number;
backoff_factor: number;
};
```
`ctx.fetch` retries GET, HEAD, and OPTIONS requests by default (`max_attempts` defaults to `3`, `initial_interval_ms` to `500`, and `backoff_factor` to `2`). POST, PUT, PATCH, and DELETE default to a single attempt because they are not assumed idempotent. To override either, pass a `ContextFunctionRetryConfig` explicitly, as in the "Request with Custom Retry Configuration" example below.
### Response handling
`ctx.fetch` reads the response body as JSON and returns the parsed object. If the body is not valid
JSON, it falls back to returning the raw text. Non-2xx responses throw an error that includes the
status code, status text, and response body — so you do not need to check `response.ok` yourself.
Because of this, the return type is the decoded body, not a `Response` object.
### Examples
#### Basic GET Request
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ fetch }: TaskContext) {
const data = await fetch("https://api.example.com/data");
return data;
}
```
#### POST Request with JSON Body
If `body` is an object, `ctx.fetch` JSON-encodes it for you. Pass a string if you want to send a raw
body (e.g. form-encoded or pre-serialized JSON).
To call another task in the same app, use [`ctx.callTask`](./call-task) — you don't need to go
through HTTP unless you're hitting an [HTTP-triggered task](../task-triggers) externally.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ fetch }: TaskContext) {
const result = await fetch("https://api.example.com/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer token123",
},
body: {
name: "John Doe",
email: "john@example.com",
},
});
return result;
}
```
#### Request with Custom Retry Configuration
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ fetch }: TaskContext) {
const data = await fetch(
"https://unreliable-api.com/data",
{
method: "GET",
headers: { "User-Agent": "Compose/1.0" },
},
{
max_attempts: 5,
initial_interval_ms: 2000,
backoff_factor: 1.5,
}
);
return data;
}
```
## Next Steps
Manage state across tasks and task runs with collections.
Interact with EVM blockchains and smart contracts.
# Overview
Source: https://docs.goldsky.com/compose/context/overview
How Compose context functions provide auditable, retryable, and durable access to the outside world from sandboxed tasks.
Context functions allow task sandboxes to access the outside world.
Context functions support individual retry configuration and are automatically logged for auditing and debugging. All communication outside of the task sandbox happens via Context Functions, making Compose apps run deterministic, given the same world context.
## Retry Configuration
All Context functions accept an optional retry configuration:
```typescript theme={null}
type ContextFunctionRetryConfig = {
max_attempts: number; // 3 for read-only calls, 1 for mutations
initial_interval_ms: number; // Default: 500
backoff_factor: number; // Default: 2
};
```
**How Context Function Retries Work:**
* Each context function call can have its own retry configuration
* If a context function fails, it retries according to its configuration
* If all context function retries are exhausted, the context function will throw
* If you don't catch a failed context function call, a task-level retry may trigger, restarting the task and any context-function retries
## Durable execution and context caching
Compose provides durable execution guarantees. All context function calls are deterministically cached — if a task is interrupted (e.g. by a restart or deployment) and resumed, context functions that already completed will return their cached results instead of re-executing. This means:
* Transactions that already succeeded will not be re-sent
* Fetch calls that already returned will not be re-made
* Collection operations that already completed will not be repeated
This caching is scoped to each individual task run. Cached results are automatically cleaned up when the run completes (success or failure).
Non-deterministic values, such as timestamps or random IDs, must be produced through [ctx.sideEffect()](./side-effect) so a replay returns the same value instead of recomputing it.
By default, read-only context calls make up to 3 attempts (`max_attempts: 3`, `initial_interval_ms: 500`, `backoff_factor: 2`). This covers `readContract`, `simulate`, `getBalance`, and `ctx.fetch` with `GET`, `HEAD`, or `OPTIONS`. Mutations such as `callTask`, `writeContract`, `sendTransaction`, and `ctx.fetch` with `POST`, `PUT`, `PATCH`, or `DELETE` default to a single attempt (`max_attempts: 1`). Pass a `retryConfig` to override either default.
## Overview of key context properties and functions
Here's a brief overview of what context enables. Use the left nav or links below to find full reference docs.
* State Management - [Collections](./collections)
* Blockchain Interactions - [evm](./evm/overview)
* HTTP requests - [fetch](./fetch)
* Task to task execution - [callTask](./call-task)
* Non-deterministic values - [sideEffect](./side-effect)
* Reading env variables - [env](./env)
* Reorg handling - [Reorgs](./evm/reorgs)
* Turbo pipeline management - [turbo](./turbo)
* Run-aware logging - [logger](#logger)
## Full TaskContext interface
```typescript theme={null}
export type ContextFunctionRetryConfig = {
max_attempts: number;
initial_interval_ms: number;
backoff_factor: number;
};
export type Chain = {
id: number;
name: string;
testnet: boolean;
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
rpcUrls: {
public: { http: string[] };
default: { http: string[] };
};
blockExplorers: {
default: { name: string; url: string };
};
contracts?: Record;
};
export type ScalarIndexType = "text" | "numeric" | "boolean" | "timestamptz";
export interface CollectionIndexSpec {
path: string;
type: ScalarIndexType;
unique?: boolean;
}
export interface FindOptions {
limit?: number;
offset?: number;
}
// Filter helpers for comparison operators
export type FilterHelper =
| "$gt"
| "$gte"
| "$lt"
| "$lte"
| "$in"
| "$ne"
| "$nin"
| "$exists";
export type HelperValue = Partial<
Record
>;
export type FilterValue = string | number | boolean | HelperValue;
export type Filter = Record;
export type WithId = T & { id: string };
export interface Collection {
readonly name: string;
insertOne(doc: TDoc, opts?: { id?: string }): Promise<{ id: string }>;
findOne(filter: Filter): Promise | null>;
findMany(filter: Filter, options?: FindOptions): Promise>>;
getById(id: string): Promise | null>;
/**
* @param opts.upsert - Defaults to true. Set to false to throw if document doesn't exist.
*/
setById(
id: string,
doc: TDoc,
opts?: { upsert?: boolean },
): Promise<{ id: string; upserted?: boolean; matched?: number }>;
deleteById(id: string): Promise<{ deletedCount: number }>;
drop(): Promise;
}
export type Address = `0x${string}`;
export interface WalletConfig {
name?: string; // defaults to "default"
privateKey?: string;
sponsorGas?: boolean; // defaults to true for Privy wallets, false for private key wallets
}
export type ReplayOnReorg = {
type: "replay";
};
export type LogOnReorg = {
type: "log";
logLevel?: "error" | "info" | "warn"; // defaults to "error"
};
export type CustomReorgAction = {
type: "task";
// your task will be sent with a payload the full transaction minus gas and nonce
task: string;
};
export type OnReorgOptions = ReplayOnReorg | LogOnReorg | CustomReorgAction;
export type OnReorgConfig = {
action: OnReorgOptions;
depth: number;
};
export interface TransactionConfirmation {
// this the number of block confirmations before we resolve the promise
// i.e. "wait 5 blocks before proceeding to the next step in my task"
confirmations?: number;
onReorg?: OnReorgConfig;
}
export interface IWallet {
readonly name: string;
readonly address: Address;
writeContract(
chain: Chain,
contractAddress: Address,
functionSig: string,
args: unknown[],
confirmation?: TransactionConfirmation,
retryConfig?: ContextFunctionRetryConfig,
): Promise<{
hash: string;
receipt: TransactionReceipt;
userOpHash?: string; // set for gas-sponsored transactions (ERC-4337)
}>;
readContract(
chain: Chain,
contractAddress: Address,
functionSig: string,
args: unknown[],
retryConfig?: ContextFunctionRetryConfig,
): Promise;
sendTransaction(
config: {
to: Address;
data: `0x${string}`;
chain: Chain;
value?: bigint;
maxFeePerGas?: bigint;
maxPriorityFeePerGas?: bigint;
gas?: bigint;
nonce?: number; // EOA wallets only
},
confirmation?: TransactionConfirmation,
retryConfig?: ContextFunctionRetryConfig,
): Promise<{
hash: string;
receipt: TransactionReceipt;
userOpHash?: string; // set for gas-sponsored transactions (ERC-4337)
}>;
simulate(
chain: Chain,
contractAddress: Address,
functionSig: string,
args: unknown[],
retryConfig?: ContextFunctionRetryConfig,
): Promise<{ hash: string }>;
getBalance(
chain: Chain,
retryConfig?: ContextFunctionRetryConfig,
): Promise; // native token balance in wei
}
export interface Log {
address: Address;
topics: `0x${string}`[];
data: `0x${string}`;
blockHash: `0x${string}`;
blockNumber: bigint;
logIndex: number;
transactionHash: `0x${string}`;
transactionIndex: number;
removed?: boolean;
}
export interface TransactionReceipt {
blockHash: `0x${string}`;
blockNumber: bigint;
contractAddress: Address | null;
cumulativeGasUsed: bigint;
effectiveGasPrice: bigint;
from: Address;
gasUsed: bigint;
logs: Log[];
logsBloom: `0x${string}`;
status: "success" | "reverted";
to: Address | null;
transactionHash: `0x${string}`;
transactionIndex: number;
type: "legacy" | "eip1559" | "eip2930" | "eip4844" | "eip7702";
}
export interface OnchainEvent {
blockNumber: number;
blockHash: string;
transactionIndex: number;
removed: boolean;
address: string;
data: `0x${string}`;
topics: `0x${string}`[];
transactionHash: string;
logIndex: number;
}
export interface FetchConfig {
method?: string;
headers?: Record;
body?: Record | string;
}
export interface Logger {
info(message: string, data?: Record): void;
warn(message: string, data?: Record): void;
error(message: string, data?: Record): void;
}
export type TaskContext = {
env: Record;
logger: Logger;
callTask: , T = unknown>(
taskName: string,
args: Args,
retryConfig?: ContextFunctionRetryConfig,
) => Promise;
sideEffect: (fn: () => T | Promise) => Promise;
fetch: (
url: string,
fetchConfigOrRetryConfig?: FetchConfig | ContextFunctionRetryConfig,
retryConfig?: ContextFunctionRetryConfig,
) => Promise;
evm: {
chains: Record;
wallet: (config: WalletConfig) => Promise;
decodeEventLog: (abi: Abi, log: OnchainEvent) => Promise;
contracts: Record; // auto-generated from ABIs in src/contracts/
};
collection: (
name: string,
indexes?: CollectionIndexSpec[],
) => Promise>;
turbo: {
createPipeline: (config: CreateTurboPipelineConfig, retryConfig?: ContextFunctionRetryConfig) => Promise;
createJob: (config: CreateTurboJobConfig, retryConfig?: ContextFunctionRetryConfig) => Promise;
getPipeline: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
getStatus: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
listPipelines: (retryConfig?: ContextFunctionRetryConfig) => Promise;
pausePipeline: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
resumePipeline: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
restartPipeline: (name: string, options?: RestartOptions, retryConfig?: ContextFunctionRetryConfig) => Promise;
deletePipeline: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
getLogs: (name: string, options?: GetLogsOptions, retryConfig?: ContextFunctionRetryConfig) => Promise;
getState: (name: string, retryConfig?: ContextFunctionRetryConfig) => Promise;
validate: (definition: TurboPipelineDefinition, retryConfig?: ContextFunctionRetryConfig) => Promise;
};
};
```
See the [turbo reference](./turbo) for the full type definitions used above (`TurboPipeline`, `TurboPipelineStatus`, `ValidateResult`, etc.).
## Logger
`context.logger` is a run-aware structured logger. Unlike `console.log`, logs emitted through `context.logger` are tagged with the current task name and run ID, which enables two powerful debugging workflows in the Compose dashboard:
1. **Search app-level logs, then jump to the run** — On the Logs tab of your app, you can search for a log pattern across all runs. Each matching log entry includes a "View run" link that takes you directly to the full task run, where you can see the complete OpenTelemetry trace of everything that happened in that run.
2. **View run-specific logs** — On any task run's detail page, the Logs tab shows only logs from that specific run, making it easy to understand what happened in a single execution.
### Usage
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ logger, evm, env }: TaskContext) {
logger.info("Starting price update", { source: "coingecko" });
try {
const wallet = await evm.wallet();
const { hash } = await wallet.writeContract(
evm.chains.base,
env.CONTRACT_ADDRESS as `0x${string}`,
"setPrice(bytes32,bytes32)",
[timestampBytes, priceBytes]
);
logger.info("Price updated", { hash, chain: "base" });
} catch (error) {
logger.error("Price update failed", {
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
```
### API
```typescript theme={null}
interface Logger {
info(message: string, data?: Record): void;
warn(message: string, data?: Record): void;
error(message: string, data?: Record): void;
}
```
Each method takes a message string and an optional `data` object for structured metadata. BigInt values (common in EVM code) are automatically serialized as strings.
### `console.log` vs `context.logger`
Both `console.log` and `context.logger` output to your terminal during local development. The difference is in the cloud:
| | `console.log` | `context.logger` |
| -------------------------------- | ------------- | ---------------- |
| Appears in app-level logs | Yes | Yes |
| Tagged with run ID and task name | No | Yes |
| "View run" link in dashboard | No | Yes |
| Appears in run-specific Logs tab | No | Yes |
| Structured `data` field | No | Yes |
Use `console.log` for quick debugging. Use `context.logger` when you want logs that you can trace back to a specific task run in production.
## Next Steps
Make auditable HTTP requests with fetch.
Manage state across tasks and task runs with collections.
# Non-Deterministic Values (sideEffect)
Source: https://docs.goldsky.com/compose/context/side-effect
Cache non-deterministic values like timestamps and UUIDs with ctx.sideEffect so task replays stay deterministic.
## Non-deterministic values and durable execution
Durable execution replays a task from the start after an interruption, such as a restart or rolling deploy. Context calls that already completed return their cached results instead of re-running. A bare `Date.now()` or `crypto.randomUUID()` is not a context call, so it produces a different value on every replay and desynchronizes the run from its cached history.
`ctx.sideEffect()` runs your callback once and caches the returned value like any other context call. On replay, it returns the cached value and the callback does not run again.
```typescript theme={null}
sideEffect(fn: () => T | Promise): Promise
```
### Example
Generate a request id and timestamp once, then reuse them in a `writeContract` call:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main(ctx: TaskContext) {
// Computed once; the cached values are returned on any replay.
const { requestId, timestamp } = await ctx.sideEffect(async () => ({
requestId: crypto.randomUUID(),
timestamp: Date.now(),
}));
const wallet = await ctx.evm.wallet({ name: "default" });
// requestId and timestamp are passed in the contract call args
await wallet.writeContract(
ctx.evm.chains.base,
ctx.env.CONTRACT_ADDRESS as `0x${string}`,
"recordRequest(string,uint256)",
[requestId, timestamp]
);
}
```
Use `ctx.sideEffect()` for any value that must stay the same across replays: timestamps, random values, UUIDs, or any other source of non-determinism.
For the caching model that makes this necessary, see [Durable execution and context caching](./overview#durable-execution-and-context-caching).
## Next Steps
Invoke another task in the same app.
How context functions, caching, and retries work.
# Turbo Pipelines (turbo)
Source: https://docs.goldsky.com/compose/context/turbo
## Manage Turbo Pipelines with "turbo"
`ctx.turbo` lets a task create and manage [Turbo pipelines](/turbo-pipelines/introduction) and
[jobs](/turbo-pipelines/job-mode) from inside your Compose app. Use it to spin up a pipeline in
response to an on-chain event, kick off a one-time backfill and poll it to completion, or pause and
clean up pipelines your app owns.
A Turbo **job** is a pipeline whose definition has `job: true` — it runs to completion and then exits,
rather than streaming forever. `ctx.turbo.createJob` is a shortcut for `createPipeline` with that flag
set.
### How pipelines are named and cleaned up
Pipelines you create through `ctx.turbo` are namespaced under your Compose app. You pass a plain name
like `token-transfers`, and the pipeline is registered internally with your app's prefix. Every method
handles the prefix for you, so you always refer to pipelines by the plain name you gave them, and
`listPipelines` only returns pipelines that belong to your app.
The upshot is that when your Compose app is deleted, the pipelines it created are deleted with it — you
don't have to track and tear them down yourself.
The namespaced name (app prefix included) has a length limit. If your app name plus the pipeline
name is too long, `createPipeline` throws. Keep pipeline names short and the error message tells you
the maximum.
### Methods
```typescript theme={null}
turbo: {
createPipeline(config: CreateTurboPipelineConfig, retryConfig?): Promise;
createJob(config: CreateTurboJobConfig, retryConfig?): Promise;
getPipeline(name: string, retryConfig?): Promise;
getStatus(name: string, retryConfig?): Promise;
listPipelines(retryConfig?): Promise;
pausePipeline(name: string, retryConfig?): Promise;
resumePipeline(name: string, retryConfig?): Promise;
restartPipeline(name: string, options?: RestartOptions, retryConfig?): Promise;
deletePipeline(name: string, retryConfig?): Promise;
getLogs(name: string, options?: GetLogsOptions, retryConfig?): Promise;
getState(name: string, retryConfig?): Promise;
validate(definition: TurboPipelineDefinition, retryConfig?): Promise;
}
```
### Config types
```typescript theme={null}
export type TurboPipelineDefinition = {
sources: Record;
transforms: Record;
sinks: Record;
job?: boolean;
};
export type CreateTurboPipelineConfig = {
name: string; // lowercase letters, numbers, and hyphens
resourceSize?: string;
description?: string;
useDedicatedIp?: boolean;
definition: TurboPipelineDefinition;
};
// createJob takes the same config — the client sets job: true for you.
export type CreateTurboJobConfig = Omit & {
definition: Omit;
};
export type RestartOptions = { clearState?: boolean };
export type GetLogsOptions = {
logLevels?: string; // comma-separated, e.g. "error,warn"
cursor?: number;
after?: number;
search?: string;
direction?: "asc" | "desc";
};
```
### Response types
```typescript theme={null}
export type TurboPipeline = {
name: string;
status?: string;
version?: number;
definition?: TurboPipelineDefinition & {
name?: string;
resource_size?: string;
description?: string;
use_dedicated_ip?: boolean;
};
created_at: string;
updated_at?: string;
};
export type TurboPipelineStatus = {
name: string;
status: string;
};
export type ValidateResult = {
valid: boolean;
errors?: Array<{ message: string } | string>;
warnings?: Array<{ message: string } | string>;
};
export type PipelineActionResult = {
name: string;
action: string;
};
export type PipelineDeleteResult = {
name: string;
deleted: boolean;
};
export type PipelineLogsResult = {
logs: unknown[];
};
```
The `definition` is the same [pipeline configuration](/turbo-pipelines/pipeline-config) you'd write in
YAML, expressed as an object. Reads (`getPipeline`, `getStatus`, `listPipelines`, `getLogs`,
`getState`, `validate`) retry on transient failures by default; the mutating methods do not. Every call
is recorded as a span on the task run, like [`ctx.fetch`](./fetch).
### Examples
#### Kick off a one-time backfill (job)
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ turbo }: TaskContext) {
const job = await turbo.createJob({
name: "solana-backfill",
resourceSize: "m",
definition: {
sources: {
token_transfers: {
type: "dataset",
dataset_name: "solana.token_transfers",
version: "1.0.0",
start_block: 250000000,
end_block: 250000002,
},
},
transforms: {
filtered: { type: "sql", primary_key: "id", sql: "SELECT * FROM token_transfers" },
},
sinks: {
out: { type: "postgres", from: "filtered", schema: "public", table: "token_transfers", secret_name: "MY_PG", primary_key: "id" },
},
},
});
return job;
}
```
#### Create a streaming pipeline in response to an event
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ turbo }: TaskContext, params: { contract: string }) {
// Sanitize the contract address for use in the pipeline name
const slug = params.contract.slice(2, 10).toLowerCase();
return turbo.createPipeline({
name: `transfers-${slug}`,
definition: {
sources: {
logs: { type: "dataset", dataset_name: "ethereum.raw_logs", version: "1.0.0", start_at: "latest" },
},
transforms: {
filtered: {
type: "sql",
primary_key: "id",
sql: `SELECT * FROM logs WHERE address = lower('${params.contract}')`,
},
},
sinks: {
out: { type: "postgres", from: "filtered", schema: "public", table: "transfers", secret_name: "MY_PG", primary_key: "id" },
},
},
});
}
```
#### Poll a job until it finishes
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ turbo }: TaskContext) {
const { status } = await turbo.getStatus("solana-backfill");
if (status === "STOPPED" || status === "FAILED") {
const logs = await turbo.getLogs("solana-backfill", { logLevels: "error" });
return { done: true, status, logs };
}
return { done: false, status };
}
```
Pair this with a [cron trigger](../task-triggers) to check on a long-running job on a schedule
instead of blocking a single task run.
#### Validate before deploying
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ turbo }: TaskContext) {
const result = await turbo.validate({
sources: { blocks: { type: "dataset", dataset_name: "ethereum.raw_blocks", version: "1.0.0" } },
transforms: { t: { type: "sql", primary_key: "id", sql: "SELECT * FROM blocks" } },
sinks: { out: { type: "blackhole", from: "t" } },
});
if (!result.valid) {
const messages = (result.errors ?? []).map((e) =>
typeof e === "string" ? e : e.message
);
throw new Error(messages.join("; "));
}
}
```
#### List, pause, and delete the pipelines your app owns
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ turbo }: TaskContext) {
const pipelines = await turbo.listPipelines();
for (const p of pipelines) {
await turbo.pausePipeline(p.name);
}
await turbo.deletePipeline("solana-backfill");
}
```
## Next Steps
Learn how Turbo pipelines, sources, transforms, and sinks work.
Run a pipeline as a one-time job that exits when it's done.
# Debugging
Source: https://docs.goldsky.com/compose/debugging
Debug Compose apps locally with chain forking, wallet impersonation, and per-task callTask invocation.
## Debugging locally
Logs and run events stream to the terminal where you run `goldsky compose start`. Output is formatted and colored so it's easy to scan.
### Chain forking
For testing against live on-chain state without spending gas, start your app with the `--fork-chains` flag:
```bash theme={null}
goldsky compose start --fork-chains
```
This creates in-memory forks of all chains you interact with, powered by [TEVM](https://tevm.sh). All wallets are automatically funded with test ETH, so you can freely test contract interactions. Your task code stays exactly the same as in production — no special dev code needed. See [Environments](./environments#forking-for-local-compose-development) for more details.
#### Impersonating wallet addresses
If you need to test privileged contract methods that are restricted to a specific address (e.g. an owner or admin), you can impersonate that address on the fork without needing the private key:
```bash theme={null}
goldsky compose start --fork-chains --impersonate "my-wallet=0xOwnerAddress"
```
Any wallet whose name matches the mapping will resolve to the impersonated address and all contract interactions will execute as that address on the local fork. Your task code doesn't change at all. See [Impersonated wallets](./context/evm/wallets#impersonated-wallets) for full details.
### Testing individual tasks
`callTask` targets your deployed app by default. To test a task against your locally running app, pass `--env local`:
```bash theme={null}
goldsky compose callTask my_task '{"key": "value"}' --env local
```
The payload must be valid JSON. The local port is resolved automatically (the `--port` flag, then `.compose/.port`, then 4000), so this works even when `start` picked a fallback port.
## Debugging a Deployed Compose App
Once your app is running in the cloud, debug it from its details page in the web app at `https://app.goldsky.com/dashboard/compose/{appName}`. From there you can view logs and inspect task run records. See [Monitoring your app via the webapp](./deploy-monitor#monitoring-your-app-via-the-webapp) for a walkthrough.
Every context function call (for example `evm.wallet()`, `evm.writeContract(...)`, `ctx.db.collection(...)`) is automatically captured as a run event and shown in the task run view.
### Using `context.logger` for run-aware logs
Use `context.logger` instead of `console.log` to get logs that are tagged with the task name and run ID. This unlocks a powerful debugging flow in the dashboard:
1. **Search for a log pattern** in the app-level Logs tab (e.g. search for "payout failed")
2. **Click "View run"** on any matching log entry to jump directly to that task run
3. **Inspect the full trace** — see every context function call, other logs, and the outcome of that specific run
Logs emitted with `context.logger` also appear in the **run-specific Logs tab**, so when you're looking at a single task run you see only the logs from that execution. See the full [logger reference](./context/overview#logger) for the API.
### Examples
```typescript theme={null}
import { TaskContext } from "compose";
export async function main(
{ evm, logger }: TaskContext,
params: { payouts: bigint[]; resultId: string }
) {
try {
const { payouts, resultId } = params;
logger.info("Reporting payouts", { resultId, payoutCount: payouts.length });
const wallet = await evm.wallet();
const { hash } = await wallet.writeContract(
evm.chains.polygon,
"0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts]
);
logger.info("Payouts reported", { hash });
return { hash };
} catch (error) {
if (error instanceof Error && error.message.includes("payout denominator already set")) {
logger.warn("Payout denominator already set, marking as resolved", { resultId });
} else {
logger.error("Error reporting payout", {
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
}
```
## Next Steps
Learn about deploying your app to the cloud for production use cases.
View the full CLI command reference
# Deploying and Monitoring
Source: https://docs.goldsky.com/compose/deploy-monitor
Deploy Compose apps with goldsky compose deploy and monitor runs, logs, and state through the Goldsky webapp.
Once you've built out your Compose App and tested it locally, you'll want to deploy it for production usage. Your app will be deployed to a fully isolated runtime environment
with a dedicated database. Environment variables and secrets you've provided will be available to your app, and our durability engine will ensure task execution and
resumption across restarts and rolling deploys. You'll be able to monitor your app via the Goldsky webapp.
### The `deploy` command
Deployment is done by running this command with a reference to your manifest file. The deploy command is idempotent and will handle upserting your app to the cloud.
```bash theme={null}
goldsky compose deploy
```
Your manifest must include an `api_version` field to deploy. This pins your app to a specific Compose runtime. Use `"stable"` if unsure. See [Release channels](./app-configuration#release-channels) for details.
The Compose CLI extension will handle provisioning the necessary resources and launching your app based on the configuration in your manifest. Once complete, cron tasks will start and HTTP triggers will be available. See [Task triggers](./task-triggers) for more information.
On success, `deploy` prints the URL of your app's dashboard.
### Hosted Postgres database
When you deploy, Compose automatically provisions a dedicated Postgres database for your app. This database is used for two things:
* **Your state.** Any [collections](./context/collections) you create in your tasks are stored here. You can query or inspect your own data directly.
* **Compose internal state.** Compose uses the same database to power durable execution (resuming tasks cleanly after crashes and rolling deploys), reorg monitoring for transactions, and wallet bookkeeping. These internal tables live in reserved namespaces and are managed by the runtime.
Because your app has its own database, your data is fully isolated from other apps and from Goldsky's own systems.
### Monitoring your app via the webapp
Like with our indexing products, you'll see your Compose Apps in the Goldsky webapp. From there you can view the app's status, recent task runs (with per-operation details — each `ctx.*` call a task made), and raw log output.
The URL for your app's dashboard is:
```
https://app.goldsky.com/{projectId}/dashboard/compose/{appName}
```
#### Viewing and downloading deployed source
When you deploy, the CLI uploads your app's full local source tree: the transitive local import closure, `package.json` and lockfile, `tsconfig.json`, and everything under `src/contracts/`. `node_modules`, `.env*`, and files that look like keys or secrets are excluded.
The dashboard's Code tab is a file browser over that source tree. The Download app button produces a zip that is a runnable local Compose project, including a names-only `.env.example`.
Apps deployed before source capture existed show per-task compiled bundles instead of original sources. Redeploy with a current CLI to get full fidelity. Very large projects may skip source capture, in which case the CLI warns at deploy time and the dashboard falls back to the same per-task bundles.
The CLI equivalents are [`goldsky compose source`](./cli-reference#inspecting-a-deployed-app) and [`goldsky compose download`](./cli-reference#inspecting-a-deployed-app); see [App lifecycle](./app-lifecycle#view-and-download-deployed-source) for the workflow.
### Managing a deployed app from the CLI
For day-to-day app management from the terminal, use the [lifecycle commands](./app-lifecycle):
```bash theme={null}
goldsky compose status # runtime status
goldsky compose list # all apps in your project
goldsky compose logs -f # tail logs
goldsky compose pause # stop triggers without deleting state
goldsky compose resume
goldsky compose delete # delete the app (and optionally the database)
```
### Deleting a Compose app
You can delete a Compose app either from the webapp (navigate to the app's dashboard and click **Delete**) or from the CLI:
```bash theme={null}
goldsky compose delete
```
Both paths require you to type the app name to confirm. See [App lifecycle](./app-lifecycle#delete-an-app) for CLI flags.
#### Database management options
When deleting a Compose app that uses a hosted Postgres database, you can choose whether to keep or delete the associated database:
* **Delete associated database**: The hosted Postgres database will be permanently deleted along with the app and all its data. This is the default in the webapp modal ("Delete associated database" is pre-checked). From the CLI, pass `--delete-database` or answer the interactive prompt to opt in.
* **Keep database**: Preserve the database when deleting the app. This is useful if you want to retain your data for analysis or migrate it to another app. This is the default in the CLI; in the webapp, uncheck "Delete associated database" before confirming.
Database deletion is permanent and cannot be undone. Make sure to back up any important data before deleting.
#### Pipeline validation
If you choose to delete the associated database, the system will check whether the database is being used by any active pipelines. If the database is referenced by one or more pipelines, the deletion will be blocked and you'll see an error message listing the affected pipelines.
To proceed with database deletion in this case, you must first:
1. Delete or modify the pipelines that reference the database
2. Then retry deleting the Compose app with the database option selected
Alternatively, you can keep the database by unchecking the "Delete associated database" option, which allows you to delete the app while preserving the database for use by the existing pipelines.
# Environments and RPCs
Source: https://docs.goldsky.com/compose/environments
Configure Compose for local chains, forked networks, testnets, and mainnets across the development lifecycle.
We covered how to set your own [Environment Variables](./app-configuration#env-variables) in the manifest, but you'll likely need to work with smart
contracts locally and on testnets. Additionally you'll likely need to interact with APIs, both your own and third party ones, with different credentials
and different URLs throughout the development lifecycle.
Compose supports two manifest environments: `local` (used when you run `goldsky compose start` or `goldsky compose dev`) and `cloud` (used when your app
runs on Compose's infrastructure after `goldsky compose deploy`). The active environment is picked automatically based on whether the runtime is executing
locally or in the cloud — there is no `--env` flag on `deploy`.
## Chains and RPCs
There are four primary chain environments Compose is equipped to support: fully local (Foundry, Truffle, Hardhat, etc), locally forked networks (powered by TEVM),
testnets and mainnets.
### Local chains (Foundry, Truffle, Hardhat)
If you're developing your compose app locally alongside a smart contract running on a local chain, you can customize the RPC endpoints that Compose uses to read from
and write to your contract. You can use hard coded values right in the code, or env variables configured in your [Manifest](./app-configuration#env-variables).
#### Use a local RPC node in code
If you're just starting out your contract with local development, and building your Compose app at the same time, you can use a Custom chain object right in code.
For full reference in custom chain configuration, go [here](./context/evm/chains). For full reference on interacting with contracts, go [here](./context/evm/contracts).
```typescript theme={null}
import { TaskContext, Chain } from "compose";
export async function main({ evm }: TaskContext) {
// This is a local-only private key I've funded on my test chain (thus safe to hard code)
// For private keys used on mainnets and testnets, always use Secrets
const privateKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const wallet = await evm.wallet({ privateKey });
const localChain: Chain = {
id: 0,
name: "foundry-local",
testnet: true,
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["http://127.0.0.1:8545"] }, // this is the url used by your local Anvil (etc) node
public: { http: ["http://127.0.0.1:8545"] },
},
blockExplorers: {
default: { name: "na", url: "http://127.0.0.1:8545" }, // not used for local dev, so you can pass in any value here
},
};
const localContractAddress = "0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`;
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash } = await wallet.writeContract(
localChain,
localContractAddress,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 3,
}
);
}
```
#### Use a local RPC only in the dev environment
If you already have your contract and compose app deployed but are iterating on the contract and the Compose App locally, you can use env variables
to override the RPC only when running locally.
### Manifest
```yaml theme={null}
name: "my_app"
env: # since we're only specifying a local version of the RPC_URL env var, it'll be undefined when deployed to cloud
local:
# This is a local-only private key I've funded on my test chain (thus safe to hard code)
# For private keys used on mainnets and testnets, always use Secrets
PRIVATE_KEY: "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
RPC_URL: "http://127.0.0.1:8545"
CONTRACT_ADDRESS: "0x67206e6E82FA1b11fd8C545Ad3422dBb1444E53C" # we can update this as we iterate on the contract locally
cloud:
CONTRACT_ADDRESS: "0x1234567890abcdef1234567890abcdef12345678" # we can update this when we deploy our contract to mainnet
tasks:
- name: "bitcoin_oracle"
path: "./src/tasks/bitcoin_oracle.ts"
triggers:
- type: "cron"
expression: "* * * * *"
```
Task code
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
// private key will be undefined in cloud (since env var isn't set for cloud) and the wallet will default to our auto-funded smart wallets
// however you can use a mainnet private key if you want by saving it as a secret, see wallet docs for more info
const wallet = await evm.wallet({ privateKey: env.PRIVATE_KEY });
const chain = {
...evm.chains.base,
};
// override the chain's RPC only if the env var is set
if (env.RPC_URL) {
chain.rpcUrls = {
default: { http: [env.RPC_URL] }, // this is the url used by your local Anvil (etc) node
public: { http: [env.RPC_URL] },
};
}
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash } = await wallet.writeContract(
chain,
env.CONTRACT_ADDRESS as `0x${string}`, // use the env var for the contract address which we'll update as we iterate
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 3,
}
);
}
```
### Forking for local Compose development
If you're just iterating on your Compose app but your smart contract isn't changing, then a good option for local development can be to use TEVM for forking.
This is done by starting your app with the `--fork-chains` option like so: `goldsky compose start --fork-chains`. When you use forking, everything in your code will be exactly
the same locally as in cloud, but internally we'll fork all the chains you interact with and we'll fund all your wallets on the local fork for gas. This allows
you to freely iterate on your compose app while testing against a cloned contract and its state.
You can also impersonate specific wallet addresses on the fork to test privileged contract methods without needing the private key — see [Impersonated wallets](./context/evm/wallets#impersonated-wallets) for details.
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm }: TaskContext) {
// we'll auto-fund all smart wallets in forking mode so you can simulate gas sponsoring in prod
const wallet = await evm.wallet({ name: "my-smart-wallet" });
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash } = await wallet.writeContract(
// when you run compose with the --fork-chains flag we'll make a fork of base at the time of running the app and we'll clone all your existing contract state
// when you deploy this to cloud, it'll run against the actual base mainnet, thus you don't need any special dev code when testing locally with forking
evm.chains.base,
"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 3,
}
);
}
```
### BYO RPCs for mainnets and testnets
By default, Compose uses our internal [edge RPCs](https://erpc.cloud/edge) and our gas-sponsored [smart wallets](./context/evm/wallets), so you don't need to worry about wallets,
RPCs or gas funding, while giving your app the most optimal performance and durability. However, there may be times when you want to use your own RPC nodes
or wallets, which is also fully supported. Below is an example of using your own RPCs and private keys. See [Wallets](./context/evm/wallets) and [Chains](./context/evm/chains)
for more details.
### Manifest
```yaml theme={null}
name: "my_app"
secrets:
# set these with "goldsky compose secret set --value " prior to deploying, see secrets docs for more info
- PROD_FUNDING_WALLET
- ALCHEMY_TOKEN
env:
local:
ALCHEMY_BASEURL: "https://base-testnet.g.alchemy.com/v2/"
cloud:
ALCHEMY_BASEURL: "https://base-mainnet.g.alchemy.com/v2/"
tasks:
- name: "bitcoin_oracle"
path: "./src/tasks/bitcoin_oracle.ts"
triggers:
- type: "cron"
expression: "* * * * *"
```
Task code
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ evm, env }: TaskContext) {
// env.PROD_FUNDING_WALLET was populated via the secrets reference in the manifest
const wallet = await evm.wallet({ privateKey: env.PROD_FUNDING_WALLET });
const chain = {
...evm.chains.base,
rpcUrls: {
default: { http: [`${env.ALCHEMY_BASEURL}${env.ALCHEMY_TOKEN}`] },
public: { http: [`${env.ALCHEMY_BASEURL}${env.ALCHEMY_TOKEN}`] },
},
};
const resultId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
const payouts = [1000n, 2000n, 3000n];
const { hash } = await wallet.writeContract(
chain,
"0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`,
"reportPayouts(bytes32,uint256[])",
[resultId, payouts],
{
confirmations: 3,
}
);
}
```
## Other environment use cases
Another common need for different environments is interacting with your own, or third party, APIs. For example, you may need to test your compose app
against a locally running version of your API for local development. This is pretty straight forward when configuring env vars in your [Manifest](./app-configuration#env-variables).
### Manifest
```yaml theme={null}
name: "my_app"
env:
local:
API_BASE: "http://localhost:4001"
cloud:
API_BASE: "https://api.mydomain.com"
tasks:
- name: "bitcoin_oracle"
path: "./src/tasks/bitcoin_oracle.ts"
triggers:
- type: "cron"
expression: "* * * * *"
```
Task code
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ env, fetch }: TaskContext) {
const apiEndpoint = `${env.API_BASE}/some/endpoint`;
const resp = await fetch(apiEndpoint);
}
```
# Migrate from Gelato W3F
Source: https://docs.goldsky.com/compose/gelato
Migrate from Gelato Web3 Functions to Compose, including triggers, task authoring, wallets, and monitoring.
Compose and Gelato Web3 Functions (W3F) have a lot of similarities and solve similar problems, but they work a bit differently. For the most part, anything you've done with W3F can be done in a similar fashion with Compose, but there are some differences to be aware of.
## Terminology
A few naming differences to keep in mind:
* Task — In Compose a "task" is a TypeScript file with your code logic. It can import from other files and libraries and must export a single `main` function. This is effectively the equivalent of a "function" in W3F terms.
* Trigger — In Compose a "trigger" encompasses the concept of both "triggers" and "tasks" in W3F.
## Configuration
Compose apps are completely configured within your code repository. All configuration is done in the [manifest](./app-configuration) YAML file (`compose.yaml`). Compose apps are deployed with the CLI, and the manifest is responsible for provisioning infra and dependencies.
## Triggers
Compose has similar triggering options to W3F with a few differences:
* Compose doesn't differentiate between cron and time intervals — it just uses cron expressions for time-based triggering.
* Compose has HTTP triggers, allowing your application logic to trigger tasks and send payloads over HTTP with optional bearer token authentication.
* Like W3F, Compose tasks can be triggered by onchain events. You declare them directly in your manifest with an `onchain_event` trigger (network, contract address, event signatures) — no separate pipeline to configure. See [Task triggers](./task-triggers#chain-event-triggers).
* For more complex event-driven flows than a single contract's events can express, you can build a Turbo pipeline with a webhook sink pointed at a Compose HTTP trigger.
* Compose doesn't support per-block triggers out of the box. You can recreate this with a Turbo pipeline (webhook sink) that indexes every block and posts to an HTTP-triggered task.
## Task authoring and output
All task authoring is done in TypeScript for Compose apps — we do not currently support authoring tasks in Solidity like W3F. Unlike W3F, Compose tasks don't output onchain transactions by default. They can return a JSON payload when called through an HTTP trigger, but all other effects are done inside the task code. Tasks can execute any number of onchain transactions, make HTTP calls to external systems, store data in collections for use later or by other tasks, and so on. Since external interactions happen in code rather than as task output, Compose apps can include any number of them.
## Monitoring
Both platforms have a web UI dashboard for monitoring, logs, and so on. Compose also has an event log that gives details about task execution steps and failures. This is useful for both auditing and debugging the behavior of Compose apps. The event log is built to be publicly shareable as an audit trail, but currently it's only visible to users logged into a Goldsky project at app.goldsky.com.
Compose does not currently have a notification system for app errors beyond proactively checking the UI and our own internal monitoring and on-call rotation for platform issues. However, logging can be set up within Compose apps to send to external systems. Compose doesn't make assumptions about what constitutes a failure within a task run, allowing users to manually define success/failure conditions and wire up logging to existing systems. We are designing a system for task logic to define its own critical failure conditions, which would let us use our existing email notification system and flag Compose apps with critical failures in the dashboard. If this is a strict requirement, please contact us with your specific requirements.
## Wallets, gas, and compute funding
Gas funding in Goldsky doesn't rely on users depositing gas tokens in our system. Instead, transactions are sent from wallets created in code, and any number of wallets can be created within your Compose app. By default, wallets are Privy-managed smart wallets with built-in gas sponsorship enabled — gas is metered as you use it and paid for as part of your monthly Goldsky bill. Compose also lets you store your own EOA private keys as secrets and use those to create wallets. EOA wallets default to paying their own gas (so you must keep them funded), but you can opt into gas sponsorship per wallet by passing `sponsorGas: true` to `evm.wallet({...})` — see the [wallets reference](./context/evm/wallets). Compute costs are included in your monthly Goldsky bill, paid in fiat.
# Build a Bitcoin price oracle
Source: https://docs.goldsky.com/compose/guides/build-a-bitcoin-oracle
Build an on-chain Bitcoin price oracle using Compose, CoinGecko, and contract codegen
This guide walks you through building a Bitcoin price oracle that fetches the BTC/USD price every minute and writes it on-chain. It demonstrates cron triggers, contract codegen, external API calls, and collection storage.
## How it works
```mermaid theme={null}
flowchart LR
A[Cron Trigger] -->|"every minute"| B[Compose Task]
B -->|"fetch price"| C[CoinGecko API]
C -->|"BTC/USD price"| B
B -->|"oracle.write(timestamp, price)"| D[On-chain Contract]
B -->|"insertOne"| E[Collection]
```
1. **Cron trigger** fires every minute
2. **CoinGecko API** provides the current BTC/USD price
3. **On-chain contract** receives the price via a typed contract class
4. **Collection** stores the price for historical queries
## Prerequisites
* [Goldsky CLI installed](/installation)
## Project structure
```text theme={null}
bitcoin-oracle/
├── compose.yaml # Compose configuration
├── tsconfig.json # TypeScript config
├── src/
│ ├── contracts/
│ │ └── PriceOracle.json # Contract ABI (generates typed class)
│ ├── lib/
│ │ └── utils.ts # toBytes32 helper
│ └── tasks/
│ └── bitcoin-oracle.ts # Main task
```
## Step 1: Set up the project
Clone the example repository:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/bitcoin-oracle
```
## Step 2: Generate contract types
The project includes a `PriceOracle.json` ABI in `src/contracts/`. Generate the typed contract class:
```bash theme={null}
goldsky compose codegen
```
This creates a typed `PriceOracle` class in `.compose/generated/` that provides type-safe contract interaction.
## Step 3: Understand the task
The `bitcoin-oracle.ts` task handles everything:
```typescript theme={null}
import { TaskContext } from "compose";
import { toBytes32 } from "../lib/utils";
const ORACLE_CONTRACT = "0x34a264BCD26e114eD6C46a15d0A3Ba1873CaA708";
export async function main(context: TaskContext) {
const { fetch, evm, collection } = context;
const wallet = await evm.wallet({ name: "bitcoin-oracle-wallet" });
// Instantiate the typed contract (generated from src/contracts/PriceOracle.json)
const oracle = new evm.contracts.PriceOracle(
ORACLE_CONTRACT,
evm.chains.polygonAmoy,
wallet
);
// Fetch Bitcoin price from CoinGecko API
const response = await fetch<{ bitcoin: { usd: number } }>(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
{
max_attempts: 3,
initial_interval_ms: 1000,
backoff_factor: 2,
}
);
if (!response) {
throw new Error("Failed to fetch Bitcoin price");
}
const bitcoinPrice = response.bitcoin.usd;
const timestamp = Date.now();
// Convert to bytes32 format and write on-chain
const timestampAsBytes32 = toBytes32(timestamp);
const priceAsBytes32 = toBytes32(Math.round(bitcoinPrice * 100));
const { hash, receipt } = await oracle.write(timestampAsBytes32, priceAsBytes32);
// Store in a collection for historical queries
const priceHistory = await collection("bitcoin_prices");
const { id } = await priceHistory.insertOne({
price: bitcoinPrice,
timestamp: timestamp,
});
return {
success: true,
oracleHash: hash,
price: bitcoinPrice,
timestamp,
priceId: id,
};
}
```
### Key Compose features used
* **`context.fetch`** — HTTP requests with built-in retry and backoff
* **`evm.wallet`** — managed wallet with gas sponsorship
* **`evm.contracts.PriceOracle`** — typed contract class generated from ABI JSON via `compose codegen`
* **`collection`** — persistent document storage for price history
## Step 4: Configure the Compose app
The `compose.yaml` uses a cron trigger to run every minute:
```yaml theme={null}
name: "bitcoin-oracle"
api_version: "stable"
tasks:
- path: "./src/tasks/bitcoin-oracle.ts"
name: "bitcoin_oracle"
triggers:
- type: "cron"
expression: "* * * * *"
retry_config:
max_attempts: 2
initial_interval_ms: 1000
backoff_factor: 1
```
## Step 5: Run locally
```bash theme={null}
goldsky compose start --fork-chains
```
`--fork-chains` allows you to run a smart wallet locally. You can also use a private key wallet for your local Compose app. See more [here](/compose/secrets).
The task runs immediately and then every minute. You should see logs showing the fetched price and transaction hash.
## Step 6: Deploy to Goldsky
```bash theme={null}
goldsky compose deploy
```
## Customization
### Change the price source
Replace the CoinGecko URL with any API that returns a JSON price:
```typescript theme={null}
const response = await fetch<{ price: number }>(
"https://your-api.com/price",
{ max_attempts: 3, initial_interval_ms: 1000, backoff_factor: 2 }
);
```
### Use your own contract
1. Drop your contract's ABI JSON into `src/contracts/MyContract.json`
2. Run `goldsky compose codegen` to generate the typed class
3. Use it in your task:
```typescript theme={null}
const myContract = new evm.contracts.MyContract(
"0xYOUR_CONTRACT_ADDRESS",
evm.chains.baseSepolia,
wallet
);
await myContract.yourMethod(arg1, arg2);
```
### Change the cron schedule
```yaml theme={null}
triggers:
- type: "cron"
expression: "*/5 * * * *" # every 5 minutes
```
## Resources
* [Compose introduction](/compose/introduction)
* [Contract codegen](/compose/context/evm/contracts)
* [Task triggers](/compose/task-triggers)
* [Collections](/compose/context/collections)
* [CoinGecko API](https://www.coingecko.com/en/api)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/bitcoin-oracle)
# Build a corporate-actions distributor
Source: https://docs.goldsky.com/compose/guides/build-a-corporate-actions-distributor
Pay N share-token holders pro-rata for a tokenized corporate action (dividend, coupon, rebate, airdrop) using Compose to orchestrate an on-demand Goldsky Turbo pipeline as the snapshot subroutine
This guide walks you through building a corporate-actions distributor: an HTTP-triggered Compose task that snapshots holders of a share token at an operator-supplied record block, then pays each holder their pro-rata share of an escrowed USDC pool. Compose orchestrates Goldsky Turbo as an ephemeral, on-demand subroutine. When a campaign is declared, the task spawns a one-shot [job-mode](/turbo-pipelines/job-mode) pipeline to snapshot holders, waits for it to finish, pays each holder, and deletes the pipeline. There is no always-on indexing.
## How it works
```mermaid theme={null}
flowchart LR
Op([Operator]) -->|POST campaignId, recordBlock, totalAmount| T[declare_campaign task]
T -->|1. approve + declare| C[DistributionCampaign.sol]
T -->|2. POST /api/v1/pipelines| P[Turbo job-mode pipeline]
P -->|3. backfill Transfers| DB[(share_balances_id
per-campaign table)]
T -->|4. poll /state every 2s| P
T -->|5. read snapshot via SQL| DB
T -->|6. pay each holder (25 concurrent)| C
T -->|7. read escrowRemaining| C
T -->|8. DELETE pipeline + drop table| P
```
One HTTP request drives the entire lifecycle. No cron, no off-chain handoff:
1. The operator declares a distribution by `POST`ing `{ campaignId, recordBlock, totalAmount }`. The task validates `recordBlock <= currentBlock`, approves USDC, and calls `DistributionCampaign.declare()`, which atomically pulls the escrow.
2. The same task spawns a job-mode Turbo pipeline filtered to the share-token contract over `block_number BETWEEN AND `. The planner prunes everything outside that window. The sink writes raw `Transfer` rows into a per-campaign Postgres table.
3. The task polls `/state` every 2 seconds until the pipeline reports `completed` (or its k8s deployment auto-cleans up after a successful run, which we infer from `state=unknown` plus the table having rows).
4. The task reads the snapshot with a SQL aggregate over the raw rows (`SUM(credits) - SUM(debits)` per account) and computes pro-rata. Each holder gets `floor(balance × totalAmount / totalSupply)`. The floor remainder goes to the last holder so the sum equals `totalAmount` exactly.
5. The task fires up to 25 sponsored `pay()` calls concurrently via `Promise.allSettled`. Already-paid holders are filtered out by an on-chain `isPaid()` read first. The contract's `require(!paid[id][holder])` guard means duplicates are structurally impossible anyway.
6. The task re-reads `escrowRemaining` on-chain to confirm completion. Zero means done: mark the campaign `complete`, `DELETE` the pipeline, drop the per-campaign table. Non-zero means the operator can re-POST the same `campaignId` to resume.
Re-posting the same `campaignId` after any kind of failure picks up cleanly. The contract is the sole source of truth for "did this holder get paid?", so Compose's state machine never has to be.
## Prerequisites
* [Goldsky CLI installed](/installation)
* [Foundry](https://book.getfoundry.sh/getting-started/installation) for the contract deploys
* A small amount of ETH on Base mainnet for the three contract deploys (around 0.0005 ETH)
* A project API key for `goldsky compose deploy -t ` and a separate (or same) key set as the `GOLDSKY_PROJECT_KEY` secret so the running app can manage Turbo pipelines
## Project structure
```text theme={null}
corporate-actions/
├── compose.yaml # 1 HTTP task; declares GOLDSKY_PROJECT_KEY secret
├── contracts/
│ ├── ShareToken.sol # Minimal ERC-20, pre-mints to 25 demo holders
│ ├── MockUSDC.sol # 6-decimal mock with permissionless mint
│ └── DistributionCampaign.sol # AlreadyPaid guard, escrow, audit events
├── scripts/
│ ├── seed-holders.json # 25 demo holders, uneven amounts
│ └── deploy.sh # forge create x3 in one go
└── src/
├── lib/
│ ├── constants.ts # CONFIG, polling cadence, concurrency
│ ├── turbo.ts # /api/v1/pipelines client + snapshot pipeline builder
│ ├── db.ts # Neon HTTP /sql client (via context.fetch)
│ └── driver.ts # state-machine driver: snapshot → paying → complete
└── tasks/
└── declare-campaign.ts # HTTP trigger; drives full lifecycle inline
```
## Step 1: Set up the project
Clone the example repository:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/corporate-actions
```
## Step 2: Understand the task
`declare-campaign.ts` is the only task. It validates input, approves USDC, declares the campaign on-chain, spawns the pipeline, and drives the campaign through its state machine inside the HTTP request:
```typescript theme={null}
export async function main(context: TaskContext, params?: DeclareParams) {
const { evm, collection } = context;
// Idempotent on campaignId. A second POST drives an existing campaign
// forward instead of declaring a new one.
const campaigns = await collection("campaigns", [
{ path: "status", type: "text" },
]);
const existing = await campaigns.getById(params.campaignId.toLowerCase());
if (existing) {
await driveCampaign(context, campaigns, existing);
return responseFor(existing, "resumed");
}
// Approve + declare on-chain (atomic escrow pull).
const wallet = await evm.wallet({ name: "corp-actions-operator", sponsorGas: true });
await wallet.writeContract(chain, payToken, "approve(address,uint256)", [campaignContract, totalAmount]);
const { hash } = await wallet.writeContract(chain, campaignContract,
"declare(bytes32,address,address,uint256)",
[campaignId, payToken, shareToken, totalAmount]);
// Spawn the snapshot pipeline.
const pipeline = await createSnapshotPipeline(context, { campaignId, shareToken, recordBlock });
// Persist campaign metadata, then drive the campaign through
// snapshot → paying → complete inline.
await campaigns.setById(rowId, { ...campaign, status: "snapshotting", pipelineName: pipeline.name });
await driveCampaign(context, campaigns, campaign);
return responseFor(campaign, "declared");
}
```
The lifecycle logic lives in `src/lib/driver.ts`. `driveCampaign` is a state-machine dispatcher: `snapshotting` polls the pipeline, then transitions to `paying`; `paying` reads the snapshot, fires `pay()` calls, then checks `escrowRemaining` to decide whether to mark `complete`.
### Key Compose features used
* **Compose orchestrating Turbo** via the v1 pipelines REST API. The task spawns, polls, and deletes the pipeline from inside its own HTTP handler.
* **Auto-provisioned hosted Neon DB.** Compose-cloud creates a per-app Neon project and a `CORPORATE_ACTIONS` project secret pointing at it. Turbo writes to that DB via the same secret. No glue code on either side.
* **Gas-sponsored writes.** `sponsorGas: true` on `evm.wallet()` means the operator wallet never holds ETH. Goldsky pays gas for every `approve()` and `pay()`.
* **`context.fetch` for everything external.** The Neon HTTP `/sql` client, the v1 pipelines API client, and the chain RPC reads all go through `context.fetch`. That's the only `--allow-net` egress path the task gets.
* **Resumable on `campaignId`.** Compose's collection holds campaign metadata. The contract holds payment truth. Re-POSTing the same id drives the existing row forward.
## Step 3: Understand the contracts
`DistributionCampaign.sol` is the core piece. It holds USDC in escrow, gates payouts on a per-holder `paid` bitmap, and emits a rich audit event:
```solidity theme={null}
struct Campaign {
address operator;
address payToken;
address shareToken;
uint256 totalAmount;
uint256 escrowRemaining;
uint256 declaredAt;
bool declared;
bool sealed;
}
function declare(bytes32 userId, address payToken, address shareToken, uint256 totalAmount) external;
function pay(bytes32 id, address holder, uint256 amount, uint256 sharesAtSnapshot) external;
function isPaid(bytes32 id, address holder) external view returns (bool);
function seal(bytes32 id) external; // operator can recover unspent escrow
event HolderPaid(
bytes32 indexed id,
address indexed holder,
address indexed payToken,
uint256 amount,
uint256 sharesAtSnapshot
);
```
A holder can never be paid twice, regardless of what Compose does:
```solidity theme={null}
require(!paid[id][holder], "AlreadyPaid");
paid[id][holder] = true;
campaigns[id].escrowRemaining -= amount;
IERC20(payToken).transfer(holder, amount);
```
`ShareToken.sol` is a minimal ERC-20 that pre-mints to a demo holder set in its constructor. `MockUSDC.sol` is a 6-decimal mock with permissionless mint, so you can fund the operator without bridging real USDC.
## Step 4: Pre-create the operator wallet
The operator wallet is the address that calls `declare()` and `pay()` on the contract. Provision it ahead of the deploy so you can hard-code its address into the contract's deployment if you want to restrict who can call `declare()`:
```bash theme={null}
goldsky compose wallet create corp-actions-operator --env cloud
```
The command prints the wallet address. Save it for the next step.
## Step 5: Deploy the contracts
`scripts/deploy.sh` deploys all three contracts in one run. It reads `seed-holders.json` to pre-mint share-token balances to 25 demo addresses:
```bash theme={null}
PRIVATE_KEY=0x... ./scripts/deploy.sh
```
The script prints the three addresses and the ShareToken's deploy block. You need all four values in the next step.
## Step 6: Wire in the addresses
Open `src/lib/constants.ts` and replace the four constants:
```typescript theme={null}
export const CONFIG = {
chain: "base" as const,
shareToken: "0x..." as Hex, // from step 5
payToken: "0x..." as Hex, // MockUSDC from step 5
campaignContract: "0x..." as Hex, // from step 5
shareTokenDeployBlock: 45654954, // block number printed by deploy.sh
};
```
`shareTokenDeployBlock` matters for performance. The job-mode source has to be set to `start_at: "earliest"` (the source-level `end_block` would make it non-hybrid and Turbo refuses to run). So we anchor the scan window in the SQL filter instead: `block_number BETWEEN AND `. The planner prunes everything outside that window before it touches a row.
## Step 7: Set the project secret
The running compose app needs a Goldsky project API key so it can spawn, poll, and delete Turbo pipelines via the v1 API. Set it once:
```bash theme={null}
goldsky secret create GOLDSKY_PROJECT_KEY
```
You don't have to create the `CORPORATE_ACTIONS` secret. Compose-cloud provisions a Neon DB for the app on first deploy and creates that secret for you, pointed at the new DB.
## Step 8: Deploy to Goldsky
```bash theme={null}
goldsky compose deploy
```
The compose-cloud deploy will:
1. Provision a hosted Neon DB for the app.
2. Create the `CORPORATE_ACTIONS` project secret pointed at it.
3. Build and start the task pod. The HTTP trigger is now reachable at `https://api.goldsky.com/api/admin/compose/v1/corporate-actions/tasks/declare_campaign`.
## Step 9: Declare a campaign and verify
Fund the operator wallet with MockUSDC (the address was printed at the end of `goldsky compose wallet create`):
```bash theme={null}
cast send "mint(address,uint256)" 1000000000000 \
--rpc-url https://mainnet.base.org --private-key $PRIVATE_KEY
```
That mints 1,000,000 mUSDC, enough for many demo campaigns.
Pick a record block past finality, then POST:
```bash theme={null}
RECORD_BLOCK=$(cast block-number --rpc-url https://mainnet.base.org)
RECORD_BLOCK=$((RECORD_BLOCK - 32))
curl -sX POST "https://api.goldsky.com/api/admin/compose/v1/corporate-actions/tasks/declare_campaign" \
-H "content-type: application/json" \
-H "Authorization: Bearer $GOLDSKY_TOKEN" \
-d "{
\"campaignId\": \"0x000000000000000000000000000000000000000000000000000000000000c0a1\",
\"recordBlock\": $RECORD_BLOCK,
\"totalAmount\": \"10000000000\"
}"
```
That declares a 10,000 mUSDC distribution. The request stays open for roughly 10 to 30 seconds while compose snapshots, computes pro-rata, and fires the 25 `pay()` calls in a single batch. The response body includes the final campaign state (`complete` on the happy path, or `paying` if it needs another drive call).
Verify on-chain:
```bash theme={null}
cast call "getCampaign(bytes32)" \
--rpc-url https://mainnet.base.org
```
`escrowRemaining` is exactly 0 once all 25 holders are paid. The full audit trail is in the contract's `HolderPaid` events.
## Crash-safety walkthrough
The contract is the source of truth for "did this holder get paid?". Compose can crash any time and restart any time without double-paying or skipping a holder.
To verify:
1. POST a fresh campaign (new `campaignId`).
2. Mid-flight, pause the compose app: `goldsky compose pause`.
3. Resume: `goldsky compose resume`.
4. Re-POST the same `campaignId`. Within a single drive call, every remaining holder gets paid. There are zero duplicates on-chain. Verify by counting `HolderPaid` events for the campaign.
What's protecting you:
* **Compose's collection only stores campaign metadata** (status, declareTxHash, pipelineName, persisted payouts). It does not cache per-holder paid state, so there's no off-chain table that can diverge from on-chain truth.
* **Per-holder `isPaid()` check before each `pay()` call.** Already-paid holders are skipped before the tx is even attempted.
* **The contract's `require(!paid[id][holder], "AlreadyPaid")` guard.** Even if a stale tx arrives after restart, the contract rejects it. Double-pay is structurally impossible regardless of compose's state.
A pod kill mid-snapshot is also recoverable. Re-POSTing the same `campaignId` polls the existing pipeline. If the pipeline auto-cleaned up after success, we infer completion from the agg table having rows.
## Customization
### Why operator-supplied `recordBlock`
In real corporate actions, the record date is set in advance and is the cutoff for who gets the payout. The snapshot is by definition backwards-looking. So the operator passes an explicit `recordBlock`, typically a block past finality like `currentBlock - 32`. The pipeline backfills exactly that range and commits the snapshot. There's no live "wait for finality" gate inside compose. The operator already accommodated finality when they chose the block.
Future-dated record blocks (declare today, snapshot tomorrow) are a real corporate-action feature but out of scope for this demo.
### Swap the share token
Update `CONFIG.shareToken` and `CONFIG.shareTokenDeployBlock` in `src/lib/constants.ts` and redeploy the compose app. Each campaign's pipeline bakes in the address at declare time, so existing campaigns are unaffected.
### Add a chain
Add a `chain` discriminator to `Campaign` and `DeclareParams`, populate `CONFIG` with a per-chain map, and pass `chain` into `buildSnapshotPipeline` so the dataset name (`base.erc20_transfers` vs `arbitrum.erc20_transfers`) is parameterized.
### Use real USDC
Replace `MockUSDC.sol` with the real USDC address per chain in `CONFIG.payToken`. Real USDC isn't fee-on-transfer, so `escrowRemaining` math is exact.
### Tune concurrency
`CONCURRENCY` in `src/lib/constants.ts` is the upper bound on parallel `pay()` calls. The gas-sponsored bundler caps per-sender throughput around 1 to 5 userOps per second; the default of 25 is sized for the demo's 25-holder set firing in one visible batch. For larger distributions, drop it to keep the bundler happy.
## When NOT to use this pattern
This is push-based pro-rata. It scales comfortably up to roughly 100 holders per request. Beyond that, the right shape is a **merkle-claim contract** instead: the operator publishes one merkle root and holders pull. The compose plumbing (orchestrating Turbo for the snapshot, computing pro-rata, building the audit trail) carries over to either model. Only the on-chain shape changes.
## Resources
* [Compose introduction](/compose/introduction)
* [Task triggers](/compose/task-triggers)
* [EVM wallets & gas sponsoring](/compose/context/evm/wallets)
* [Turbo job-mode pipelines](/turbo-pipelines/job-mode)
* [CMTAT IncomeVault](https://github.com/CMTA/IncomeVault), Swiss-bank reference implementation for tokenized-equity dividends. The example's event schema is anchored on this convention.
* [ERC-1726 Dividend-Paying Token](https://github.com/Roger-Wu/erc1726-dividend-paying-token)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/corporate-actions)
# Build a multi-chain NAV oracle
Source: https://docs.goldsky.com/compose/guides/build-a-nav-oracle
Publish a tokenized fund's Net Asset Value to multiple chains on a schedule, with a Chainlink-compatible on-chain interface and an operator kill-switch
This guide walks you through building a Proof-of-Reserves / NAV oracle for a tokenized RWA fund. A single Compose task runs every 5 minutes, fetches a structured NAV bundle from a custodian endpoint, and publishes it to `ReserveAggregator` contracts on Base Sepolia and Arbitrum Sepolia in one run. The on-chain contract implements `AggregatorV3Interface`, so any existing Chainlink consumer can read it unchanged.
## How it works
```mermaid theme={null}
flowchart LR
A[Cron Trigger] -->|every 5 min| B[Compose Task]
B -->|GET| C[Custodian Endpoint]
C -->|"{totalNav, cash, tbills, repo, asOf, ripcord}"| B
B -->|updateNav| D[ReserveAggregator on Base Sepolia]
B -->|updateNav| E[ReserveAggregator on Arbitrum Sepolia]
```
Each cycle:
1. **Fetch** the NAV bundle from a custodian JSON endpoint. If the response carries `ripcord: true`, the task logs a skip message and returns without publishing.
2. **Scale** human USD numbers to 18-decimal fixed point, matching Chainlink's convention for USD-denominated feeds.
3. **Publish** to both chains independently via `Promise.allSettled`. A failure on one chain does not block the other — the next cron cycle reconciles.
## Prerequisites
* [Goldsky CLI installed](/installation)
* [Foundry](https://book.getfoundry.sh/getting-started/installation) for contract deploys
* Testnet ETH on Base Sepolia and Arbitrum Sepolia to deploy the contracts
## Project structure
```text theme={null}
nav-oracle/
├── compose.yaml # 1 cron task, 2 chains
├── mock-custodian.json # Default data source (swap for your API)
├── contracts/
│ └── ReserveAggregator.sol # AggregatorV3Interface-compatible publisher
└── src/
├── lib/
│ └── scaling.ts # USD → 18-decimal bigint helper
└── tasks/
└── nav-oracle.ts # The cron task
```
## Step 1: Set up the project
Clone the example repository:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/nav-oracle
```
## Step 2: Understand the task
`nav-oracle.ts` fetches the bundle, validates the ripcord, and publishes to both chains:
```typescript theme={null}
import type { TaskContext } from "compose";
import { toScaled18 } from "../lib/scaling";
const CUSTODIAN_URL =
"https://raw.githubusercontent.com/goldsky-io/documentation-examples/main/compose/nav-oracle/mock-custodian.json";
const BASE_SEPOLIA_AGGREGATOR = "0x8099A30Ac752f86C77A0e0210085a908ba6d02fE";
const ARBITRUM_SEPOLIA_AGGREGATOR = "0x02D9Df62B7AED15739D638B92BAcEA2ce4Cb3d70";
export async function main(context: TaskContext) {
const { fetch, evm } = context;
// sponsorGas: true lets Goldsky pay gas for every write, so the publisher
// wallet never needs to be funded.
const wallet = await evm.wallet({
name: "nav-oracle-publisher",
sponsorGas: true,
});
const bundle = await fetch(CUSTODIAN_URL, {
max_attempts: 3,
initial_interval_ms: 1000,
backoff_factor: 2,
});
// Ripcord: operator kill-switch. Skip cleanly — not an error.
if (bundle.ripcord) {
console.log(`Ripcord engaged for ${bundle.accountName} — skipping publish.`);
return { success: true, skipped: "ripcord" };
}
const args = [
toScaled18(bundle.cash),
toScaled18(bundle.tbills),
toScaled18(bundle.repo),
toScaled18(bundle.totalNav),
BigInt(Math.floor(new Date(bundle.asOf).getTime() / 1000)),
];
const signature = "updateNav(uint256,uint256,uint256,uint256,uint64)";
const results = await Promise.allSettled([
wallet.writeContract(evm.chains.baseSepolia, BASE_SEPOLIA_AGGREGATOR, signature, args),
wallet.writeContract(evm.chains.arbitrumSepolia, ARBITRUM_SEPOLIA_AGGREGATOR, signature, args),
]);
// ... (summarize per-chain results, throw only if both failed)
}
```
### Key Compose features used
* **Multi-chain writes from a single task** — one `wallet.writeContract` call per chain, issued in parallel via `Promise.allSettled`.
* **`sponsorGas: true`** — the publisher wallet is a Compose-managed Privy wallet whose transactions are gas-sponsored by Goldsky. No ETH needed on the publisher ever.
* **`context.fetch`** — retries the custodian call with exponential backoff before surfacing a failure.
* **Ripcord pattern** — the data source itself carries a boolean kill-switch the operator can flip out-of-band. Useful when the upstream has a known issue and you want the oracle to pause without a redeploy.
## Step 3: Understand the contract
`ReserveAggregator.sol` is a single-operator, `AggregatorV3Interface`-compatible publisher. The full bundle is stored in one struct; the scalar `totalNav` is exposed via the standard Chainlink reader so existing consumers work unchanged:
```solidity theme={null}
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer, // totalNav scaled to 18 decimals
uint256 startedAt, // asOf from the bundle
uint256 updatedAt,
uint80 answeredInRound
);
function latestBundle() external view returns (NavBundle memory);
function updateNav(
uint256 cash,
uint256 tbills,
uint256 repo,
uint256 totalNav,
uint64 asOf
) external; // onlyPublisher
```
Only the `publisher` address set in the constructor can call `updateNav`. That address is your Compose-managed wallet.
## Step 4: Pre-create the publisher wallet
Compose's wallet-create command provisions the named wallet in the cloud and prints its address, so you can pass that address as the `publisher` constructor argument before you deploy the contracts:
```bash theme={null}
goldsky compose wallet create nav-oracle-publisher --env cloud
```
The command prints the wallet address to stdout. Save it for the next step.
## Step 5: Deploy `ReserveAggregator` on both chains
Deploy to Base Sepolia:
```bash theme={null}
forge create contracts/ReserveAggregator.sol:ReserveAggregator \
--rpc-url https://sepolia.base.org \
--private-key $PRIVATE_KEY \
--broadcast --root . \
--constructor-args 0xYOUR_PUBLISHER_ADDRESS "Example RWA Fund I NAV / USD"
```
And Arbitrum Sepolia:
```bash theme={null}
forge create contracts/ReserveAggregator.sol:ReserveAggregator \
--rpc-url https://sepolia-rollup.arbitrum.io/rpc \
--private-key $PRIVATE_KEY \
--broadcast --root . \
--constructor-args 0xYOUR_PUBLISHER_ADDRESS "Example RWA Fund I NAV / USD"
```
Record both deployed addresses.
`--broadcast` must come *before* `--constructor-args` — forge treats `--constructor-args` as variadic, so any flag that follows it gets consumed as another positional argument and the transaction is never sent.
## Step 6: Wire in the addresses
Open `src/tasks/nav-oracle.ts` and replace the two address constants near the top with the ones you just deployed:
```typescript theme={null}
const BASE_SEPOLIA_AGGREGATOR = "0x..."; // from step 5
const ARBITRUM_SEPOLIA_AGGREGATOR = "0x..."; // from step 5
```
## Step 7: Deploy to Goldsky
```bash theme={null}
goldsky compose deploy
```
The task fires on the next 5-minute boundary. Watch it:
```bash theme={null}
goldsky compose logs
```
You should see a line like:
```text theme={null}
Published Example RWA Fund I NAV=$50,825,000 — base:ok, arb:ok
```
Verify on-chain by reading back `latestRoundData()`:
```bash theme={null}
cast call --rpc-url https://sepolia.base.org 0xYOUR_BASE_ADDRESS \
"latestRoundData()(uint80,int256,uint256,uint256,uint80)"
```
The `answer` field is the total NAV scaled to 18 decimals; `updatedAt` is the custodian's `asOf` timestamp.
## Customization
### Swap the data source
Change `CUSTODIAN_URL` to your own endpoint. Your API must return:
```json theme={null}
{
"accountName": "Your Fund",
"asOf": "2026-04-22T14:00:00Z",
"cash": 125000.00,
"tbills": 42500000.00,
"repo": 8200000.00,
"totalNav": 50825000.00,
"ripcord": false
}
```
Amounts are human-readable USD — the task scales to 18 decimals before writing on-chain.
### Add or swap chains
Add another `wallet.writeContract(...)` inside the `Promise.allSettled` block, targeting a different `evm.chains.` and contract address:
```typescript theme={null}
wallet.writeContract(evm.chains.optimismSepolia, OP_SEPOLIA_AGGREGATOR, signature, args),
```
Deploy a matching `ReserveAggregator` to that chain first.
### Change the publish cadence
Real Proof-of-Reserves / NAV feeds typically publish hourly or daily. Change the cron expression in `compose.yaml`:
```yaml theme={null}
triggers:
- type: "cron"
expression: "0 * * * *" # every hour
```
### Use the ripcord
Any host can flip the kill-switch by returning `"ripcord": true` from the custodian endpoint. The next task run logs `Ripcord engaged …` and skips the publish without erroring — Compose's retry logic is not triggered. Flip it back to `false` and publishing resumes on the following cycle.
### Rotate the publisher
If you need to re-create the Compose wallet or move to a different key, call `setPublisher(newAddress)` from the current publisher. The old wallet loses `updateNav` permissions; only the new one can publish.
## Resources
* [Compose introduction](/compose/introduction)
* [Task triggers](/compose/task-triggers)
* [EVM wallets & gas sponsoring](/compose/context/evm/wallets)
* [Chainlink `AggregatorV3Interface` reference](https://docs.chain.link/data-feeds/api-reference#aggregatorv3interface)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/nav-oracle)
# Build a Polymarket copy-trader
Source: https://docs.goldsky.com/compose/guides/build-a-polymarket-copy-trader
Mirror Polymarket wallets' trades automatically using Compose and Turbo with a webhook pipeline
This guide walks you through building a Polymarket copy-trading bot. A Turbo pipeline decodes on-chain `OrderFilled` events, filters them to a configurable set of wallets, and webhooks each fill into a Compose app that signs and submits the same trade to the Polymarket CLOB. Winning shares auto-redeem on a 5-minute cron.
It demonstrates the Turbo → Compose webhook pattern, cron triggers, sponsored-gas wallets, `ctx.fetch` for external APIs, and collection-based state.
The example targets [Polymarket V2](https://docs.polymarket.com/v2-migration) (cutover 2026-04-28). V2 introduced new Exchange contracts on Polygon, switched collateral from USDC.e to **pUSD** (a 1:1 ERC-20 backed by USDC.e), and ships a new SDK at `@polymarket/clob-client-v2`. Funding still happens in USDC.e — the `setup_approvals` task wraps it into pUSD via the Collateral Onramp.
## How it works
```mermaid theme={null}
flowchart LR
A[Polygon on-chain] -->|"OrderFilled events"| B[Turbo Pipeline]
B -->|"decode + filter to watched wallets"| B
B -->|"webhook per fill"| C[Compose: copy_trade]
C -->|"sign + POST order"| D[Fly.io Proxy]
D -->|"forward to CLOB"| E[Polymarket CLOB]
F[Cron every 5m] --> G[Compose: redeem]
G -->|"redeemPositions"| H[ConditionalTokens contract]
```
1. **Polygon** emits `OrderFilled` from the V2 CTF Exchange (`0xE111…996B`) and V2 NegRisk Exchange (`0xe222…0F59`) contracts
2. **Turbo pipeline** decodes fills, keeps only trades where maker or taker is in your watched list, and posts each to a Compose HTTP task
3. **`copy_trade`** parses the fill, checks the wallet's pUSD balance, looks up market metadata, signs a V2 FAK (Fill-and-Kill) order, and submits it via the Fly.io proxy (Polymarket's CLOB is geo-blocked from US hosts)
4. **`redeem`** runs every 5 minutes, polls Polymarket's data API for redeemable positions, and calls `redeemPositions` on-chain
## Prerequisites
* [Goldsky CLI installed](/installation)
* A Compose API token from your Goldsky project (for the webhook auth secret)
* An EOA private key for the bot's wallet — no Polymarket UI onboarding or proxy wallet required
* USDC.e (`0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174`) on Polygon to fund the bot — the `setup_approvals` task wraps it into pUSD before trading
* A list of wallets you want to mirror (selecting them is out of scope for this guide)
## Project structure
```text theme={null}
copy-trader/
├── compose.yaml # Compose app + env config
├── tsconfig.json # TypeScript config
├── package.json # npm deps (bundled by Compose CLI)
├── pipeline/
│ └── polymarket-ctf-events.yaml # Turbo pipeline → webhook sink
└── src/
├── lib/
│ ├── clob.ts # Polymarket CLOB client
│ ├── gamma.ts # Market metadata lookups
│ └── types.ts # Contract addresses + types
└── tasks/
├── copy_trade.ts # HTTP: mirror a fill
├── redeem.ts # Cron: redeem winning shares
└── setup_approvals.ts # HTTP: one-time approvals
```
## Step 1: Set up the project
Clone the example repository and install dependencies:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/copy-trader
npm install
```
The Compose CLI bundles tasks with esbuild against `node_modules/`, so `npm install` is required before `goldsky compose start` or `deploy`.
## Step 2: Pick wallets to copy and update both configs
The pipeline pre-filters on-chain events to the watched list, so the same addresses must appear in two places.
In `compose.yaml`:
```yaml theme={null}
env:
cloud:
WATCHED_WALLETS: "0xwhale1,0xwhale2"
```
In `pipeline/polymarket-ctf-events.yaml`, update the `watched_fills` transform:
```yaml theme={null}
watched_fills:
type: sql
primary_key: id
sql: |
SELECT * FROM order_fills
WHERE maker IN (
'0xwhale1',
'0xwhale2'
)
OR taker IN (
'0xwhale1',
'0xwhale2'
)
```
## Step 3: Set the wallet private key
The Compose app signs CLOB orders as an EOA. No Polymarket proxy wallet is involved:
```bash theme={null}
goldsky compose secret set PRIVATE_KEY --value "0x..."
```
## Step 4: Create the webhook auth secret
The Turbo webhook authenticates to your Compose app with a Goldsky-level secret. This is a one-time setup per project:
```bash theme={null}
goldsky secret create --name COMPOSE_WEBHOOK_AUTH \
--value '{"type": "httpauth", "secretKey": "Authorization", "secretValue": "Bearer YOUR_COMPOSE_API_TOKEN"}'
```
The pipeline references this secret by name in its webhook sink, so every pipeline in the project can reuse it.
## Step 5: Deploy the app and pipeline
```bash theme={null}
goldsky compose deploy
goldsky turbo apply pipeline/polymarket-ctf-events.yaml
```
The pipeline starts at the latest Polygon block, so fills produced before you deployed will not be backfilled.
## Step 6: Fund the wallet
Send USDC.e to the EOA that corresponds to the private key from step 3. Compose sponsors gas on all on-chain calls, so you do not need MATIC in the wallet.
## Step 7: Grant approvals and wrap collateral
V2 markets settle in pUSD, so the bot needs to (a) approve the Collateral Onramp to spend USDC.e, (b) wrap the wallet's USDC.e balance into pUSD, and (c) approve pUSD + ConditionalTokens to both V2 Exchanges. The `setup_approvals` task does all of this in one call. Compose sponsors every transaction:
```bash theme={null}
curl -X POST -H "Authorization: Bearer $COMPOSE_TOKEN" \
https://api.goldsky.com/api/admin/compose/v1/copy-trader/tasks/setup_approvals
```
The task is idempotent: re-call it after every USDC.e top-up to wrap the new balance. Approvals are max-allowance, so re-approving is a cheap no-op.
The bot begins trading as soon as the next fill on a watched wallet lands in the pipeline.
## Understanding the code
### Parsing a fill
V2's `OrderFilled` event encodes the maker's side as a `uint8` (`0 = BUY`, `1 = SELL`) and exposes a single `tokenId` instead of the V1 maker/taker asset-id pair. The whale we want to copy can be the maker or the taker — the taker takes the opposite side:
```typescript theme={null}
function parseFill(
row: OrderFillRow,
watchedWallets: Set
): { side: "BUY" | "SELL"; tokenId: string; whalePrice: number } {
const makerIsWhale = watchedWallets.has(row.maker.toLowerCase());
const takerIsWhale = watchedWallets.has(row.taker.toLowerCase());
// Maker's side as encoded in the event: "0" = BUY, "1" = SELL.
const makerSide: "BUY" | "SELL" = row.side === "0" ? "BUY" : "SELL";
const oppositeSide: "BUY" | "SELL" = makerSide === "BUY" ? "SELL" : "BUY";
const whaleSide = makerIsWhale ? makerSide : oppositeSide;
// Price = pUSD per share. Layout depends on the maker's side.
// makerSide = BUY: makerAmount = pUSD, takerAmount = shares
// makerSide = SELL: makerAmount = shares, takerAmount = pUSD
const usdcAmount = makerSide === "BUY" ? row.maker_amount : row.taker_amount;
const sharesAmount = makerSide === "BUY" ? row.taker_amount : row.maker_amount;
const price = sharesAmount > 0 ? usdcAmount / sharesAmount : 0;
return {
side: whaleSide,
tokenId: row.token_id,
whalePrice: price,
};
}
```
### On-chain balance as source of truth
Before every BUY, the task reads pUSD balance directly from Polygon rather than keeping a local counter. pUSD has 6 decimals (matching USDC.e):
```typescript theme={null}
const balResp = (await ctx.fetch("https://polygon-bor-rpc.publicnode.com", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_call",
params: [
{
to: "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB", // pUSD
data: "0x70a08231000000000000000000000000" + address.slice(2).toLowerCase(),
},
"latest",
],
id: 1,
}),
})) as { result?: string };
const pusdBalance = balResp?.result ? Number(BigInt(balResp.result)) / 1e6 : 0;
if (pusdBalance < 1.1) {
return { status: "BALANCE_LOW", balance: pusdBalance };
}
```
A local budget collection would drift out of sync with the real wallet after redemptions, unsponsored gas, or manual top-ups. Reading the chain avoids that entirely.
### Signing and submitting via `ctx.fetch`
Polymarket's `@polymarket/clob-client-v2` SDK uses axios for HTTP, which fails under Compose's task runtime because task binaries run without `--allow-net`. The template reuses the SDK's pure signing utilities (local crypto only) and routes every HTTP call through `ctx.fetch`. The `version: 2` argument selects the V2 Exchange domain and order struct (V2 fees are computed on-chain, so they are no longer signed):
```typescript theme={null}
import {
OrderBuilder,
Side,
OrderType,
SignatureTypeV2,
createL2Headers,
orderToJsonV2,
} from "@polymarket/clob-client-v2";
const builder = new OrderBuilder(wallet, 137, SignatureTypeV2.EOA);
// Build + sign locally (no HTTP)
const signedOrder = await builder.buildMarketOrder(
{ tokenID, price, amount, side: Side.BUY },
{ tickSize, negRisk },
2 // V2 order version
);
// Submit through ctx.fetch → Fly proxy → CLOB
const body = orderToJsonV2(signedOrder, creds.key, OrderType.FAK);
const bodyStr = JSON.stringify(body);
const l2Headers = await createL2Headers(wallet, creds, {
method: "POST",
requestPath: "/order",
body: bodyStr,
});
const resp = await ctx.fetch(`${host}/order`, {
method: "POST",
headers: { ...l2Headers, "Content-Type": "application/json" },
body: bodyStr,
});
```
`ctx.fetch` is host-mediated, which means it has network access even though the task itself does not. This is the general pattern for calling external APIs from a Compose task — see [calling external APIs](/compose/context/fetch) for more.
### Why the Fly.io proxy
Polymarket's CLOB API geo-blocks the US. Compose tasks run from `us-west`. The template points `CLOB_HOST` at a shared Goldsky-hosted Fly.io proxy in Amsterdam that forwards every request from an EU IP. If you want to isolate yourself from the shared proxy, deploy your own copy of [`fly-polymarket-proxy`](https://github.com/goldsky-io/fly-polymarket-proxy) and update `CLOB_HOST` in `compose.yaml`.
## Key Compose features used
* **Turbo → Compose webhook pattern** — a pipeline sinks decoded on-chain events directly to an HTTP task via a `COMPOSE_WEBHOOK_AUTH` secret
* **`ctx.fetch`** — all outbound HTTP (CLOB, Gamma, Polymarket data API, Polygon RPC) goes through Compose's host-mediated fetch
* **`ctx.evm.wallet` with `sponsorGas: true`** — approvals and redemptions use a Compose-sponsored wallet so the EOA does not need MATIC
* **Cron triggers** — the `redeem` task runs every 5 minutes via `0 */5 * * * *`
* **`ctx.collection`** — `positions` and `trades` collections persist bot state across invocations
* **Compose secrets** — the wallet private key is stored via `goldsky compose secret set`, not baked into the manifest
## Customization
### Trade size
The default is Polymarket's \$1 minimum notional. Raise it in `compose.yaml`:
```yaml theme={null}
TRADE_AMOUNT_USD: "10"
```
### Different markets
The pipeline filters by address only, so it picks up every fill on the CTF Exchange and NegRisk Exchange. To scope to a specific market type (e.g. a particular event's outcomes), add a `token_id` filter to the `watched_fills` transform.
### Your own proxy
`CLOB_HOST` defaults to the Goldsky-hosted Fly proxy. To isolate from it, deploy [`fly-polymarket-proxy`](https://github.com/goldsky-io/fly-polymarket-proxy) yourself and point `CLOB_HOST` at your deployment.
## Resources
* [Compose introduction](/compose/introduction)
* [Turbo introduction](/turbo-pipelines/introduction)
* [Task triggers](/compose/task-triggers)
* [Calling external APIs](/compose/context/fetch)
* [Collections](/compose/context/collections)
* [Secrets](/compose/secrets)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/copy-trader)
# Build a binary prediction market
Source: https://docs.goldsky.com/compose/guides/build-a-prediction-market
Build a binary prediction market on Gnosis ConditionalTokens using Compose, CoinGecko, and multi-task orchestration
This guide walks you through building a self-contained binary prediction market on [Gnosis ConditionalTokens (CTF)](https://docs.gnosis.io/conditionaltokens/). A single cron orchestrator runs every 5 minutes, fetches the current BTC/USD price, resolves the previous cycle's market, and prepares a new one for the next 5-minute bucket. It demonstrates multi-task orchestration, `callTask` fan-out, Compose-managed wallets as on-chain oracles, and idempotent chain writes.
## How it works
```mermaid theme={null}
flowchart LR
A[Cron Trigger] -->|"every 5 min"| B[orchestrator]
B -->|callTask| C[market_data]
C -->|"BTC/USD price"| D[CoinGecko API]
B -->|callTask| E[launch_market]
B -->|callTask| F[resolve_market]
E -->|prepareCondition| G[Gnosis CTF on Base Sepolia]
F -->|reportPayouts| G
B -->|"persist state"| H[Collection]
```
Each 5-minute cycle:
1. **`market_data`** fetches the current BTC/USD price from CoinGecko.
2. **`resolve_market`** reports payouts for any market whose `endTime` has passed. Outcome is `[1, 0]` (UP) if the current price is at or above the market's `openPrice`, else `[0, 1]` (DOWN).
3. **`launch_market`** prepares a new condition on the CTF for the current 5-minute bucket, using the same fetched price as the new market's `openPrice`.
One price fetch per cycle serves both purposes — the closing tick of the expiring market sits at the same moment as the opening tick of the new one.
## Prerequisites
* [Goldsky CLI installed](/installation)
## Project structure
```text theme={null}
prediction-market/
├── compose.yaml # 1 cron + 4 callable tasks
├── package.json # viem dependency
├── tsconfig.json # Compose path alias
├── src/
│ ├── lib/
│ │ ├── constants.ts # Chain, CTF address, wallet name, salt
│ │ ├── types.ts # Market type
│ │ └── utils.ts # questionId derivation, wallet helper
│ └── tasks/
│ ├── orchestrator.ts # Cron: fetch → resolve → launch
│ ├── market-data.ts # HTTP: CoinGecko
│ ├── launch-market.ts # Chain: prepareCondition
│ ├── resolve-market.ts # Chain: reportPayouts
│ └── generate-wallet.ts # HTTP: returns oracle address
```
## Step 1: Set up the project
Clone the example repository:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/prediction-market
npm install
```
## Step 2: Understand the orchestrator
`orchestrator.ts` is the only task with a cron trigger. It fans out to the three worker tasks via `context.callTask`:
```typescript theme={null}
import type { TaskContext } from "compose";
import { ASSET_PAIR, DURATION_SEC } from "../lib/constants";
import type { Market } from "../lib/types";
import { computeQuestionId, floorToMarketStart } from "../lib/utils";
import type { TaskPayload as LaunchPayload } from "./launch-market";
import type { TaskPayload as ResolvePayload } from "./resolve-market";
import type { ResponsePayload as PriceData } from "./market-data";
export async function main(context: TaskContext) {
const { collection, callTask } = context;
const nowMs = Date.now();
const currentMarketStart = floorToMarketStart(nowMs);
const markets = await collection("markets", [
{ path: "endTime", type: "numeric" },
{ path: "resolved", type: "boolean" },
]);
// One HTTP hit — used for BOTH the closePrice of the expiring market
// AND the openPrice of the new one (same 5-min boundary).
const { priceUsd } = await callTask, PriceData>(
"market_data",
{},
);
// Resolve any overdue, unresolved markets.
const overdue = await markets.findMany({
endTime: { $lte: nowMs },
resolved: false,
});
for (const market of overdue) {
const close = market.closePrice ?? priceUsd;
// Snapshot closePrice before the chain call so retries produce a
// deterministic outcome even if prices move between attempts.
if (market.closePrice === undefined) {
await markets.setById(market.questionId, { ...market, closePrice: close });
}
await callTask("resolve_market", {
market: { ...market, closePrice: close },
});
}
// Launch the market for the current 5-min bucket if it doesn't exist yet.
const currentQid = computeQuestionId({
assetPair: ASSET_PAIR,
durationSec: DURATION_SEC,
startTimestampSec: Math.floor(currentMarketStart / 1000),
});
if (!(await markets.getById(currentQid))) {
await callTask("launch_market", {
startTime: currentMarketStart,
openPrice: priceUsd,
});
}
}
```
### Key Compose features used
* **`context.callTask`** — orchestrator delegates work to specialized child tasks with their own retry configs.
* **`context.collection`** — persistent document storage indexed by `endTime` and `resolved` for fast lookup of overdue markets.
* **`evm.wallet`** — a Compose-managed EOA named `prediction-market-oracle` serves as the CTF oracle.
* **`context.fetch`** — CoinGecko HTTP request with built-in retries (see `market-data.ts`).
* **`writeContract`** — raw ABI-signature calls for `prepareCondition` and `reportPayouts` on the CTF.
## Step 3: Understand the oracle pattern
The Gnosis CTF requires an `oracle` address when a condition is prepared. Only that address can later call `reportPayouts` for the condition. In this example, the oracle is a Compose-managed EOA — the same wallet that signs both transactions:
```typescript theme={null}
// src/lib/utils.ts
import type { TaskContext, IWallet } from "compose";
import { ORACLE_WALLET_NAME } from "./constants";
export function getOracleWallet(context: TaskContext): Promise {
return context.evm.wallet({ name: ORACLE_WALLET_NAME });
}
```
```typescript theme={null}
// src/tasks/launch-market.ts — prepareCondition uses the oracle as an input
await oracle.writeContract(
evm.chains[CHAIN],
CTF_ADDRESS,
"prepareCondition(address,bytes32,uint256)",
[oracle.address, questionId, "2"],
);
```
```typescript theme={null}
// src/tasks/resolve-market.ts — reportPayouts is signed by the oracle
await oracle.writeContract(
evm.chains[CHAIN],
CTF_ADDRESS,
"reportPayouts(bytes32,uint256[])",
[market.questionId, payouts],
);
```
Because each deploy of this example produces a fresh oracle address, the conditions it creates on the shared CTF are namespaced cleanly — they never collide with any other user of the same CTF.
## Step 4: Understand questionId derivation
Every CTF condition is keyed by a `questionId` the oracle chooses. This example derives one deterministically from the market parameters so retries always compute the same value:
```typescript theme={null}
// src/lib/utils.ts
export function computeQuestionId(args: {
assetPair: string;
durationSec: number;
startTimestampSec: number;
}): Hex {
return keccak256(
concat([
stringToHex(SALT, { size: 32 }),
stringToHex(args.assetPair, { size: 32 }),
numberToHex(args.durationSec, { size: 32 }),
numberToHex(args.startTimestampSec, { size: 32 }),
]),
);
}
```
The `SALT` constant (`"GOLDSKY_COMPOSE_DEMO"`) scopes every questionId to this example. Change it if you fork the app for your own purposes.
## Step 5: Configure the Compose app
`compose.yaml` declares one cron plus four callable tasks:
```yaml theme={null}
name: "prediction-market"
api_version: "stable"
tasks:
- path: "./src/tasks/orchestrator.ts"
name: "orchestrator"
triggers:
- type: "cron"
expression: "10 */5 * * * *"
retry_config:
max_attempts: 3
initial_interval_ms: 500
backoff_factor: 1
- path: "./src/tasks/launch-market.ts"
name: "launch_market"
- path: "./src/tasks/resolve-market.ts"
name: "resolve_market"
- path: "./src/tasks/market-data.ts"
name: "market_data"
retry_config:
max_attempts: 3
initial_interval_ms: 500
backoff_factor: 1
- path: "./src/tasks/generate-wallet.ts"
name: "generate_wallet"
triggers:
- type: "http"
authentication: "auth_token"
```
Only tasks with a `triggers` block can be invoked from outside the app. `launch_market`, `resolve_market`, and `market_data` are called exclusively via `context.callTask` from the orchestrator.
## Step 6: Deploy to Goldsky
```bash theme={null}
goldsky compose deploy
```
Compose sponsors gas by default, so the oracle wallet needs no funding. The first cron tick fires on the next 5-minute boundary.
Watch the cycles:
```bash theme={null}
goldsky compose logs
```
Look for `cycle complete:` log lines and on-chain `ConditionPreparation` / `ConditionResolution` events on [BaseScan](https://sepolia.basescan.org/address/0xb04639fB29CC8D27e13727c249EbcAb0CDA92331), filtered by your oracle EOA (topic\[2]).
To print the oracle EOA address:
```bash theme={null}
goldsky compose wallet list
```
## Customization
### Change the asset
Replace `BTC_USD` and the CoinGecko URL with any asset CoinGecko supports:
```typescript theme={null}
// src/lib/constants.ts
export const ASSET_PAIR = "ETH_USD" as const;
export const PRICE_URL =
"https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd";
```
Update the response parsing in `market-data.ts` to read `response.ethereum.usd`.
### Change the market duration
```typescript theme={null}
// src/lib/constants.ts
export const DURATION_SEC = 900; // 15 minutes
export const DURATION_MS = DURATION_SEC * 1000;
```
Update the cron expression to match the new cadence:
```yaml theme={null}
triggers:
- type: "cron"
expression: "10 */15 * * * *" # every 15 minutes
```
### Change the chain
Swap `baseSepolia` for any other EVM testnet. You'll also need a deployed ConditionalTokens contract on that chain — see [Gnosis's ConditionalTokens repository](https://github.com/gnosis/conditional-tokens-contracts) to deploy your own if none exists there.
```typescript theme={null}
// src/lib/constants.ts
export const CHAIN = "arbitrumSepolia" as const;
export const CTF_ADDRESS = "0xYOUR_CTF_ADDRESS";
```
## Resources
* [Compose introduction](/compose/introduction)
* [Task triggers](/compose/task-triggers)
* [Collections](/compose/context/collections)
* [EVM wallets](/compose/context/evm/wallets)
* [Gnosis ConditionalTokens](https://docs.gnosis.io/conditionaltokens/)
* [CoinGecko API](https://www.coingecko.com/en/api)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/prediction-market)
# Build a VRF system
Source: https://docs.goldsky.com/compose/guides/build-a-vrf-system
Build an on-chain verifiable random function (VRF) system using Compose and drand
This guide walks you through building a randomness delivery system using Compose and [drand](https://drand.love), a distributed randomness beacon. The system listens for on-chain randomness requests and fulfills them with drand values that anyone can verify off-chain against drand's BLS public key.
This example stores the drand signature on-chain but does not verify the BLS signature inside the Solidity contract (on-chain BLS12-381 verification is expensive and out of scope for this guide). Consumers who need trustless randomness should either verify the signature off-chain before using the result, or add on-chain BLS verification to the contract.
## How it works
```mermaid theme={null}
flowchart LR
A[Source Contract] -->|"emit event"| B[Compose Task]
B -->|"fetch randomness"| C[drand API]
C -->|"round, randomness, signature"| B
B -->|"fulfillRandomness"| D[Target Contract]
```
1. **Source contract** emits a `RandomnessRequested` event
2. **Compose task** is triggered by the on-chain event
3. **drand API** provides verifiable randomness with BLS signatures
4. **Target contract** records the randomness along with the drand round and signature so consumers can verify it off-chain
## Prerequisites
* [Goldsky CLI installed](/installation)
* [Foundry](https://book.getfoundry.sh/getting-started/installation) for contract deployment
* A funded wallet on Base Sepolia
## Project structure
```text theme={null}
VRF/
├── compose.yaml # Compose configuration
├── contracts/
│ └── RandomnessConsumer.sol # Example contract
├── src/
│ ├── contracts/
│ │ └── RandomnessConsumer.json # Contract ABI (for codegen)
│ ├── lib/
│ │ └── drand.ts # drand API utilities
│ └── tasks/
│ ├── generate-wallet.ts # Wallet generation utility
│ ├── request-randomness.ts # HTTP endpoint for requests
│ └── fulfill-randomness.ts # Main fulfillment task
```
## Step 1: Set up the project
Clone the example repository:
```bash theme={null}
git clone https://github.com/goldsky-io/documentation-examples.git
cd documentation-examples/compose/VRF
```
## Step 2: Generate contract types
The project includes a `RandomnessConsumer.json` ABI in `src/contracts/`. Generate the typed contract class:
```bash theme={null}
goldsky compose codegen
```
This creates a typed `RandomnessConsumer` class that provides type-safe contract interaction instead of raw function signature strings.
## Step 3: Generate your Compose wallet
Start Compose locally in one terminal:
```bash theme={null}
goldsky compose start --fork-chains
```
`--fork-chains` lets you run a smart wallet locally by forking supported chains with TEVM. You can also use a private key wallet for local development — see [Secrets](/compose/secrets) for how to store the key.
In another terminal, get your wallet address:
```bash theme={null}
goldsky compose callTask generate_wallet '{}' --env local
```
Save the wallet address — this will be the authorized fulfiller for your contract.
## Step 4: Deploy the smart contract
The `RandomnessConsumer.sol` contract handles randomness requests and fulfillment:
```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract RandomnessConsumer {
struct RandomnessRequest {
address requester;
bool fulfilled;
bytes32 randomness;
uint64 round;
bytes signature;
}
address public fulfiller;
uint256 public nextRequestId;
mapping(uint256 => RandomnessRequest) public requests;
event RandomnessRequested(uint256 indexed requestId, address indexed requester);
event RandomnessFulfilled(uint256 indexed requestId, bytes32 randomness, uint64 round, bytes signature);
constructor(address _fulfiller) {
fulfiller = _fulfiller;
}
function requestRandomness() external returns (uint256 requestId) {
requestId = nextRequestId++;
requests[requestId] = RandomnessRequest({
requester: msg.sender,
fulfilled: false,
randomness: bytes32(0),
round: 0,
signature: ""
});
emit RandomnessRequested(requestId, msg.sender);
}
function fulfillRandomness(
uint256 requestId,
bytes32 randomness,
uint64 round,
bytes calldata signature
) external {
require(msg.sender == fulfiller, "OnlyFulfiller");
RandomnessRequest storage request = requests[requestId];
require(request.requester != address(0), "RequestNotFound");
require(!request.fulfilled, "AlreadyFulfilled");
request.fulfilled = true;
request.randomness = randomness;
request.round = round;
request.signature = signature;
emit RandomnessFulfilled(requestId, randomness, round, signature);
}
}
```
Deploy to Base Sepolia:
```bash theme={null}
forge create contracts/RandomnessConsumer.sol:RandomnessConsumer \
--rpc-url https://sepolia.base.org \
--private-key $PRIVATE_KEY \
--broadcast \
--constructor-args 0xYOUR_COMPOSE_WALLET_ADDRESS
```
Save the deployed contract address.
## Step 5: Configure the Compose app
Update `compose.yaml` with your contract address:
```yaml theme={null}
name: "compose-vrf"
api_version: "stable"
tasks:
- path: "./src/tasks/generate-wallet.ts"
name: "generate_wallet"
triggers:
- type: "http"
authentication: "auth_token"
- path: "./src/tasks/request-randomness.ts"
name: "request_randomness"
triggers:
- type: "http"
authentication: "auth_token"
- path: "./src/tasks/fulfill-randomness.ts"
name: "fulfill_randomness"
triggers:
- type: "onchain_event"
network: "base_sepolia"
contract: "0xYOUR_DEPLOYED_CONTRACT_ADDRESS"
events:
- "RandomnessRequested(uint256,address)"
retry_config:
max_attempts: 3
initial_interval_ms: 1000
backoff_factor: 2
```
Update the contract address in the task files:
* `src/tasks/fulfill-randomness.ts` — `TARGET_CONTRACT`
* `src/tasks/request-randomness.ts` — `CONTRACT_ADDRESS`
## Step 6: Understand the fulfillment task
The `fulfill-randomness.ts` task handles the core logic:
```typescript theme={null}
import { TaskContext, OnchainEvent } from "compose";
import {
fetchLatestRandomness,
toBytes32,
toBytes,
DRAND_CHAIN_INFO,
} from "../lib/drand.ts";
const CONTRACT_ADDRESS = "0xYOUR_DEPLOYED_CONTRACT_ADDRESS";
export async function main(context: TaskContext, event?: OnchainEvent) {
const { fetch, evm } = context;
// Extract request ID from the event
const requestId = event?.topics[1] ? BigInt(event.topics[1]) : 0n;
// Fetch randomness from drand
const drandResponse = await fetchLatestRandomness(fetch);
console.log(`Fetched drand round ${drandResponse.round}`);
// Get wallet and instantiate typed contract (generated from src/contracts/RandomnessConsumer.json)
const wallet = await evm.wallet({
name: "randomness-fulfiller",
});
const contract = new evm.contracts.RandomnessConsumer(
CONTRACT_ADDRESS,
evm.chains.baseSepolia,
wallet
);
// Fulfill the randomness request on-chain
const { hash } = await contract.fulfillRandomness(
requestId.toString(),
toBytes32(drandResponse.randomness),
drandResponse.round,
toBytes(drandResponse.signature)
);
console.log(`Fulfilled request ${requestId} in tx ${hash}`);
return {
success: true,
requestId: requestId.toString(),
transactionHash: hash,
drand: {
round: String(drandResponse.round),
randomness: toBytes32(drandResponse.randomness),
chainHash: DRAND_CHAIN_INFO.hash,
},
};
}
```
Key points:
* **Contract codegen** — `evm.contracts.RandomnessConsumer` is generated from the ABI JSON, providing type-safe method calls like `contract.fulfillRandomness(...)` instead of raw function signature strings
* Uses `evm.chains.baseSepolia` — a built-in chain, no custom configuration needed
* Gas is sponsored by default for smart wallets on supported chains. On chains where sponsorship isn't available, fund the wallet directly or pass `sponsorGas: false` and cover gas from the wallet
* `fulfillRandomness` does seven or more SSTOREs (the signature is a 96-byte `bytes` field). Gas estimation handles this on Base Sepolia, but if you port this to a chain where you set the gas limit manually, budget \~250k. On Monad, where the full gas limit is charged regardless of gas used, keep the limit as tight as possible
## Step 7: Understand the drand integration
The `drand.ts` library fetches verifiable randomness:
```typescript theme={null}
export type DrandResponse = {
round: number;
randomness: string; // hex - sha256(signature)
signature: string; // hex - BLS12-381 signature (96 bytes)
previous_signature: string;
};
export const DRAND_CHAIN_INFO = {
hash: "52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971",
publicKey: "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a",
genesisTime: 1692803367,
period: 3, // seconds between rounds
};
export async function fetchLatestRandomness(
fetchFn: (url: string) => Promise
): Promise {
const response = await fetchFn(
`https://api.drand.sh/${DRAND_CHAIN_INFO.hash}/public/latest`
);
if (!response) {
throw new Error("Failed to fetch randomness from drand");
}
return response;
}
```
The randomness is verifiable off-chain using drand's BLS12-381 signatures. Anyone can check that the signature is valid against drand's public key, and that `sha256(signature) == randomness`, using a drand client library.
## Step 8: Run locally
Start the Compose app:
```bash theme={null}
goldsky compose start --fork-chains
```
## Step 9: Test the system
Request randomness by calling the contract:
```bash theme={null}
cast send 0xYOUR_CONTRACT_ADDRESS "requestRandomness()" \
--rpc-url https://sepolia.base.org \
--private-key $PRIVATE_KEY
```
Watch the Compose logs. You should see:
1. The `RandomnessRequested` event being detected
2. Randomness fetched from drand
3. The fulfillment transaction submitted
## Step 10: Deploy to Goldsky
Once tested locally, deploy to Goldsky's cloud:
```bash theme={null}
goldsky compose deploy
```
## Customization
### Different chains
Use any [supported chain](/compose/context/evm/chains) — no custom configuration needed:
```typescript theme={null}
const wallet = await evm.wallet({ name: "my-wallet" });
const result = await wallet.writeContract(
evm.chains.arbitrumSepolia, // or baseSepolia, polygonAmoy, etc.
CONTRACT_ADDRESS,
"myFunction(uint256)",
[arg1]
);
```
### Different events
Modify the `compose.yaml` to listen for different events:
```yaml theme={null}
triggers:
- type: "onchain_event"
network: "base_sepolia"
contract: "0xYOUR_CONTRACT"
events:
- "YourCustomEvent(uint256,address,bytes32)"
```
## Resources
* [drand documentation](https://docs.drand.love)
* [Compose introduction](/compose/introduction)
* [Task triggers](/compose/task-triggers)
* [EVM wallets](/compose/context/evm/wallets)
* [GitHub repository](https://github.com/goldsky-io/documentation-examples/tree/main/compose/VRF)
# Solana Transactions with Gill
Source: https://docs.goldsky.com/compose/guides/solana-transactions
Build, sign, and send Solana transactions from Compose tasks using Gill and a sandboxed RPC transport.
Compose tasks can build, sign, and send Solana transactions using [Gill](https://github.com/solana-foundation/gill), a lightweight Solana client library. Since Compose tasks run in a sandboxed environment, you'll wire Gill's RPC transport through the built-in [fetch](../context/fetch) function so that all network activity remains auditable.
## Setup
Install Gill in your Compose project:
```bash theme={null}
npm install gill
```
Add your Solana RPC URL and keypair as [secrets](../secrets) in `compose.yaml`:
```yaml highlight={3-5} theme={null}
name: "my-solana-app"
api_version: "stable"
secrets:
- SOLANA_RPC_URL
- SOLANA_KEYPAIR
tasks:
- path: "./src/tasks/solana-writer.ts"
name: "solana_writer"
triggers:
- type: "http"
authentication: "none"
```
## Sandboxed RPC transport
Compose tasks cannot use the global `fetch` — all HTTP requests must go through the provided `context.fetch`. Create a transport adapter that Gill can use:
```typescript theme={null}
import { TaskContext } from "compose";
import { createSolanaRpcFromTransport } from "gill";
function createSandboxedTransport(
rpcUrl: string,
sandboxedFetch: TaskContext["fetch"],
) {
return async ({
payload,
}: {
payload: unknown;
signal?: AbortSignal;
}): Promise => {
const result = await sandboxedFetch(rpcUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(payload),
});
if (result === undefined) {
throw new Error(`Solana RPC request failed for ${rpcUrl}`);
}
return result;
};
}
```
Then create an RPC client from the transport:
```typescript theme={null}
const transport = createSandboxedTransport(rpcUrl, fetch);
const rpc = createSolanaRpcFromTransport(transport);
```
This pattern applies to any Solana library that accepts a custom transport or fetch function. The key requirement is routing all network calls through `context.fetch` so they're recorded in Compose's event logs.
## Loading a keypair
Store your Solana keypair as a secret in the same JSON byte-array format used by the Solana CLI (the output of `solana-keygen`). Then load it in your task:
```typescript theme={null}
import {
createKeyPairFromBytes,
createSignerFromKeyPair,
} from "gill";
const keypairBytes = new Uint8Array(JSON.parse(env.SOLANA_KEYPAIR));
const keyPair = await createKeyPairFromBytes(keypairBytes);
const signer = await createSignerFromKeyPair(keyPair);
```
## Building and sending a transaction
Here's a complete example that fetches Bitcoin's price, writes it to a Solana program, and stores the result in a [collection](../context/collections):
```typescript theme={null}
import { TaskContext } from "compose";
import {
createSolanaRpcFromTransport,
createKeyPairFromBytes,
createSignerFromKeyPair,
address,
getAddressEncoder,
getProgramDerivedAddress,
createTransaction,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
getBase64EncodedWireTransaction,
AccountRole,
} from "gill";
const PROGRAM_ID = "4MUYDek4T93NNN9dsRfxRTZc4KznZ1vTTe4vLtoS2AEs";
const SYSTEM_PROGRAM = "11111111111111111111111111111111";
const DEVNET_RPC_URL = "https://api.devnet.solana.com";
// Anchor instruction discriminator for "write" (from IDL)
const WRITE_DISCRIMINATOR = new Uint8Array([235, 116, 91, 200, 206, 170, 144, 120]);
function toBytes32(value: number): Uint8Array {
const bytes = new Uint8Array(32);
let remaining = value;
for (let i = 31; i >= 0 && remaining > 0; i--) {
bytes[i] = remaining & 0xff;
remaining = Math.floor(remaining / 256);
}
return bytes;
}
function createSandboxedTransport(
rpcUrl: string,
sandboxedFetch: TaskContext["fetch"],
) {
return async ({
payload,
}: {
payload: unknown;
signal?: AbortSignal;
}): Promise => {
const result = await sandboxedFetch(rpcUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(payload),
});
if (result === undefined) {
throw new Error(`Solana RPC request failed for ${rpcUrl}`);
}
return result;
};
}
export async function main(context: TaskContext) {
const { fetch, env, collection } = context;
// --- Solana RPC setup ---
const rpcUrl = env.SOLANA_RPC_URL || DEVNET_RPC_URL;
const transport = createSandboxedTransport(rpcUrl, fetch);
const rpc = createSolanaRpcFromTransport(transport);
// --- Load signer ---
const keypairBytes = new Uint8Array(JSON.parse(env.SOLANA_KEYPAIR));
const keyPair = await createKeyPairFromBytes(keypairBytes);
const signer = await createSignerFromKeyPair(keyPair);
// --- Fetch Bitcoin price ---
const response = await fetch<{ bitcoin: { usd: number } }>(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
{ max_attempts: 3, initial_interval_ms: 1000, backoff_factor: 2 },
);
if (!response) {
throw new Error("Failed to fetch Bitcoin price");
}
const bitcoinPrice = response.bitcoin.usd;
const timestamp = Date.now();
// --- Build transaction ---
const key = toBytes32(timestamp);
const value = toBytes32(Math.round(bitcoinPrice * 100));
// Derive PDA: seeds = ["data", signer_pubkey, key]
const addressEncoder = getAddressEncoder();
const signerPubkeyBytes = addressEncoder.encode(signer.address);
const [pda] = await getProgramDerivedAddress({
programAddress: address(PROGRAM_ID),
seeds: [new TextEncoder().encode("data"), signerPubkeyBytes, key],
});
// Instruction data: 8-byte discriminator + 32-byte key + 32-byte value
const instructionData = new Uint8Array(8 + 32 + 32);
instructionData.set(WRITE_DISCRIMINATOR, 0);
instructionData.set(key, 8);
instructionData.set(value, 40);
const writeInstruction = {
programAddress: address(PROGRAM_ID),
accounts: [
{ address: pda, role: AccountRole.WRITABLE as const },
{ address: signer.address, role: AccountRole.WRITABLE_SIGNER as const },
{ address: address(SYSTEM_PROGRAM), role: AccountRole.READONLY as const },
],
data: instructionData,
};
// --- Sign and send ---
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const tx = createTransaction({
version: "legacy",
feePayer: signer,
instructions: [writeInstruction],
latestBlockhash,
});
const signedTx = await signTransactionMessageWithSigners(tx);
const signature = getSignatureFromTransaction(signedTx);
const encodedTx = getBase64EncodedWireTransaction(signedTx);
await rpc.sendTransaction(encodedTx, { encoding: "base64" }).send();
// --- Store result ---
const priceHistory = await collection("bitcoin_prices");
const { id } = await priceHistory.insertOne({
price: bitcoinPrice,
timestamp,
});
return { success: true, signature, price: bitcoinPrice, timestamp, priceId: id };
}
```
### What's happening
1. **RPC setup** — A sandboxed transport wraps `context.fetch` so Gill's RPC calls go through Compose's auditable network layer.
2. **Load signer** — The keypair is loaded from a Compose secret and converted into a Gill transaction signer.
3. **Fetch off-chain data** — The task fetches Bitcoin's price from CoinGecko with built-in retry logic.
4. **Derive PDA** — A Program Derived Address is computed from seeds so the on-chain program knows where to store the data.
5. **Build instruction** — The instruction data is assembled: an 8-byte Anchor discriminator followed by the key and value.
6. **Sign and send** — The transaction is created, signed, base64-encoded, and submitted to the Solana RPC.
7. **Store metadata** — The price and transaction signature are persisted in a Compose collection for later retrieval.
## Next Steps
Learn more about using npm packages in Compose tasks.
Store and query state across task runs.
Trigger your Solana tasks via cron, HTTP, or on-chain events.
Securely manage your Solana keypair and RPC URL.
# Build Offchain-x-Onchain Systems with Compose
Source: https://docs.goldsky.com/compose/introduction
Compose is the offchain-to-onchain framework for high-stakes systems — write durable TypeScript that moves money, data, and events between platforms.
Compose is the offchain-to-onchain framework for high-stakes systems. It lets you write code that durably moves money, data, and events between every platform you use, whether it's offchain or onchain.
Write simple TypeScript code that runs in verifiable sandboxes, with auditable datastores and traceable functions. Monitor the input and output of every mutation in production, and use TEE-powered attestations to prove that your logic actually ran as intended.
Run our install script to get all necessary dependencies and instantly generate your first Compose App.
## Use Cases
Tailored to your app's logic and update cadence
Trustless, verifiable and auditable data sources
Detect anomalies and pause or reroute activity
Market data ingestion with application-specific rules
Verify offchain facts (KYC/AML, account status) and write onchain attestations
Rebalance strategies, payouts, or maintenance windows
Read across chains and external APIs then write atomically, anywhere
Triggered notifications from onchain events with guaranteed delivery
## How it Works
Every Compose app can have multiple tasks, which can be triggered through on-chain events, cron-like scheduling, or an API.
Tasks will run in auditable sandboxes with immutable tracing on every single function call that interfaces with the external world. State is fully versioned and tracked, and execution is persisted through any unforeseen downtime.
* **Ultracompliance:** Write your code in your local environment, and run exactly the same code in our cloud environment or in a Trusted Executable Environment (TEE).
* **Actually useful sandboxes:** Task code runs in a sandbox, but can call safe functions that interact with the outside world.
* **Trace everything**: Every step of every task is traced. Our CLI and UI allows you to step through every single task execution down to the function calls.
## Architecture
```mermaid theme={null}
flowchart TB
subgraph external["External Systems and Clients"]
EXT[" "]
end
subgraph services["External Services"]
HTTP((HTTP Calls))
BC((Blockchains))
SDB[(Stage DB)]
end
subgraph sandbox["Secure Task Sandbox"]
direction TB
TASK[Compose Task Process]
NOTE["Only communicates via IPC bus"]
end
ES[(Event Storage)]
EXT -->|"HTTP task trigger"| HOST
EXT -->|"Cron trigger"| HOST
EXT -->|"Audit query"| ES
HTTP <-->|"fetch"| HOST
BC <-->|"reads/writes"| HOST
SDB <-->|"cross-task state"| HOST
HOST[Compose Host] <-->|"Task run\ntrigger/response"| TASK
HOST <-->|"Host function\nrequest/response"| TASK
HOST -->|"Log events"| ES
```
Compose executes tasks in secure and auditable sandboxes which route all external communication through the Compose host, leveraging it for durability handling and guaranteed execution.
Every single function call that mutates state (databases, blockchains) or accesses external resources will be tracked, with inputs and outputs saved. This is accessible through a UI for tracing or debugging purposes.
The task sandbox is completely isolated and can be run as a Trusted Executable Environment or as a normal container. **The same code can be used in all environments.** Code run in a TEE will also generate attestations, with guarantees that the code run is exactly the code you intend, with no side effects.
Get started by [setting up your first Compose application](/compose/quick-start) in under 5 minutes.
# Using Packages
Source: https://docs.goldsky.com/compose/packages
Use npm packages such as viem inside Compose tasks, with bundling guidance and sandbox limitations.
## Overview
Compose apps are bundled with esbuild and run in secure, auditable sandboxes. The sandboxes disallow OS, filesystem, and direct networking activity.
To make external HTTP requests, use the provided [fetch](./context/fetch) function. This keeps compose apps fully auditable, trusted, and secure.
Because tasks are bundled with esbuild (not resolved by Deno), you can use any packages installed via npm, yarn, or pnpm. Native Node.js packages, or packages that rely on direct filesystem or network access, will not work inside the sandbox.
To use third-party packages:
1. Make sure your project has a `package.json` (run `npm init -y` if it doesn't).
2. Install packages with your preferred package manager (`npm install viem`, `yarn add viem`, etc.).
3. Import using bare specifiers in your task files (e.g. `import { keccak256 } from "viem"`).
Packages must be installed in `node_modules/` — esbuild resolves imports from there, not from Deno's module cache. Do **not** use Deno-style `npm:` prefixes (e.g. `"npm:viem"`) or URL imports like `https://deno.land/std@.../...` in task files; they won't resolve at bundle time.
You can import libraries like viem for more customized functionality. If you use HTTP-enabled functions from viem, you'll need to specify a custom transport backed by the built-in [compose fetch](./context/fetch) — the standard viem transports can't reach the network from inside the sandbox. Otherwise, you can use utility functions the same way you would in any TypeScript app.
## npm packages in dashboard deploys
Deploying from the dashboard (editing an app in the browser) supports a fixed allowlist of npm packages:
* `viem`
* `ethers`
* `gill`
* `@solana/web3.js`
* `@solana/spl-token`
* `@noble/hashes`
* `@noble/curves`
* `bs58`
* `zod`
* `bignumber.js`
Versions come from your app's `package.json`. Any package outside the allowlist fails with a `cli-only-package` error; deploy with the CLI instead, which bundles any sandbox-compatible package.
HTTP client packages such as `axios`, `node-fetch`, and `got` are deliberately left off the allowlist. All network access from a task must go through [`ctx.fetch`](./context/fetch), so they cannot reach the network from inside the sandbox either way.
## Example
```typescript theme={null}
import { TaskContext } from "compose";
import {
concat,
keccak256,
stringToHex,
numberToHex,
} from "viem";
export async function main({ evm, env }: TaskContext) {
const assetPair = "BTC-USD";
const durationInSeconds = 3600;
const startTimestamp = Math.floor(Date.now() / 1000);
// use viem utilities to compute a deterministic id
const resultId = keccak256(
concat([
stringToHex("CANDLE", { size: 32 }),
stringToHex(assetPair, { size: 32 }),
numberToHex(durationInSeconds, { size: 32 }),
numberToHex(startTimestamp, { size: 32 }),
])
);
console.log(resultId);
}
```
## Next Steps
Debug and monitor your apps
Learn about deploying your app to the cloud for production use cases.
# Deploy a Compose App
Source: https://docs.goldsky.com/compose/quick-start
Get up and running with Compose in under 5 minutes.
### 1. Install the Compose CLI Extension
If you already have the Goldsky CLI installed, you'll just need to install the Compose extension.
```bash theme={null}
goldsky compose install
```
If you don't already have the Goldsky CLI installed you'll need to start by installing that, then run the above command.
If you run any `goldsky compose ` before installing the extension, it prints an install hint and exits; it no longer auto-installs.
**For macOS/Linux:**
```shell theme={null}
curl https://goldsky.com | sh
```
**For Windows:**
```shell theme={null}
npm install -g @goldskycom/cli
```
Windows users need to have Node.js and npm installed first. Download from [nodejs.org](https://nodejs.org) if not already installed.
### 2. Create your app
Create a new Compose project with a working Bitcoin oracle example:
```bash theme={null}
goldsky compose init
```
This command will:
* Prompt you for a project name and create the app under that directory
* Scaffold a complete example app with a working Bitcoin oracle
```bash theme={null}
demo-app/
compose.yaml # the app manifest
tsconfig.json # TypeScript configuration
.env # local environment variables
.gitignore
src/
tasks/
bitcoin-oracle.ts # example task
lib/
utils.ts # shared utilities
```
### 3. Start the app locally
`cd` into the project directory that was just created:
```bash theme={null}
cd
```
Start the app:
```bash theme={null}
goldsky compose start --fork-chains
```
`--fork-chains` allows you to run a smart wallet locally. You can also use a private key wallet for your local Compose app. See more [here](/compose/secrets).
Your Compose application is now running! The server starts on port 4000 by default. If 4000 is taken, `start` walks up to port 4009 and prints `Port 4000 is in use, using instead.`. To pin an exact port, pass `-p, --port `; `start` fails fast if that port is taken.
You can test triggering a task like so:
```bash theme={null}
goldsky compose callTask bitcoin_oracle '{}' --env local
```
## Updating the CLI
To update to the latest version, run the update command:
```bash theme={null}
goldsky compose update
```
# Wallets and Secrets
Source: https://docs.goldsky.com/compose/secrets
Manage wallets and secrets for Compose apps, including built-in smart wallets and securely stored EOA private keys.
Most Compose apps need some form of credential — an EOA private key, an auth token for an external service, etc. Compose handles both built-in smart wallets and arbitrary user-managed secrets.
### Wallets
Many compose apps will need to make blockchain transactions and will need gas and payment funds to do so. By default, Compose will make you a smart wallet which allows you to pay gas fees in fiat as part of your normal monthly Goldsky bill.
This allows simple USD based accounting for companies and users who are not "blockchain-native". You can easily fund these smart wallets with any tokens you may require for your particular business logic (by default Goldsky
will pay gas fees if you haven't funded the wallet yourself). Goldsky hosted wallets are created dynamically and idempotently in your
app code, allowing any type of logic in wallet creation — for specifics see [Context](./context).
However there may be times when specific EOA wallets are needed for specific transactions, such as owner-only contract methods. In those cases you can store your private key securely in a secret — see below for details.
To fund your built-in smart wallet (beyond Goldsky's default gas-sponsorship behavior), you can retrieve its public key from the compose Dashboard at app.goldsky.com. See [monitoring](./deploy-monitor) for details.
### Examples
```typescript theme={null}
import { TaskContext } from "compose";
export async function main({ env, evm }: TaskContext) {
// a default smart wallet
// this will make a smart wallet and will sponsor gas by default
const mySmartWallet = await evm.wallet({ name: "my-wallet" });
// you can disable gas sponsoring on a smart wallet
// allowing you to obtain the public key from your compose dashboard and fund it yourself
const mySelfFundedSmartWallet = await evm.wallet({
name: "my-wallet",
sponsorGas: false
});
// make an EOA based wallet using a private key secret
// by default privateKey wallets don't use gas sponsoring
// see below for info about storing the secret
const myPrivateKeyWallet = await evm.wallet({
privateKey: env.MY_PRIVATE_KEY
});
}
```
### Secrets
Compose secrets are scoped to a specific Compose app within your Goldsky project. You can set or update secrets at any time using the CLI, but a running app will only pick up new secret values after a redeploy.
This protects running apps from accidental changes and lets you roll forward (or back) safely by pairing a deploy with the secrets it expects. The way secrets are managed in the cloud vs locally is slightly different.
Secret names must be in SCREAMING\_SNAKE\_CASE — all uppercase letters, digits, and underscores, starting with a letter (e.g. `MY_SECRET`, `API_KEY_V2`).
#### Manage Secrets for Local Dev
In local dev, you'll put your secrets in your `.env` file. Every compose app created with `goldsky compose init` comes with a gitignored `.env` file by default.
```bash .env theme={null}
# Compose Local Secrets
PRIVATE_KEY=132981234adsufyadsf78134asdf
```
#### Manage Secrets for Cloud
To add or update a secret in the cloud:
```bash theme={null}
goldsky compose secret set MY_PRIVATE_KEY --value xyz123
```
To update a secret and immediately redeploy so the app picks it up:
```bash theme={null}
goldsky compose secret set MY_PRIVATE_KEY --value xyz123 --redeploy
```
To remove a cloud secret:
```bash theme={null}
goldsky compose secret delete MY_PRIVATE_KEY
```
To list all secrets for the app:
```bash theme={null}
goldsky compose secret list
```
You can also write secrets into your local `.env` with the same command by adding `--env local`:
```bash theme={null}
goldsky compose secret set MY_PRIVATE_KEY --value xyz123 --env local
```
#### How secrets are stored
Cloud secrets are end-to-end encrypted. When you run `goldsky compose secret set`, the CLI encrypts the value on your machine with your project's public key before uploading it, so the value is never sent or stored in plaintext.
There is no way to read a secret value back: not from the CLI, not from the dashboard, and not by Goldsky. `goldsky compose secret list` shows secret names only. A secret is decrypted only inside your running app, where it appears on `ctx.env`.
To rotate a secret, set a new value. The previous value is never displayed, and the dashboard secrets page shows names, not values.
If a secret value would appear in a task run's trace output, it is redacted in the dashboard. That is intended behavior, not data loss.
#### Syncing .env to the cloud at deploy
If you've been developing locally with secrets in your `.env` file, `compose deploy --sync-env` uploads every entry from your `.env` as a cloud secret before deploying. This is a convenient way to push a whole app's secrets in one step.
```bash theme={null}
goldsky compose deploy --sync-env
```
A running app only picks up new secret values after a redeploy. Use `--redeploy` on `secret set`, or run `goldsky compose deploy` yourself.
#### Using Secrets
For secrets to be injected into your app at runtime, they have to be listed in the `secrets:` section of the [manifest](./app-configuration). Only secrets named in the manifest of the currently-deployed version are made available to your tasks — so older deploys keep working even as you add, rename, or remove secrets for newer ones.
```yaml compose.yaml highlight={2-5} theme={null}
name: "my_app"
secrets:
- MY_PRIVATE_KEY
- MY_API_KEY
- CONTRACT_ADDRESS
tasks:
- name: "price_fetcher"
path: "./tasks/fetch_price.ts"
- name: "data_processor"
path: "./tasks/process_data.ts"
```
Inside a task, each secret is exposed on `ctx.env` under its exact name:
```typescript highlight={4,11,16} theme={null}
import { TaskContext } from "compose";
export async function main({ env, evm, fetch }: TaskContext) {
const myPrivateKeyWallet = await evm.wallet({ privateKey: env.MY_PRIVATE_KEY });
const address = "0x1234567890abcdef1234567890abcdef12345678" as `0x${string}`;
const questionId = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd";
const { hash } = await myPrivateKeyWallet.writeContract(
evm.chains.polygon,
env.CONTRACT_ADDRESS as `0x${string}`,
"prepareCondition(address,bytes32,uint256)",
[address, questionId, 2]
);
const response = await fetch(`https://api-service/api/v1/path?auth_token=${env.MY_API_KEY}`);
}
```
## Next Steps
Configure your app manifest, tasks, and secrets.
Deploy your app and monitor it in the cloud.
# Task Triggers
Source: https://docs.goldsky.com/compose/task-triggers
Configure cron, HTTP, and onchain event triggers for Compose tasks in your manifest file.
Your compose tasks can be triggered by several different mechanisms. Triggers are configured in your [manifest file](./app-configuration) for each task.
Each task can have multiple triggers (one of each type), so triggers are configured as an array. Below is an overview of all the supported trigger types: `cron`, `http`, and `onchain_event`.
## Local Task Execution
For locally testing tasks you can use the `callTask` CLI command. This works regardless of what trigger types a task has configured (and even for tasks with no triggers at all).
#### Trigger Task locally with CLI
```bash theme={null}
goldsky compose callTask "my_task" '{ "foo": "bar" }' --env local
```
#### Task Code
```typescript theme={null}
import { TaskContext } from "compose";
type Payload = {
foo: string;
};
export async function main({ collection }: TaskContext, payload: Payload) {
// the task will be called with the payload sent from the CLI callTask command
console.log(payload.foo);
}
```
## Task To Task Execution
Tasks can call other tasks directly — no trigger configuration required on the called task. This is done using the [callTask](./context/call-task) context function.
### Example
```typescript highlight={10-17} theme={null}
import { TaskContext } from "compose";
type ProcessDataPayload = {
dataId: string;
operation: string;
};
export async function main({ callTask }: TaskContext, payload: ProcessDataPayload) {
// Call another task with a payload
const result = await callTask<{ success: boolean; processed: number }>(
"process-data",
{
dataId: payload.dataId,
operation: payload.operation,
}
);
return {
status: "completed",
result,
};
}
```
The `process-data` task will receive the payload and can return a response:
```typescript theme={null}
import { TaskContext } from "compose";
type ProcessDataArgs = {
dataId: string;
operation: string;
};
export async function main({ collection }: TaskContext, payload: ProcessDataArgs) {
// Process the data based on the operation
const processed = await collection("data").findMany({ id: payload.dataId });
return {
success: true,
processed: processed.length,
};
}
```
## Chain Event Triggers
Tasks can be triggered by onchain events. The payload delivered to the task is the raw encoded log. Compose provides helpers for decoding the event in your task code — see [Contracts](./context/evm/contracts) for more details on decoding.
Onchain event triggers only fire when your app is **deployed** to the cloud. They do not fire during local development.
To test a task with an onchain trigger locally, use `callTask` with a sample event payload:
```bash theme={null}
goldsky compose callTask "my_task" '{"blockNumber":48758053,"address":"0xb74de3F91e04d0920ff26Ac28956272E8d67404D","data":"0x","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x2c50...","logIndex":343}' --env local
```
You can copy a real event payload from a block explorer to use as test data.
#### Example event object sent as the payload to your task:
```json theme={null}
{
"blockNumber": 48758053,
"blockHash":
"0x6794a56583329794f184d50862019ecf7b6d8ba6b3210f68ca4b91a8fa81817d",
"transactionIndex": 29,
"removed": false,
"address": "0xb74de3F91e04d0920ff26Ac28956272E8d67404D",
"data": "0x",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000eec2ba9b9f0202c63bba29ea9a4ce5c23f9865fd",
"0x0000000000000000000000000000000000000000000000000000000000001099",
],
"transactionHash":
"0x2c500a55f5c24d587e73805975d91395634a971dca5939f43d34d774d0f7147b",
"logIndex": 343,
}
```
#### Example configuration for an onchain event trigger:
```yaml highlight={6-10} theme={null}
name: "my-app"
tasks:
- name: "on_transfer"
path: "./tasks/on_transfer.ts"
triggers:
- type: "onchain_event"
network: "base_sepolia"
contract: "0xb74de3F91e04d0920ff26Ac28956272E8d67404D"
events:
- "Transfer(address,address,uint256)"
```
#### Task code
Here's an example in which we decode the event with a contract class we've generated with `goldsky compose codegen`, see [Contracts](./context/evm/contracts) for more info.
```typescript theme={null}
import { TaskContext, OnchainEvent } from "compose";
// the payload for an onchain triggered task will always be our built in OnchainEvent interface
export async function main({ evm }: TaskContext, payload: OnchainEvent) {
// we can decode the event with our ABI generated contract class
// the type of this decodedEvent will be a union of all events that the ABI specifies, allowing you to conditionally process based on the eventName
const decodedEvent = evm.contracts.MyNFT.decodeEventLog(payload);
if (decodedEvent.eventName === "Transfer") {
// in this code block typescript will know the decodedEvent is of type TransferEventDecoded
// this pattern is useful when your trigger supports multiple event types
console.log("Transfer event:", decodedEvent.args);
}
// alternative would be to cast, this is useful if your trigger is configured for a single event type
const castEvent = evm.contracts.MyNFT.decodeEventLog(payload);
}
```
#### Onchain Trigger Properties
| Property | Type | Required | Description |
| ----------------- | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `"onchain_event"` | Yes | Type discriminator |
| `network` | string | Yes | The network slug in snake\_case (e.g. `base_sepolia`, `ethereum_mainnet`). See [Chains](./context/evm/chains) for the full list. |
| `contract` | string | Yes | The contract address (`0x` followed by 40 hex characters). |
| `events` | string\[] | No | Specific event signatures (human-readable, not encoded) to match — e.g. `"Transfer(address,address,uint256)"`. If omitted, all logs from the contract are delivered. |
| `dataset_version` | string | No | Override the dataset version for chains that don't use `1.0.0` (e.g., `megaeth_testnet_v2`). If not specified, the server auto-detects the correct version. |
#### Failure Handling
Onchain-triggered tasks honor the task's [`retry_config`](./app-configuration#retry-configuration). When a task throws (or otherwise fails), Compose retries it up to `max_attempts` with the configured backoff. If every attempt fails, **that event is dropped and the pipeline advances to the next one** — it is not replayed.
This means an unhandled exception in an onchain-triggered task only stalls delivery for the duration of your retry window, not indefinitely. If you want a failure to be visible without consuming retries, catch the error in your task and return normally — the run is recorded as successful and the pipeline moves on.
## Cron Triggers
Tasks can be triggered on a schedule. Tasks triggered by cron are invoked with an empty payload (`{}`), since each invocation is generic.
### Example
```yaml highlight={6-7} theme={null}
name: "my-app"
tasks:
- name: "hourly_sync"
path: "./tasks/sync.ts"
triggers:
- type: "cron"
expression: "0 * * * *" # Every hour at minute 0
```
### 6-field (second-granularity) example
Pass a 6-field expression if you need second-level precision — the first field becomes seconds:
```yaml theme={null}
triggers:
- type: "cron"
expression: "*/30 * * * * *" # every 30 seconds
```
#### Cron Trigger Properties
| Property | Type | Required | Description |
| ------------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type` | `"cron"` | Yes | Type discriminator |
| `expression` | string | Yes | A cron expression. Supports both 5-field (`minute hour day month weekday`) and 6-field (`second minute hour day month weekday`) formats. Accepts `*`, numbers, ranges (`1-5`), lists (`1,3,5`), and step values (`*/2`). |
## HTTP Triggers
Tasks can be triggered by HTTP requests. This is often used to kick off compose tasks from your application logic. HTTP triggers accept any JSON payload you want for dynamic execution.
### Example
```yaml highlight={6-7} theme={null}
name: "my-app"
tasks:
- name: "status"
path: "./tasks/status.ts"
triggers:
- type: "http"
authentication: "auth_token" # other option is "none"
```
#### Http Trigger Properties
| Property | Type | Required | Description |
| ---------------- | -------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `"http"` | Yes | Type discriminator |
| `authentication` | `"auth_token" \| "none"` | Yes | Type of authentication to use on the endpoint. |
| `role` | `"Owner" \| "Admin" \| "Editor" \| "Viewer"` | No | The minimum RBAC role required to call the endpoint (only applies when `authentication` is `"auth_token"`). Defaults to `"Viewer"` (any team member). |
| `ip_whitelist` | string\[] | No | Restrict access to specific IPs. Supports IPv4, IPv6, and CIDR notation (e.g. `["192.168.1.0/24", "10.0.0.1"]`). |
Authenticated triggers require a Goldsky API token when the app is deployed, but can be called without auth when testing locally. Unauthenticated triggers can be called without auth both locally and when deployed.
### Base URL
Locally, Compose runs on `http://localhost:4000` by default. If port 4000 is taken, `start` picks the next free port up to 4009 and records it in `.compose/.port`. The base pattern for all task endpoints is:
```
http://localhost:4000/tasks/{taskName}
```
When deployed to the cloud, tasks with an authenticated HTTP trigger (`authentication: "auth_token"`) are reachable at:
```
https://api.goldsky.com/api/admin/compose/v1/{appName}/tasks/{taskName}
```
When deployed to the cloud, tasks with an unauthenticated HTTP trigger (`authentication: "none"`) are reachable at:
```
https://api.goldsky.com/api/public/compose/v1/{projectId}/{appName}/tasks/{taskName}
```
### HTTP Request Format
```bash theme={null}
curl -X POST http://localhost:4000/tasks/{taskName} \
-H "Content-Type: application/json" \
-d '{"key": "value"}'
```
When deployed to the cloud with `authentication: "auth_token"`, you'll need to pass a Goldsky API token:
```bash highlight={2} theme={null}
curl -X POST https://api.goldsky.com/api/admin/compose/v1/{appName}/tasks/{taskName} \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"foo": "foo"}'
```
### Execute HTTP trigger without parameters
```bash highlight={3} theme={null}
curl -X POST http://localhost:4000/tasks/bitcoin-oracle \
-H "Content-Type: application/json" \
-d '{}'
```
### Execute an HTTP trigger with parameters
```bash highlight={4-5} theme={null}
curl -X POST http://localhost:4000/tasks/bitcoin-oracle \
-H "Content-Type: application/json" \
-d '{
"contractAddress": "0x742d35Cc6634C0532925a3b8D6Ac6E7D9C3c1234",
"threshold": 1000
}'
```
### Response Format
Tasks return JSON responses with the data returned by the task's `main` function.
Calling this task:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main(_context: TaskContext, payload: { name?: string }) {
const name = payload?.name || "World";
return { success: true, greeting: `Hello, ${name}!` };
}
```
via this request:
```bash theme={null}
curl -X POST http://localhost:4000/tasks/greeting \
-H "Content-Type: application/json" \
-d '{
"name":"Adam"
}'
```
returns this JSON response:
```json theme={null}
{
"success": true,
"greeting": "Hello, Adam!"
}
```
## Next Steps
You can use any sandbox compatible typescript packages with any package manager.
Debug and monitor your apps
# Task Authoring Basics
Source: https://docs.goldsky.com/compose/tasks
Author Compose tasks as TypeScript modules with a main function, sandboxed runtime, and access to context functions.
Tasks are the core execution units in Compose. A task is a durable workflow that can execute its own logic as well as trigger other tasks. Tasks run in a sandboxed environment
and cannot access the file system, network, blockchains, etc, without communicating outside of the sandbox by calling [Context Functions](./context). This allows all task functionality to be fully auditable and verifiable.
Each task is a TypeScript module that exports a `main` function with access to context functions for durable operations.
The `main` function receives the injected context as its first argument and the payload sent by the trigger as its second argument.
Types for context functions are stored in the local `.compose/` folder and can be referenced from your task files like so:
```typescript theme={null}
import { TaskContext } from "compose";
```
## Task Structure
Every task must export a `main` function with this signature:
```typescript theme={null}
import { TaskContext } from "compose";
export async function main(
// injected by compose at runtime
context: TaskContext,
// the payload sent with the trigger (e.g. an HTTP request body or onchain log).
// Empty when a task is triggered by cron.
payload?: Record,
): Promise {
// Task implementation
}
```
The runtime loads your task file and looks for an exported `main` function. If none is found, the task fails to load. Default exports, `run`, and lifecycle hooks like `onStart` / `onShutdown` are not supported — `main` is the single entrypoint.
## Task Triggers
Tasks are invoked by "triggers" — see [Task Triggers](./task-triggers) for details. The supported trigger types are:
* **Cron** — called on a schedule expressed as a cron expression, with no payload.
* **HTTP** — called by your application or CLI with various auth options. The request body is passed as the payload.
* **Onchain** — triggered by onchain events, called with a raw log payload decodable via [Contract classes](./context/evm/contracts).
## Task-Level Retry Configuration
Configure retries in your app manifest:
```yaml highlight={4-7} theme={null}
tasks:
- name: "unreliable_task"
path: "./tasks/flaky.ts"
retry_config:
max_attempts: 5
initial_interval_ms: 2000
backoff_factor: 1.5
```
**How Task Retries Work:**
1. **Task Failure**: If the task throws an error, the durable execution engine marks the attempt as failed.
2. **Retry Delay**: Waits `initial_interval_ms` before the first retry.
3. **Exponential Backoff**: Each subsequent retry interval is the previous one multiplied by `backoff_factor`.
4. **Retry Sequence** (for the example above): delays of 2000ms → 3000ms → 4500ms → 6750ms between the 5 attempts.
5. **Final Failure**: After `max_attempts` attempts, the task is permanently failed.
6. **Default Behavior**: When `retry_config` is omitted, tasks default to `max_attempts: 3`, `initial_interval_ms: 1000`, and `backoff_factor: 2`.
## Task Context
A task's `main()` function is invoked with a context object — see [Context](./context/overview) for full details. Here's a brief overview of what context enables:
* State management — [Collections](./context/collections)
* Blockchain interactions — [evm](./context/evm/overview)
* HTTP requests — [fetch](./context/fetch)
* Task-to-task execution — [callTask](./context/call-task)
* Reading env variables — [env](./context/env)
## Next Steps
Get detailed API docs for all available context functions for HTTP, blockchain, and database operations.
Learn how to trigger tasks and query your application via HTTP API.
# Flashblocks
Source: https://docs.goldsky.com/edge-rpc/capabilities/flashblocks
Faster block confirmations on OP Stack chains via Edge RPC
Flashblocks provide faster block confirmations on OP Stack chains by streaming pre-confirmed blocks before they're finalized on L1. Edge RPC routes to flashblocks-enabled upstreams on supported networks.
## Supported Networks
| Network | Chain ID | Alias |
| ---------------- | -------- | ------------------ |
| Base | 8453 | `base` |
| Base Sepolia | 84532 | `base-sepolia` |
| Optimism | 10 | `optimism-mainnet` |
| Optimism Sepolia | 11155420 | `optimism-sepolia` |
| Unichain | 130 | `unichain-mainnet` |
| Unichain Testnet | 1301 | `unichain-testnet` |
## Usage
Target flashblocks-enabled upstreams by adding `use-upstream=flashblocks*` to your request:
```bash theme={null}
https://edge.goldsky.com/standard/evm/8453?secret=demo&use-upstream=flashblocks*
```
```bash curl theme={null}
curl "https://edge.goldsky.com/standard/evm/8453?secret=demo&use-upstream=flashblocks*" \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```
```javascript ethers.js theme={null}
import { JsonRpcProvider } from 'ethers'
const provider = new JsonRpcProvider(
'https://edge.goldsky.com/standard/evm/8453?secret=demo&use-upstream=flashblocks*'
)
const blockNumber = await provider.getBlockNumber()
```
```javascript viem theme={null}
import { createPublicClient, http } from 'viem'
import { base } from 'viem/chains'
const client = createPublicClient({
chain: base,
transport: http('https://edge.goldsky.com/standard/evm/8453?secret=demo&use-upstream=flashblocks*')
})
const blockNumber = await client.getBlockNumber()
```
You can also pass the directive as a header instead of a query parameter:
```bash theme={null}
curl "https://edge.goldsky.com/standard/evm/8453?secret=demo" \
-X POST \
-H "Content-Type: application/json" \
-H "X-ERPC-Use-Upstream: flashblocks*" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```
# HyperEVM System Tx
Source: https://docs.goldsky.com/edge-rpc/capabilities/hyperevm-system-transactions
Route to archive or realtime node pools on HyperEVM via Edge RPC
HyperEVM (Chain ID: 999) has two distinct node pools with different transaction visibility:
| Type | Node | Description | Use Case |
| ----------- | -------- | ---------------------------------------------------------- | --------------------------------------- |
| `systx*` | nanoreth | Full archive nodes, **includes system transactions** | Indexing, historical queries, debugging |
| `standard*` | hlnode | Realtime-optimized nodes, **excludes system transactions** | Frontend dApps, realtime data |
If you don't specify a node type, requests may load-balance across both pools, causing inconsistent results.
## Archive Nodes (with system transactions)
```bash theme={null}
https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=systx*
```
```bash curl theme={null}
curl "https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=systx*" \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1", true],"id":1}'
```
```javascript ethers.js theme={null}
import { JsonRpcProvider } from 'ethers'
const provider = new JsonRpcProvider(
'https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=systx*'
)
// Archival data with system transactions
const block = await provider.getBlock(1)
```
```javascript viem theme={null}
import { createPublicClient, http } from 'viem'
const client = createPublicClient({
transport: http('https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=systx*')
})
const block = await client.getBlock({ blockNumber: 1n })
```
## Realtime Nodes (without system transactions)
```bash theme={null}
https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=standard*
```
```bash curl theme={null}
curl "https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=standard*" \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```
```javascript ethers.js theme={null}
import { JsonRpcProvider } from 'ethers'
const provider = new JsonRpcProvider(
'https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=standard*'
)
const blockNumber = await provider.getBlockNumber()
```
```javascript viem theme={null}
import { createPublicClient, http } from 'viem'
const client = createPublicClient({
transport: http('https://edge.goldsky.com/standard/evm/999?secret=demo&use-upstream=standard*')
})
const blockNumber = await client.getBlockNumber()
```
# x402 nanopayments
Source: https://docs.goldsky.com/edge-rpc/capabilities/x402
Pay-per-request access to Edge RPC over the x402 HTTP protocol, no account required