MCPInstall

compose-reference

Load this skill whenever building, editing, or deploying a Goldsky Compose app — it is the reference layer that gives the concrete rules for how to build one: the exact shape of compose.yaml (every top-level, task, and trigger field), every goldsky compose CLI flag, the TaskContext API (env, fetch, callTask, logEvent, evm, collection), wallet APIs (smart wallet, BYO EOA), gas sponsorship, contract codegen, the dashboard URL, and pricing. Consult it before writing or editing any compose.yaml or task file — do not synthesize the manifest/CLI/API shape from memory — and also to answer any user question about how Compose works or what a field, flag, or API does. Pairs with /compose (the entry-point build guide, loaded first); use /compose-doctor to debug a broken app. Do NOT load for Turbo, Mirror, Subgraphs, Edge, or Boost — those have their own reference skills.

Installs
97
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/goldsky-io/goldsky-agent --skill compose-reference
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads compose-reference automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Goldsky Compose Reference

Reference for the compose.yaml manifest, the full goldsky compose CLI surface, the TaskContext API, wallets, gas sponsorship, contract codegen, the dashboard, and pricing. For interactive build flows use /compose; for debugging use /compose-doctor.

This is the reference layer of the Compose skill family. /compose (loaded first) carries the general build rules and concepts; the template skills (/compose-bitcoin-oracle, /compose-vrf, /compose-dividend-distribution, /compose-compliance-oracle) carry example app source. Load this skill for any concrete field, flag, manifest shape, or API signature — and always before writing a compose.yaml or task file, rather than synthesizing the shape from memory.

Always validate the manifest before deploying. goldsky compose start catches schema errors fast.

Sandbox import rule — get this wrong and the task fails to bundle or crashes at runtime. Two things are NEVER imported: the Compose runtime capabilities and the EVM SDK. env, fetch, callTask, logEvent, evm (wallets, chains, contracts, decodeEventLog), and collection all come from the injected context argument — there is no @goldsky/compose-evm (or similar) package to import; reach chains via context.evm.chains.<name>, never by importing viem for them. Beyond that, what you may import depends on whether the app has a package.json:

  • No package.json (Deno-style app, e.g. bitcoin-oracle): import ONLY the compose module (for types, import type { TaskContext } from "compose") and sibling project files (./lib/utils, ../contracts/Foo). Any other bare import is rejected by the bundler.
  • Has a package.json (esbuild-bundled, e.g. copy-trader with viem/@ethersproject/wallet, solana with gill): npm deps declared there ARE bundled and importable for local/pure use (crypto, signing, encoding). The hard limit is the network: Compose tasks run in a sandbox with no outbound socket of their own, so any package that does its own HTTP at runtime (axios, node-fetch, an SDK's built-in HTTP client) fails — route every network call through context.fetch and use only the SDK's pure utilities.

So: match the example you're scaffolding from. If it ships a package.json, keep its npm imports; if it doesn't, don't introduce any.

Quick Reference

Most common lookups:

compose.yaml Manifest

Top-level fields

FieldTypeRequiredNotes
namestringyes/^[a-zA-Z0-9]([a-zA-Z0-9_\-]*[a-zA-Z0-9])?$/, starts and ends with a letter or number; letters, numbers, underscores, hyphens. Uppercase and leading digits are allowed. The platform additionally rejects a name that canonicalizes (lowercase, [-_]+->-) onto an existing app's, with a 409, so my-app, My_App, and my__app cannot coexist
api_versionstringdeploy-onlysemver (e.g. 0.1.0) or stable / preview / canary (any internal-* prefix is also accepted)
tasksarrayyesNon-empty
secretsstring[]noNames only — values set via compose secret set
env{ local?, cloud? }noenv's only valid children are local and cloud — each a Record<string, string> flattened into context.env. A bare env.MY_VAR (a var name directly under env) is rejected: "not a valid key". A hardcoded per-app constant belongs in the task file, not here.

Task fields

FieldTypeRequiredNotes
namestringyes/^[a-zA-Z0-9][a-zA-Z0-9_.\-]*$/, starts with a letter or number; letters, numbers, underscores, hyphens, dots. A leading underscore is no longer allowed (_internal_task is now rejected)
pathstringyesRelative path to the .ts task file
triggersarrayyesOne or more; at most one per type
retry_configobjectno{ max_attempts, initial_interval_ms, backoff_factor } - all three required when set; the manifest validator rejects a task with any field outside name, path, retry_config, triggers

Trigger types

cron

- type: cron
  expression: "*/15 * * * *" # 5-field cron

http

- type: http
  authentication: auth_token # or "none"
  ip_whitelist: ["1.2.3.4", "10.0.0.0/8"] # optional, IPv4/IPv6/CIDR

onchain_event

- type: onchain_event
  network: polygon_amoy # snake_case required
  contract: "0xYourContractAddress" # 0x + 40 hex
  events:
    - "Transfer(address,address,uint256)" # viem signature strings, optional
  dataset_version: "..." # optional

Full manifest example

name: my-app
api_version: stable
secrets:
  - COINGECKO_API_KEY
  - ORACLE_SIGNER_KEY
env:
  cloud:
    LOG_LEVEL: info
  local:
    LOG_LEVEL: debug
tasks:
  - name: update_oracle
    path: src/tasks/update-oracle.ts
    retry_config:
      max_attempts: 3
      initial_interval_ms: 1000
      backoff_factor: 2
    triggers:
      - type: cron
        expression: "*/5 * * * *"
  - name: manual_trigger
    path: src/tasks/manual-trigger.ts
    triggers:
      - type: http
        authentication: auth_token

CLI Commands

All commands accept -t/--token and --api-server; the -n/--name flag selects the app by name (falls back to -m/--manifest, then ./compose.yaml). Token precedence is --token > the GOLDSKY_API_TOKEN env var > ~/.goldsky/auth_token (written by goldsky login). With none of the three the CLI errors with "Please run goldsky login, set GOLDSKY_API_TOKEN, or pass --token to the command."

Non-interactive guards. init without a name, deploy with a major api_version mismatch (needs --force, message "Refusing to deploy with a major api_version mismatch in non-interactive mode. Pass --force to override."), and clean without -f ("Use --force for non-interactive cleanup.") all abort in a non-TTY. Agents always run non-TTY.

Lifecycle

CommandPurposeKey flags
compose initScaffold new app[name] (prompts only when a TTY and no name)
compose startRun locally (there is no dev command)--fork-chains, --cloud, --impersonate, -p/--port
compose deployBundle + upload to cloud-m, -t, -f (Skip version compatibility prompts (required to deploy with a major version mismatch when not running in a terminal)), --sync-env, --json
compose statusShow runtime status-n, --json
compose listList all apps--json
compose historyShow deployment history for an app-n, --limit (default 20, server caps at 100), --offset (default 0), --include-failures, --json
compose pausePause-n, --json
compose resumeResume-n, --json
compose deleteDelete (type-to-confirm; --force for CI)-n, --force, --delete-database, --json
compose logsView / tail logs-f, --tail (default 100), --level, --search, --since, --max-lines, --timeout <duration>, --json
compose cleanWipe local .compose/stage.db-f, -c/--config <config>
compose update [version]Re-download the compose binary[version] (stable/preview/semver), --preview
compose callTask <task_name> <payload>Invoke a task with a JSON payload. Defaults to the deployed app--env <local|cloud> (default cloud), -p/--port <port> (implies local; --port with an explicit --env cloud is an error), -n, -m, -t, --api-server, --json

Local port resolution for callTask --env local: --port flag > .compose/.port > 4000. Connection refused, an unknown task name, and a stale port file each produce a distinct message and exit 1 (codes CONNECTION_REFUSED, TASK_NOT_FOUND, INVALID_RESPONSE, INVALID_PAYLOAD, INVALID_ENV, INVALID_FLAGS).

Secrets

CommandPurpose
compose secret set <SECRET_NAME> --value <value> [-n <app>] [--env local|cloud] [--redeploy]Set a secret (name is positional; -n = app)
compose secret delete <secret_name> [-n <app>] [--env local|cloud]Delete
compose secret list [-n <app>]List (no --env flag)
compose deploy --sync-envUpload all of .env to cloud at deploy time (there is no secret sync)

Wallets

CommandPurpose
compose wallet create <wallet_name> [-n <app>] [--env local|cloud] [--json]Create managed wallet; prints address (name is positional; -n = app)
compose wallet list [-n <app>] [--env local|cloud] [--json]Table: name, address, type (privy / private_key / tevm), created_at

Codegen

compose codegen — parse all src/contracts/*.json ABIs, write .compose/generated/index.ts and .compose/types.d.ts. Runs automatically inside compose init, compose start, and during deploy.

Contracts

Compile and deploy a contract, or submit a write call, straight from the CLI — no Foundry needed (deployContract bundles solc). Added in 0.8.0; the forge-style multi-arg/array constructor syntax needs ≥ 0.8.1 — run goldsky compose update if you're older.

compose deployContract <file.sol> — compiles in-CLI and deploys via a CREATE2 proxy through the app's Compose wallet. The forge-style multi-arg/array constructor syntax needs ≥ 0.8.1.

FlagPurpose
--chain-id <id>Target chain (required). e.g. Base Sepolia 84532, Base 8453, Polygon 137, Polygon Amoy 80002, Arbitrum 42161, Optimism 10.
--constructor-args <tokens...>Forge-style: space-separated, one token per param; arrays "[a,b]", tuples "(a,b)", nesting allowed, negatives " -5" (quoted leading space). Coerced against the compiled ABI.
--wallet <name>App wallet that deploys (default default). Match the evm.wallet({ name }) the task code uses when the contract must authorize that wallet.
--verifyVerify the contract on the block explorer.
--forceBypass the msg.sender-in-constructor guard (the sender is the CREATE2 proxy, not the wallet).
-m / -tManifest path / project token (see Lifecycle).
--api-serverAPI server URL (global flag).
--jsonJSON output (see CLI JSON Schemas).

Run from the app directory - it reads compose.yaml for the app name; app name + project id derive the deterministic CREATE2 salt. On gas-sponsored chains (Base 8453, Base Sepolia 84532) it needs no funded key and no RPC URL. deployContract routes through the cloud's Alchemy bundler, which covers Ethereum, Sepolia, Polygon, Polygon Amoy, Arbitrum, Arbitrum Sepolia, Optimism, Optimism Sepolia, Base and Base Sepolia (chain IDs 1, 11155111, 137, 80002, 42161, 421614, 10, 11155420, 8453, 84532). On a chain outside that set the deploy fails with No Alchemy bundler URL for chain <id>, so use the forge create fallback there (the constructor args stay the same; supply the ABI to src/contracts/ yourself). Multi-provider coverage is tracked as FOU-991. Runtime task-gas sponsorship (see Gas Sponsorship) covers many more chains than this deploy endpoint.

Wallet lifecycle. wallet create and wallet list now work even before the app is deployed (they provision the hosted store on demand, like deployContract/writeContract); wallet create returns the wallet's address pre-deploy. To authorize a wallet inside a constructor: wallet create <name> → deployContract --constructor-args <address> → wire the address into the task → compose deploy.

CREATE2 collisions. Re-deploying the same contract source + constructor args from the same app hits a CREATE2 collision: the platform returns HTTP 400 CONTRACT_ALREADY_DEPLOYED and the CLI reports "This contract has already been deployed with this app. The same contract + app name + project produces the same address via CREATE2." with exit 1 and no Deploy Block (--json code ALREADY_DEPLOYED; any other deploy failure is DEPLOY_CONTRACT_FAILED). A changed constructor arg, changed source, or a different app name produces a fresh address. Note the app name participates in the CREATE2 salt - renaming the app changes every future deploy address. On success it prints the contract address, tx hash, and Deploy Block, and auto-saves the ABI to src/contracts/<Name>.json - then compose codegen gives typed bindings.

compose writeContract — submit a write call to a deployed contract.

FlagPurpose
--chain-id <id>Target chain (required).
--to <address>Target contract address (required).
--function "sig(types)"Function signature, e.g. "setValue(uint256)".
--args <tokens...>Same forge-style grammar as --constructor-args.
--data <hex>Raw calldata alternative to --function / --args.
--value <amount>Native value to send; suffix a unit (wei, gwei, ether), e.g. --value 1ether.
--wallet <name>App wallet that signs (default default).
--api-serverAPI server URL (global flag).
--jsonJSON output (see CLI JSON Schemas).

Read-back

CommandPurposeKey flags
compose runs [runId]List runs, or show one run's detail--limit, --offset, --task <name>, --status <success|error|pending>, --since <1h|30m|7d>, --until <duration>, --json, -n/-m, -t, --api-server
compose collections listTable of the app's collection namestargeting + --json
compose collections query <collectionName>Query one collection--filter <json> (must be a JSON object), --limit (default 100, backend max 1000), --offset, --json, targeting
compose source [taskName]Print the deployed app's source file list, or one task's source. Never writes to disktargeting + --json
compose downloadDownload the deployed app's source archive-o/--output <path> (default <app>.zip, refuses to overwrite an existing file), --json, targeting

TaskContext API

main(context: TaskContext, params?: Record<string, unknown>): Promise<unknown> receives:

type TaskContext = {
  env: Record<string, string>;
  logger: {
    info(message: string, data?: Record<string, unknown>): void;
    warn(message: string, data?: Record<string, unknown>): void;
    error(message: string, data?: Record<string, unknown>): void;
  };
  fetch: FetchFn;
  callTask: <Args, T>(name: string, args: Args, retryConfig?: RetryConfig) => Promise<T>;
  logEvent: (event: { code: string; message: string; data?: unknown }) => Promise<void>;
  evm: {
    chains: Record<string, Chain>;              // re-exported from viem internally — access via context.evm.chains.<name>, do NOT import viem
    wallet: (config: WalletConfig) => Promise<IWallet>;
    decodeEventLog: <T>(abi: AbiItem[], log: OnchainEvent) => Promise<T>;
    contracts: Record<string, ContractClass>;   // populated by codegen
  };
  collection: <T>(name: string, indexes?: CollectionIndexSpec[]) => Promise<Collection<T>>;
  sideEffect: <T>(fn: () => T | Promise<T>) => Promise<T>;
};

No secrets namespace. Secrets flatten into context.env. For output: context.logger.info/warn/error(message, data?) is the structured, run-correlated logger (each line carries taskName, runId, appId, level, timestamp). console.log is fine for free-form output. logEvent still works but is marked @deprecated in the runtime types and will be removed in a future major version.

fetch (overloads)

type FetchConfig = {
  method?: string;                                  // defaults to "GET"
  headers?: Record<string, string>;
  body?: Record<string, unknown> | string;          // objects are JSON.stringify'd
};

interface FetchFn {
  <T>(url: string, retryConfig?: RetryConfig): Promise<T | undefined>;
  <T>(url: string, config?: FetchConfig, retryConfig?: RetryConfig): Promise<T | undefined>;
}
  • The second argument is a config object, not a bare body: ctx.fetch(url, { method: "POST", body: { a: 1 }, headers: { "X-Key": k } }). Passing a raw payload object as the second argument does not send a body. Unrecognized keys are dropped and the request goes out as a GET.
  • A non-2xx response throws Fetch failed with status <code> <statusText>: <body>.
  • The response is JSON-parsed and returns undefined when the body is not JSON.
  • This is not window.fetch. Use this, not native fetch.

callTask

callTask<Args, T>(name: string, args: Args, retryConfig?: RetryConfig): Promise<T>
  • T is whatever the callee returns. A void-returning task resolves to undefined.
  • Use for task-to-task invocation (parent/child patterns).

sideEffect

sideEffect<T>(fn: () => T | Promise<T>): Promise<T>

Wraps a non-deterministic value (timestamp, UUID, random) so it is cached like any other context call. Durable resumption replays a task from the start, so an unwrapped Date.now() changes on replay. The callback runs once. On replay the host returns the cached value and the callback never runs.

RetryConfig

type RetryConfig = {
  max_attempts: number;         // ≥0
  initial_interval_ms: number;  // >0
  backoff_factor: number;       // >0
};

All three fields are required when you pass a retryConfig explicitly (the manifest validator enforces this for retry_config too). Omitting it does not mean one attempt:

ScopeDefault
A task with no retry_config{ max_attempts: 3, initial_interval_ms: 1000, backoff_factor: 2 }
Safe/read-only context calls: readContract, simulate, getBalance, and ctx.fetch with GET/HEAD/OPTIONS{ max_attempts: 3, initial_interval_ms: 500, backoff_factor: 2 }
Everything else (callTask, writeContract, sendTransaction, prepareUserOperation, submitSignedUserOperation, wallet create/save, and ctx.fetch with POST/PUT/PATCH/DELETE){ max_attempts: 1, initial_interval_ms: 500, backoff_factor: 2 }, held at 1 deliberately to avoid blind retries of non-idempotent calls

OnchainEvent (for decodeEventLog and onchain_event triggers)

type OnchainEvent = {
  blockNumber: number;
  blockHash: string;
  transactionIndex: number;
  removed: boolean;
  address: string;
  data: Hex;
  topics: Hex[];
  transactionHash: string;
  logIndex: number;
};

For onchain_event-triggered tasks, params contains { log: OnchainEvent } plus chain-specific metadata. decodeEventLog(abi, params.log) returns the decoded struct.

IWallet

interface IWallet {
  readonly name: string;
  readonly address: Address;
  writeContract(
    chain: Chain,
    contractAddress: Address,
    functionSig: string,                 // signature string only, no ABI item
    args: unknown[],
    confirmation?: TransactionConfirmation,
    retryConfig?: RetryConfig,
  ): Promise<{ hash: string; receipt: TransactionReceipt; userOpHash?: string }>;
  sendTransaction(
    config: {
      to: Address; data: Hex; chain: Chain;
      value?: bigint; maxFeePerGas?: bigint; maxPriorityFeePerGas?: bigint;
      gas?: bigint; nonce?: number;
    },
    confirmation?: TransactionConfirmation,
    retryConfig?: RetryConfig,
  ): Promise<{ hash: string; receipt: TransactionReceipt; userOpHash?: string }>;
  readContract<T = unknown>(
    chain: Chain, contractAddress: Address, functionSig: string,
    args: unknown[], retryConfig?: RetryConfig,
  ): Promise<T>;
  simulate(                              // throws on revert
    chain: Chain, contractAddress: Address, functionSig: string,
    args: unknown[], retryConfig?: RetryConfig,
  ): Promise<unknown>;                   // viem simulateContract result
  getBalance(chain: Chain, retryConfig?: RetryConfig): Promise<string>; // decimal wei string
}

type TransactionConfirmation = {
  confirmations?: number;
  onReorg?: {
    action:
      | { type: "replay" }
      | { type: "log"; logLevel?: "error" | "info" | "warn" }   // default "error"
      | { type: "task"; task: string };
    depth: number;
  };
};

TransactionReceipt carries status: "success" | "reverted", blockNumber: bigint, blockHash, gasUsed: bigint, effectiveGasPrice: bigint, cumulativeGasUsed: bigint, from, to, contractAddress: Address | null, logs: Log[], logsBloom, transactionHash, transactionIndex, type.

Specifically: there is no string | AbiItem overload; retryConfig is a separate 6th positional arg, not a key in an options bag; there are no gas/gasPrice options on writeContract; the return has no chainId and no top-level blockNumber (the TxResult type as documented does not exist); sendTransaction is object-first, the positional (chain, to, value, data?, options?) form does not exist; simulate returns { result, request } and throws on revert, so SimulateResult { success, ... } and if (!sim.success) are dead code; getBalance returns a decimal wei string, so arithmetic without BigInt(...) string-concatenates; onReorg.action.type is "replay" | "log" | "task", there is no "skip".

Collection

type CollectionIndexSpec = {
  path: string;
  type: "text" | "numeric" | "boolean" | "timestamptz";
  unique?: boolean;
};

interface Collection<T> {
  readonly name: string;
  insertOne(doc: T, opts?: { id?: string }): Promise<{ id: string }>;
  findOne(filter: Filter): Promise<(T & { id: string }) | null>;
  findMany(filter: Filter, options?: { limit?: number; offset?: number }): Promise<Array<T & { id: string }>>;
  getById(id: string): Promise<(T & { id: string }) | null>;
  setById(id: string, doc: T, opts?: { upsert?: boolean }): Promise<{ id: string; upserted?: boolean; matched?: number }>; // upsert defaults true; false throws if absent
  deleteById(id: string): Promise<{ deletedCount: number }>;
  drop(): Promise<void>;
}

collection<T>(name, indexes?) takes CollectionIndexSpec[], not string[]. findMany options are { limit?, offset? }: skip does not exist and is silently ignored, so paging written against it always returns page 1. Reads return T & { id: string }. A filter is flat Record<string, string | number | boolean | HelperValue>, so nested-path filters are not supported.

Filter operators: $gt, $gte, $lt, $lte, $in, $ne, $nin, $exists. Equality: { field: value }.

CLI JSON Schemas

For agents parsing --json output:

compose status -n <app> --json

{
  "name": "my-app",
  "status": "RUNNING",
  "created_at": 1771630350411,
  "updated_at": 1774473580871
}

status is one of RUNNING, PAUSED, STARTING, STOPPING, ERROR, NOT_FOUND (the value comes from the API, so treat the list as non-exhaustive). Timestamps are ms epoch.

compose list --json

[
  { "name": "my-app", "status": "RUNNING", "created_at": 1771630350411, "updated_at": 1774473580871 }
]

compose logs --json

NDJSON (one object per line):

{"timestamp":"2026-04-20T10:00:00Z","level":"info","message":"..."}

Exactly three fields. There is no dashboard_url in CLI log output. To link a user to a specific run, get the run id from compose runs and build https://app.goldsky.com/<project_id>/dashboard/compose/<app-name>/runs/<run_id> yourself. The CLI's logs command also has no --run-id filter (the underlying API accepts one, the CLI does not pass it), so use compose runs <runId> for per-run detail.

compose secret list -n <app> --json

[{ "name": "MY_SECRET", "created_at": 1771630350411 }]

Values are never returned.

compose wallet list --json

[{ "name": "updater", "address": "0x...", "type": "privy", "created_at": 1771630350411 }]

type is one of privy (smart wallet), private_key (BYO EOA), tevm (local forked).

Errors in --json mode

In --json mode stdout carries only the result document (ascii art and progress bars are suppressed). Failures go to stderr as {"error": true, "code": "<CODE>", "message": "..."} with exit 1. Codes present in the CLI: VALIDATION_FAILED, SECRET_MISSING, DEPLOY_FAILED, DEPLOY_CONTRACT_FAILED, ALREADY_DEPLOYED, WRITE_CONTRACT_FAILED, WALLET_CREATE_FAILED, WALLET_LIST_FAILED, NOT_FOUND, TASK_NOT_FOUND, CONNECTION_REFUSED, INVALID_ENV, INVALID_FLAGS, INVALID_PAYLOAD, INVALID_RESPONSE, INVALID_FILTER, UNKNOWN.

Wallets — Deep Dive

Smart wallet (managed, Privy-backed)

const w = await evm.wallet({ name: "my-oracle" }); // sponsorGas defaults TRUE

Created cloud-side by Privy. Address is persisted. Gas-sponsored by default. Cannot be used in plain local dev - throws "You cannot use a named wallet without a private key in local dev. Start with "goldsky compose start --fork-chains" for full wallet support, or use a private key wallet: const wallet = await evm.wallet({ privateKey: MY_SECRET }); See https://docs.goldsky.com/compose/secrets for more info on private key wallets" Use compose start --fork-chains or switch to a BYO EOA for local iteration.

BYO EOA (private key)

const w = await evm.wallet({
  privateKey: env.MY_KEY,
  name: "my-pk-wallet",       // optional; defaults to the derived address
  sponsorGas: true,           // DEFAULTS TO FALSE — opt in explicitly
});

Works in both cloud and local. When sponsorGas: true, the wallet configures EIP-7702 delegation per chain on first use, then submits UserOperations through a sponsored bundler.

Gas Sponsorship

Bundler fallback order: Alchemy → Pimlico → Gelato. Override via BUNDLER_PROVIDER=<alchemy|pimlico|gelato> env var.

Supported chains

A chain is runtime-sponsorable if any of the three bundler providers covers it (tried in fallback order Alchemy → Pimlico → Gelato, each gated on its API keys being set). In the 0.8.1 source that union spans 112 chains. This is the runtime task-gas sponsorship set - far broader than the deployContract / writeContract cloud deploy path, which covers the 10-chain Alchemy set (1, 11155111, 137, 80002, 42161, 421614, 10, 11155420, 8453, 84532; FOU-991 tracks broader coverage); runtime sponsorship also covers the Arbitrum, Optimism (incl. Arbitrum Sepolia (421614) / Optimism Sepolia (11155420)), Polygon, Ethereum and BNB families, among many others. Don't hardcode the list - it changes; confirm current coverage on the Goldsky docs chains page.

Error on unsupported chain

No bundler provider available for chain <id>. Providers: alchemy: chain not supported; pimlico: missing keys (PIMLICO_API_KEY); gelato: …

Either use a supported chain or set sponsorGas: false and fund the EOA manually.

Caveats

  • onReorg is not supported for gas-sponsored transactions (warning logged, not fatal).
  • Passing a custom nonce to a sponsored sendTransaction is ignored (ERC-4337 smart wallets use a different nonce structure).

Contract Codegen (full example)

Input

Drop ABI JSON files into src/contracts/:

src/contracts/
├── ERC20.json
└── PriceFeed.json

Accepted ABI shapes: bare ABI array ([{ "type": "function", ... }, ...]), or wrapped object ({ "abi": [...] }), or a Foundry/Hardhat artifact (the generator extracts the abi field). The filename (without extension) becomes the generated class name.

Generate

goldsky compose codegen

(Also runs automatically during init, start, and deploy.)

Output

.compose/generated/index.ts exports a class per ABI. .compose/types.d.ts declares ambient types under the compose path alias (referenced in the scaffolded tsconfig.json).

Consume in a task

import type { TaskContext } from "compose";

export async function main({ evm, env }: TaskContext) {
  const wallet = await evm.wallet({ name: "oracle" });
  const PriceFeed = evm.contracts.PriceFeed;

  // Read — generated view methods call wallet.readContract under the hood
  const feed = new PriceFeed(env.FEED_ADDRESS, evm.chains.ethereum, wallet);
  const price = await feed.latestAnswer();

  // Write — generated state-changing methods call wallet.writeContract under the hood
  const tx = await feed.setPrice(1234n);
  return { price: price.toString(), hash: tx.hash };
}

Classes are exposed under context.evm.contracts.<Name>. Codegen names ending in Class (e.g. ERC20Class) are exposed as ERC20 at runtime. The generated constructor is new <Name>(address, chain, wallet) — pass an IWallet from evm.wallet(...); view methods read through it, state-changing methods write through it, both subject to the wallet's gas-sponsorship setting.

Supported Chains

context.evm.chains is re-exported from viem/chains. Any chain viem knows, you can address as evm.chains.<name> (e.g. evm.chains.polygonAmoy, evm.chains.monadTestnet, evm.chains.baseSepolia). For gas sponsorship specifically, see the Gas Sponsorship section — sponsorship is a subset of viem's chain list.

Dashboard

URL pattern:

https://app.goldsky.com/<project_id>/dashboard/compose/<app-name>
https://app.goldsky.com/<project_id>/dashboard/compose/<app-name>/runs/<run_id>

The dashboard shows status, secret names, logs, a Code tab file browser, a Download app button, and per-run traces. Build run URLs from a run id returned by compose runs.

Pricing

Pricing is not published. Usage is metered on three dimensions: function calls (compose_function_calls), worker hours (compose_worker_hours), and gas spend (compose_gas_spend). Gas spent by writeContract and deployContract is billed the same as runtime task gas. Per-unit prices are set per contract, so point the user at https://goldsky.com/pricing rather than quoting a tier.

Related

  • /compose — Build a new app or explain what Compose is.
  • /compose-doctor — Diagnose and fix broken apps.
  • /auth-setup — goldsky login help.
  • /secrets — General secret management.

FAQ

What does the compose-reference skill do?

Load this skill whenever building, editing, or deploying a Goldsky Compose app — it is the reference layer that gives the concrete rules for how to build one: the exact shape of compose.yaml (every top-level, task, and trigger field), every goldsky compose CLI flag, the TaskContext API (env, fetch, callTask, logEvent, evm, collection), wallet APIs (smart wallet, BYO EOA), gas sponsorship, contract codegen, the dashboard URL, and pricing. Consult it before writing or editing any compose.yaml or task file — do not synthesize the manifest/CLI/API shape from memory — and also to answer any user question about how Compose works or what a field, flag, or API does. Pairs with /compose (the entry-point build guide, loaded first); use /compose-doctor to debug a broken app. Do NOT load for Turbo, Mirror, Subgraphs, Edge, or Boost — those have their own reference skills.

How do I install compose-reference?

Run npx skills add https://github.com/goldsky-io/goldsky-agent --skill compose-reference in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/compose-reference/ (personal) or .claude/skills/compose-reference/ (project).

Which AI agents can use compose-reference?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is compose-reference safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains compose-reference?

It is published by goldsky-io in goldsky-io/goldsky-agent. It has 97 recorded installs.

More from goldsky-io/goldsky-agent

View all →

turbo-pipelines

goldsky-io/goldsky-agent

Turbo pipeline YAML reference and architecture guide. Covers: YAML field syntax (startat, from, version, primarykey), source/transform/sink configuration, validation errors, resource sizing (xs–xxl), architecture decisions (dataset vs kafka, streaming vs job, fan-out vs fan-in, sink selection, pipeline splitting). Triggers on: 'what does field X do', 'what fields does a postgres sink need', 'what resource size', 'should I use kafka or dataset', 'how to structure my pipeline'. For writing transforms, use /turbo-transforms. For end-to-end building, use /turbo-builder.

Data & Databases3.8K installs

turbo-transforms

goldsky-io/goldsky-agent

Write SQL, TypeScript, and dynamic table transforms for Turbo pipelines. Covers: decoding EVM logs with gslogdecode, filtering/casting blockchain data, UNION ALL for combining events, TypeScript/WASM transforms (invoke function), dynamic lookup tables (dynamictablecheck), transform chaining, and Solana decoding. Triggers on: 'decode Transfer events', 'write a SQL transform', 'filter by contract', 'TypeScript transform', 'dynamic table', 'UNION ALL'. For pipeline YAML structure, use /turbo-pipelines. For end-to-end building, use /turbo-builder.

turbo-doctor

goldsky-io/goldsky-agent

Diagnose and fix broken Goldsky Turbo pipelines interactively. Triggers on: pipeline in error state, stuck starting, connection refused, not getting data, duplicate rows, missing fields, slow backfill, or any named pipeline misbehaving. Runs logs/status commands, identifies root cause, and offers fixes. For CLI syntax or error pattern lookup without an active problem, use /turbo-operations instead.

3.8K installs

turbo-builder

goldsky-io/goldsky-agent

Build and deploy new Goldsky Turbo pipelines: requirements, dataset selection, YAML, validation, and deployment. Use for 'build a pipeline', 'index X on Y chain', or moving chain/contract data into Postgres, MySQL, ClickHouse, Kafka, Pub/Sub, S3, SQS, or webhooks. Also use for data goals such as Polymarket fills/positions/PnL, prediction markets, stablecoin or circulating supply, proof of reserves, sanctions/AML transfer monitoring, deposit detection, CCTP burns/mints, cross-chain settlement, payment reconciliation, wallet history streamed into the user's own database, token holder registries, tokenized equities/RWA, and wash-trading detection. A REST lookup of one wallet's balances or transfers is /feeds, not a pipeline, and so is a read of Polymarket activity, positions or balances. Debug existing pipelines with /turbo-doctor; look up syntax with /turbo-pipelines.

Data & Databases3.8K installs

datasets

goldsky-io/goldsky-agent

Use this skill when the user needs to look up or verify Goldsky blockchain dataset names, chain prefixes, dataset types, or versions. Triggers on questions like 'what\'s the dataset name for X?', 'what prefix does Goldsky use for chain Y?', 'what version should I use for Z?', or 'what datasets are available for Solana/Stellar/Arbitrum/etc?'. Also use for chain-specific dataset questions (e.g., polygon vs matic prefix, stellarnet balance datasets, solana token transfer dataset names), and to answer 'is there a dataset for X?' where X is a domain rather than a chain — decoded logs, token transfers and balances, mint/burn events for circulating supply, DEX or prediction-market activity (Polymarket), or the raw blocks, transactions and traces behind wallet history and reconciliation. Do NOT trigger for questions about CLI commands, pipeline setup, or general Goldsky architecture unless the core question is about finding the right dataset name or chain prefix. Wallet Feeds API queries are /

Data & Databases3.8K installs

auth-setup

goldsky-io/goldsky-agent

Set up Goldsky CLI authentication and project configuration. Use this skill when the user needs to: install the goldsky CLI (what's the official install command?), run goldsky login (including when the browser opens but 'authentication failed'), run goldsky project list and see 'not logged in' or 'unauthorized', switch between Goldsky projects, check which project they're currently authenticated to, or fix 'unauthorized' errors when running goldsky turbo commands. Also use for 'walk me through setting up goldsky CLI from scratch for the first time', and when they just installed Goldsky and say 'Set it up and help me get started'. If any other Goldsky skill hits an auth error, redirect here first.

Backend & APIs3.8K installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs