关于 compose-reference
面向开发者和 Agent 的 Goldsky Compose 参考技能,涵盖清单、命令行、任务 API、钱包与合约流程,并说明沙箱导入限制。
主要功能
- 01compose.yaml 字段与触发器
- 02命令行指令和参数
- 03TaskContext 与钱包参考
- 04合约代码生成与部署
- 05沙箱导入和网络规则
适用场景
- 01编写 compose.yaml 前核对字段
- 02查询应用生命周期命令参数
- 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 清单
- CLI 标志 → CLI 命令
- TaskContext 结构、IWallet、Collection → TaskContext API
- 智能钱包与自带 EOA、Gas 赞助默认设置 → 钱包 — 深入了解
- 合约代码生成工作流 → 合约代码生成
--json输出结构 → CLI JSON 架构- 仪表板 URL → 仪表板
compose.yaml 清单
顶层字段
| 字段 | 类型 | 必填 | 备注 |
|---|---|---|---|
name | string | 是 | /^[a-zA-Z0-9]([a-zA-Z0-9_\-]*[a-zA-Z0-9])?$/,以字母或数字开头和结尾;可包含字母、数字、下划线和连字符。允许大写字母和数字开头。平台还会拒绝规范化后(转为小写,将 [-_]+ 替换为 -)与现有应用名称相同的名称,并返回 409,因此 my-app、My_App 和 my__app 不能共存 |
api_version | string | 仅部署时 | semver(例如 0.1.0)或 stable / preview / canary(也接受任何以 internal-* 开头的值) |
tasks | array | 是 | 非空 |
secrets | string[] | 否 | 仅名称——值通过 compose secret set 设置 |
env | { local?, cloud? } | 否 | env 唯一有效的子项是 local 和 cloud——每项都是一个 Record<string, string>,会扁平化到 context.env 中。裸写的 env.MY_VAR(直接放在 env 下的变量名)会被拒绝,并提示“不是有效的键”。每个应用的硬编码常量应放在任务文件中,而不是这里。 |
任务字段
| 字段 | 类型 | 必填 | 备注 |
|---|---|---|---|
name | string | 是 | /^[a-zA-Z0-9][a-zA-Z0-9_.\-]*$/,以字母或数字开头;可包含字母、数字、下划线、连字符和点。不再允许以下划线开头(现在会拒绝 _internal_task) |
path | string | 是 | .ts 任务文件的相对路径 |
triggers | array | 是 | 一个或多个;每种类型最多一个 |
retry_config | object | 否 | { 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 / -t | Manifest 路径 / 项目 token(参见 Lifecycle)。 |
--api-server | API 服务 URL(全局标志)。 |
--json | JSON 输出(参见 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-server | API 服务 URL(全局标志)。 |
--json | JSON 输出(参见 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— 通用密钥管理。