mcpField
C
compose-reference

compose-reference

github.com/goldsky-io开发1297 次安装今天更新
代码仓库

关于 compose-reference

面向开发者和 Agent 的 Goldsky Compose 参考技能,涵盖清单、命令行、任务 API、钱包与合约流程,并说明沙箱导入限制。

主要功能

  1. 01compose.yaml 字段与触发器
  2. 02命令行指令和参数
  3. 03TaskContext 与钱包参考
  4. 04合约代码生成与部署
  5. 05沙箱导入和网络规则

适用场景

  1. 01编写 compose.yaml 前核对字段
  2. 02查询应用生命周期命令参数
  3. 03了解钱包及合约部署流程

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/compose-reference确认已加载。

详细介绍

Goldsky Compose 参考

compose.yaml 清单、完整的 goldsky compose CLI 功能、TaskContext API、钱包、Gas 赞助、合约代码生成、仪表板和定价的参考。交互式构建流程请使用 /compose;调试请使用 /compose-doctor。

这是 Compose 技能系列的参考层。先加载的 /compose 介绍通用构建规则和概念;模板技能(/compose-bitcoin-oracle、/compose-vrf、/compose-dividend-distribution、/compose-compliance-oracle)提供示例应用源码。遇到具体字段、标志、清单结构或 API 签名时,请加载此技能;在编写 compose.yaml 或任务文件之前,也务必先加载此技能,不要凭记忆拼凑结构。

部署前务必验证清单。 goldsky compose start 可快速捕获架构错误。

沙盒导入规则——弄错会导致任务打包失败或在运行时崩溃。 有两类内容绝不能导入:Compose 运行时能力和 EVM SDK。env、fetch、callTask、logEvent、evm(钱包、链、合约、decodeEventLog)和 collection 都来自注入的 context 参数——不存在 @goldsky/compose-evm(或类似)软件包;访问链时应使用 context.evm.chains.<name>,绝不要通过导入 viem 来访问。除此之外,可导入的内容取决于应用是否有 package.json:

  • 没有 package.json(Deno 风格应用,例如 bitcoin-oracle): 只导入 compose 模块(用于类型,例如 import type { TaskContext } from "compose")和同级项目文件(./lib/utils、../contracts/Foo)。任何其他裸导入都会被打包器拒绝。
  • 有 package.json(使用 esbuild 打包,例如带有 viem/@ethersproject/wallet 的 copy-trader、使用 gill 的 solana): 其中声明的 npm 依赖会被打包,可以导入用于本地/纯函数用途(加密、签名、编码)。硬性限制在于网络:Compose 任务在沙盒中运行,无法自行建立外部套接字连接,因此任何在运行时自行发起 HTTP 请求的软件包(axios、node-fetch、SDK 内置的 HTTP 客户端)都会失败——所有网络调用都必须通过 context.fetch,且只能使用 SDK 的纯函数工具。

因此,请遵循所参考的示例。如果示例附带 package.json,就保留其中的 npm 导入;如果没有,就不要添加任何导入。

快速参考

最常见的查阅内容:

compose.yaml 清单

顶层字段
字段类型必填备注
namestring是/^[a-zA-Z0-9]([a-zA-Z0-9_\-]*[a-zA-Z0-9])?$/,以字母或数字开头和结尾;可包含字母、数字、下划线和连字符。允许大写字母和数字开头。平台还会拒绝规范化后(转为小写,将 [-_]+ 替换为 -)与现有应用名称相同的名称,并返回 409,因此 my-app、My_App 和 my__app 不能共存
api_versionstring仅部署时semver(例如 0.1.0)或 stable / preview / canary(也接受任何以 internal-* 开头的值)
tasksarray是非空
secretsstring[]否仅名称——值通过 compose secret set 设置
env{ local?, cloud? }否env 唯一有效的子项是 local 和 cloud——每项都是一个 Record<string, string>,会扁平化到 context.env 中。裸写的 env.MY_VAR(直接放在 env 下的变量名)会被拒绝,并提示“不是有效的键”。每个应用的硬编码常量应放在任务文件中,而不是这里。
任务字段
字段类型必填备注
namestring是/^[a-zA-Z0-9][a-zA-Z0-9_.\-]*$/,以字母或数字开头;可包含字母、数字、下划线、连字符和点。不再允许以下划线开头(现在会拒绝 _internal_task)
pathstring是.ts 任务文件的相对路径
triggersarray是一个或多个;每种类型最多一个
retry_configobject否{ max_attempts, initial_interval_ms, backoff_factor }——设置时必须包含全部三项;清单验证器会拒绝包含 name、path、retry_config、triggers 以外字段的任务
触发器类型

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
完整清单示例
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 命令

所有命令都接受 -t/--token 和 --api-server;-n/--name 标志用于按名称选择应用(若未指定,则依次回退到 -m/--manifest 和 ./compose.yaml)。令牌的优先级依次为 --token > GOLDSKY_API_TOKEN 环境变量 > ~/.goldsky/auth_token(由 goldsky login 写入)。如果这三者都未设置,CLI 会报错:“请运行 goldsky login、设置 GOLDSKY_API_TOKEN,或在命令中传入 --token。”

非交互式保护。 在非 TTY 环境中,未指定名称的 init、api_version 主版本不匹配时未使用 --force 的 deploy(提示“在非交互模式下拒绝部署,因为 api_version 主版本不匹配。传入 --force 以覆盖。”),以及未使用 -f 的 clean(提示“非交互式清理请使用 --force。”)都会中止。Agent 始终在非 TTY 环境中运行。

生命周期
命令用途主要标志
compose init搭建新应用[name](仅当使用 TTY 且未提供名称时才会提示)
compose start在本地运行(没有 dev 命令)--fork-chains、--cloud、--impersonate、-p/--port
compose deploy打包并上传到云端-m、-t、-f(跳过版本兼容性提示(在未运行于终端的情况下,若主要版本不匹配,则部署时必须使用此标志))、--sync-env、--json
compose status显示运行状态-n、--json
compose list列出所有应用--json
compose history显示应用的部署历史记录-n、--limit(默认值为 20,服务端上限为 100)、--offset(默认值为 0)、--include-failures、--json
compose pause暂停-n、--json
compose resume恢复-n、--json
compose delete删除(输入名称以确认;CI 中使用 --force)-n、--force、--delete-database、--json
compose logs查看/跟踪日志-f、--tail(默认值为 100)、--level、--search、--since、--max-lines、--timeout <duration>、--json
compose clean清除本地 .compose/stage.db-f、-c/--config <config>
compose update [version]重新下载 compose 二进制文件[version](stable/preview/semver)、--preview
compose callTask <task_name> <payload>使用 JSON 负载调用任务。默认使用已部署的应用--env <local|cloud>(默认值为 cloud)、-p/--port <port>(表示使用本地环境;显式指定 --env cloud 时再使用 --port 会报错)、-n、-m、-t、--api-server、--json

callTask --env local 的本地端口解析顺序:--port 标志 > .compose/.port > 4000。连接被拒绝、任务名称未知和端口文件过期会分别显示不同的消息并以状态码 1 退出(代码 CONNECTION_REFUSED、TASK_NOT_FOUND、INVALID_RESPONSE、INVALID_PAYLOAD、INVALID_ENV、INVALID_FLAGS)。

Secrets
命令用途
compose secret set <SECRET_NAME> --value <value> [-n <app>] [--env local|cloud] [--redeploy]设置密钥(名称为位置参数;-n = 应用)
compose secret delete <secret_name> [-n <app>] [--env local|cloud]删除
compose secret list [-n <app>]列出(没有 --env 标志)
compose deploy --sync-env部署时将 .env 中的所有内容上传到云端(没有 secret sync)
Wallets
命令用途
compose wallet create <wallet_name> [-n <app>] [--env local|cloud] [--json]创建托管钱包;输出地址(名称为位置参数;-n = 应用)
compose wallet list [-n <app>] [--env local|cloud] [--json]表格:名称、地址、类型(privy / private_key / tevm)、created_at
Codegen

compose codegen — 解析所有 src/contracts/*.json ABI,并写入 .compose/generated/index.ts 和 .compose/types.d.ts。会在 compose init、compose start 内自动运行,并在部署期间运行。

Contracts

直接通过 CLI 编译并部署合约,或提交写入调用——无需 Foundry(deployContract 会打包 solc)。此功能在 0.8.0 中加入;forge 风格的多参数/数组构造函数语法需要 ≥ 0.8.1——如果版本较旧,请运行 goldsky compose update。

compose deployContract <file.sol> — 在 CLI 内编译,并通过应用的 Compose 钱包经由 CREATE2 代理部署。forge 风格的多参数/数组构造函数语法需要 ≥ 0.8.1。

标志用途
--chain-id <id>目标链(必填)。例如:Base Sepolia 84532、Base 8453、Polygon 137、Polygon Amoy 80002、Arbitrum 42161、Optimism 10。
--constructor-args <tokens...>Forge 风格:以空格分隔,每个参数对应一个 token;数组 "[a,b]"、元组 "(a,b)",支持嵌套,负数 " -5"(带引号的前导空格)。根据编译后的 ABI 进行类型转换。
--wallet <name>用于部署的应用钱包(默认值为 default)。当合约必须授权该钱包时,应与任务代码使用的 evm.wallet({ name }) 匹配。
--verify在区块浏览器上验证合约。
--force绕过构造函数中的 msg.sender 检查(发送者是 CREATE2 代理,而不是钱包)。
-m / -tManifest 路径 / 项目 token(参见 Lifecycle)。
--api-serverAPI 服务 URL(全局标志)。
--jsonJSON 输出(参见 CLI JSON Schemas)。

请在应用目录中运行——它会读取 compose.yaml 获取应用名称;应用名称和项目 ID 会共同生成确定性的 CREATE2 salt。在支持 Gas 代付的链上(Base 8453、Base Sepolia 84532),无需预存资金的密钥,也无需 RPC URL。deployContract 会通过云端的 Alchemy bundler 转发,支持 Ethereum、Sepolia、Polygon、Polygon Amoy、Arbitrum、Arbitrum Sepolia、Optimism、Optimism Sepolia、Base 和 Base Sepolia(链 ID 为 1、11155111、137、80002、42161、421614、10、11155420、8453、84532)。如果目标链不在此范围内,部署会失败并显示 No Alchemy bundler URL for chain <id>;此时请使用 forge create 备用方案(构造函数参数保持不变;需自行将 ABI 提供到 src/contracts/)。多提供商覆盖范围由 FOU-991 跟踪。运行时任务 Gas 代付(参见 Gas Sponsorship)支持的链比此部署端点更多。 钱包生命周期。 即使尚未部署应用,wallet create 和 wallet list 现在也可以使用(它们会按需配置托管存储,与 deployContract/writeContract 类似);wallet create 会在部署前返回钱包地址。要在构造函数中授权钱包:wallet create <name> → deployContract --constructor-args <address> → 将地址接入任务 → compose deploy。

CREATE2 冲突。 从同一个应用使用相同的合约源码和构造函数参数重新部署,会遇到 CREATE2 冲突:平台会返回 HTTP 400 CONTRACT_ALREADY_DEPLOYED,CLI 会报告“This contract has already been deployed with this app. The same contract + app name + project produces the same address via CREATE2.”,并以退出码 1 结束,且不会生成 Deploy Block(--json 代码为 ALREADY_DEPLOYED;其他任何部署失败的代码均为 DEPLOY_CONTRACT_FAILED)。更改构造函数参数、源码或应用名称都会生成新地址。请注意,应用名称会参与 CREATE2 盐值计算——重命名应用会改变之后每次部署的地址。成功后会打印合约地址、交易哈希和 Deploy Block,并自动将 ABI 保存到 src/contracts/<Name>.json——随后运行 compose codegen 即可生成类型化绑定。

compose writeContract ——向已部署的合约提交写入调用。

标志用途
--chain-id <id>目标链(必填)。
--to <address>目标合约地址(必填)。
--function "sig(types)"函数签名,例如 "setValue(uint256)"。
--args <tokens...>与 --constructor-args 相同的 forge 风格语法。
--data <hex>--function / --args 的原始 calldata 替代方式。
--value <amount>要发送的原生代币金额;可添加单位后缀(wei、gwei、ether),例如 --value 1ether。
--wallet <name>用于签名的应用钱包(默认值为 default)。
--api-serverAPI 服务 URL(全局标志)。
--jsonJSON 输出(参见 CLI JSON Schemas)。
回读
命令用途关键标志
compose runs [runId]列出运行记录,或显示某次运行的详情--limit、--offset、--task <name>、--status <success|error|pending>、--since <1h|30m|7d>、--until <duration>、--json、-n/-m、-t、--api-server
compose collections list显示应用的集合名称表格定位参数 + --json
compose collections query <collectionName>查询一个集合--filter <json>(必须是 JSON 对象)、--limit(默认值为 100,后端上限为 1000)、--offset、--json、定位参数
compose source [taskName]打印已部署应用的源文件列表,或某个任务的源码。绝不写入磁盘定位参数 + --json
compose download下载已部署应用的源码归档-o/--output <path>(默认值为 <app>.zip;若文件已存在则拒绝覆盖)、--json、定位参数

TaskContext API

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

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>;
};

没有 secrets 命名空间。 密钥会平铺到 context.env 中。对于输出:context.logger.info/warn/error(message, data?) 是结构化且与运行记录关联的日志记录器(每行均包含 taskName、runId、appId、level、timestamp)。自由格式输出可以使用 console.log。logEvent 仍可使用,但在运行时类型中已标记为 @deprecated,并将在未来某个主版本中移除。

fetch(重载)
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>;
}
  • 第二个参数是配置对象,而不是裸请求体:ctx.fetch(url, { method: "POST", body: { a: 1 }, headers: { "X-Key": k } })。将原始负载对象作为第二个参数传入不会发送请求体。无法识别的键会被丢弃,请求会以 GET 方式发出。
  • 非 2xx 响应会抛出 Fetch failed with status <code> <statusText>: <body>。
  • 响应会经过 JSON 解析;如果响应体不是 JSON,则返回 undefined。
  • 这不是 window.fetch。请使用此方法,而不是原生 fetch。
callTask
callTask<Args, T>(name: string, args: Args, retryConfig?: RetryConfig): Promise<T>
  • T 是被调用方返回的值。返回 void 的任务会解析为 undefined。
  • 用于任务间调用(父级/子级模式)。
sideEffect
sideEffect<T>(fn: () => T | Promise<T>): Promise<T>

将非确定性值(时间戳、UUID、随机值)封装起来,使其像其他上下文调用一样被缓存。持久化恢复会从头重放任务,因此未封装的 Date.now() 在重放时会发生变化。回调只会运行一次。重放时,主机返回缓存的值,且不会运行回调。

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

显式传入 retryConfig 时,三个字段都必须提供(清单验证器也会对 retry_config 执行此检查)。省略它并不意味着只尝试一次:

范围默认值
未设置 retry_config 的任务{ max_attempts: 3, initial_interval_ms: 1000, backoff_factor: 2 }
安全/只读上下文调用:readContract、simulate、getBalance,以及使用 GET/HEAD/OPTIONS 的 ctx.fetch{ max_attempts: 3, initial_interval_ms: 500, backoff_factor: 2 }
其他所有情况(callTask、writeContract、sendTransaction、prepareUserOperation、submitSignedUserOperation、钱包创建/保存,以及使用 POST/PUT/PATCH/DELETE 的 ctx.fetch){ max_attempts: 1, initial_interval_ms: 500, backoff_factor: 2 },有意保持为 1,以避免对非幂等调用盲目重试
OnchainEvent(用于 decodeEventLog 和 onchain_event 触发器)
type OnchainEvent = {
  blockNumber: number;
  blockHash: string;
  transactionIndex: number;
  removed: boolean;
  address: string;
  data: Hex;
  topics: Hex[];
  transactionHash: string;
  logIndex: number;
};

对于由 onchain_event 触发的任务,params 包含 { log: OnchainEvent } 以及特定于链的元数据。decodeEventLog(abi, params.log) 会返回解码后的结构体。

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 包含 status: "success" | "reverted"、blockNumber: bigint、blockHash、gasUsed: bigint、effectiveGasPrice: bigint、cumulativeGasUsed: bigint、from、to、contractAddress: Address | null、logs: Log[]、logsBloom、transactionHash、transactionIndex、type。

具体来说:没有 string | AbiItem 重载;retryConfig 是单独的第 6 个位置参数,不是选项对象中的键;writeContract 没有 gas/gasPrice 选项;返回值不包含 chainId 和顶层 blockNumber(文档中所述的 TxResult 类型并不存在);sendTransaction 以对象作为第一个参数,不存在位置参数形式 (chain, to, value, data?, options?);simulate 返回 { result, request },并在回滚时抛出异常,因此 SimulateResult { success, ... } 和 if (!sim.success) 都是无效代码;getBalance 返回十进制 wei 字符串,因此不使用 BigInt(...) 进行算术运算会导致字符串拼接;onReorg.action.type 的取值为 "replay" | "log" | "task",没有 "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?) 接受 CollectionIndexSpec[],而非 string[]。findMany 的选项为 { limit?, offset? }:skip 不存在且会被静默忽略,因此基于它编写的分页始终返回第 1 页。读取会返回 T & { id: string }。筛选条件是扁平的 Record<string, string | number | boolean | HelperValue>,因此不支持嵌套路径筛选。

筛选运算符:$gt、$gte、$lt、$lte、$in、$ne、$nin、$exists。相等条件:{ field: value }。

CLI JSON Schema

供解析 --json 输出的 Agent 使用:

compose status -n <app> --json
{
  "name": "my-app",
  "status": "RUNNING",
  "created_at": 1771630350411,
  "updated_at": 1774473580871
}

status 的取值为 RUNNING、PAUSED、STARTING、STOPPING、ERROR、NOT_FOUND(该值来自 API,因此此列表并非穷尽所有可能)。时间戳是以毫秒为单位的 Unix 纪元时间。

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

NDJSON(每行一个对象):

{"timestamp":"2026-04-20T10:00:00Z","level":"info","message":"..."}
```字段恰好有三个。CLI 日志输出中没有 `dashboard_url`。若要将用户链接到特定运行,请从 `compose runs` 获取运行 ID,然后自行构建 `https://app.goldsky.com/<project_id>/dashboard/compose/<app-name>/runs/<run_id>`。CLI 的 `logs` 命令也没有 `--run-id` 筛选器(底层 API 接受此参数,但 CLI 不会传递),因此请使用 `compose runs <runId>` 查看单次运行的详细信息。
### `compose secret list -n <app> --json`

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

绝不会返回值。

compose wallet list --json
[{ "name": "updater", "address": "0x...", "type": "privy", "created_at": 1771630350411 }]

type 是以下之一:privy(智能钱包)、private_key(BYO EOA)、tevm(本地分叉)。

--json 模式中的错误

在 --json 模式下,stdout 仅包含结果文档(不显示 ASCII 艺术字和进度条)。失败信息会以 {"error": true, "code": "<CODE>", "message": "..."} 的形式写入 stderr,并以状态码 1 退出。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。

钱包 — 深入了解

智能钱包(托管,由 Privy 支持)
const w = await evm.wallet({ name: "my-oracle" }); // sponsorGas defaults TRUE

由 Privy 在云端创建。地址会持久化保存。默认由他人赞助 Gas。 不能在纯本地开发环境中使用——会抛出 "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"。使用 compose start --fork-chains,或切换为 BYO EOA 进行本地迭代。

BYO EOA(私钥)
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
});

可在云端和本地使用。设置 sponsorGas: true 时,钱包会在首次使用时为每条链配置 EIP-7702 委托,然后通过赞助型 bundler 提交 UserOperation。

Gas 赞助

Bundler 回退顺序:Alchemy → Pimlico → Gelato。可通过 BUNDLER_PROVIDER=<alchemy|pimlico|gelato> 环境变量覆盖。

支持的链

如果三个 bundler 提供商中任意一个支持某条链,该链就可在运行时获得赞助(按 Alchemy → Pimlico → Gelato 的回退顺序尝试,并且每个提供商都要求已设置其 API 密钥)。在 0.8.1 源码中,这些链的并集涵盖 112 条链。这是运行时任务 Gas 赞助集合,远超 deployContract / writeContract 云端部署路径所覆盖的 10 条 Alchemy 链(1、11155111、137、80002、42161、421614、10、11155420、8453、84532;FOU-991 跟踪更广泛的覆盖范围);运行时赞助还覆盖 Arbitrum、Optimism(包括 Arbitrum Sepolia(421614) / Optimism Sepolia(11155420))、Polygon、Ethereum 和 BNB 系列等众多链。不要硬编码链列表——列表会变化;请在 Goldsky 文档的 chains 页面确认当前覆盖范围。

不支持的链上的错误
No bundler provider available for chain <id>. Providers: alchemy: chain not supported; pimlico: missing keys (PIMLICO_API_KEY); gelato: …

请使用受支持的链,或设置 sponsorGas: false 并手动为 EOA 充值。

注意事项
  • Gas 赞助交易不支持 onReorg(会记录警告,但不会导致失败)。
  • 向赞助型 sendTransaction 传入自定义 nonce 会被忽略(ERC-4337 智能钱包使用不同的 nonce 结构)。

合约代码生成(完整示例)

输入

将 ABI JSON 文件放入 src/contracts/:

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

**接受的 ABI 格式:**裸 ABI 数组([{ "type": "function", ... }, ...])、包装对象({ "abi": [...] })或 Foundry/Hardhat artifact(生成器会提取 abi 字段)。不含扩展名的文件名将成为生成的类名。

生成
goldsky compose codegen

(也会在 init、start 和 deploy 期间自动运行。)

输出

.compose/generated/index.ts 会导出每个 ABI 对应的类。.compose/types.d.ts 会在 compose 路径别名下声明环境类型(该别名在脚手架生成的 tsconfig.json 中引用)。

在任务中使用
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 };
}

这些类会在 context.evm.contracts.<Name> 下公开。以 Class 结尾的代码生成名称(例如 ERC20Class)在运行时会以 ERC20 的名称公开。生成的构造函数为 new <Name>(address, chain, wallet)——传入从 evm.wallet(...) 获取的 IWallet;视图方法通过该钱包读取,状态变更方法通过该钱包写入,两者都受钱包 Gas 赞助设置的限制。

支持的链

context.evm.chains 从 viem/chains 重新导出。viem 支持的任何链,都可以通过 evm.chains.<name> 访问(例如,evm.chains.polygonAmoy、evm.chains.monadTestnet、evm.chains.baseSepolia)。燃料费赞助请参阅“燃料费赞助”部分——赞助适用范围只是 viem 链列表的一个子集。

仪表板

URL 模式:

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

仪表板会显示状态、密钥名称、日志、Code 选项卡文件浏览器、Download app 按钮以及每次运行的跟踪记录。根据 compose runs 返回的运行 ID 构建运行 URL。

定价

目前未公布定价。用量按三个维度计量:函数调用(compose_function_calls)、工作器小时数(compose_worker_hours)和燃料费支出(compose_gas_spend)。writeContract 和 deployContract 消耗的燃料费与运行时任务燃料费按相同标准计费。每单位价格按合同设置,因此请引导用户访问 https://goldsky.com/pricing,而不要引用某个套餐。

相关内容

  • /compose — 构建新应用,或说明 Compose 是什么。
  • /compose-doctor — 诊断并修复损坏的应用。
  • /auth-setup — goldsky login 帮助。
  • /secrets — 通用密钥管理。

评论

支持 Markdown