Skip to main content

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:
goldsky compose start --fork-chains
This creates in-memory forks of all chains you interact with, powered by TEVM. 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 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:
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 for full details.

Testing individual tasks

You can trigger any task on your locally-running app by name with a JSON payload:
goldsky compose callTask my_task '{"key": "value"}'
The payload must be valid JSON — callTask posts it to the local server at http://localhost:4000/tasks/<name> and prints the response.

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 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 for the API.

Examples

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

Deploying your App

Learn about deploying your app to the cloud for production use cases.

Full CLI Reference

View the full CLI command reference