Skip to main content
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:
type ContextFunctionRetryConfig = {
  max_attempts: number; // Default: 1
  initial_interval_ms: number; // Default: 1000
  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 does 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

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.

Full TaskContext interface

export type LogEvent = {
  code: string;
  message: string;
  data: string;
  task?: string;
  runId?: string;
};

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<string, { address: string }>;
};

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<FilterHelper, string | number | boolean | string[] | number[]>
>;
export type FilterValue = string | number | boolean | HelperValue;
export type Filter = Record<string, FilterValue>;

export type WithId<T> = T & { id: string };

export interface Collection<TDoc = unknown> {
  readonly name: string;
  insertOne(doc: TDoc, opts?: { id?: string }): Promise<{ id: string }>;
  findOne(filter: Filter): Promise<WithId<TDoc> | null>;
  findMany(filter: Filter, options?: FindOptions): Promise<Array<WithId<TDoc>>>;
  getById(id: string): Promise<WithId<TDoc> | 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<void>;
}

export type Address = `0x${string}`;

export interface WalletConfig {
  name?: string; // defaults to "default"
  privateKey?: string;
  sponsorGas?: boolean; // defaults to true if no privateKey and false if privateKey
}

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 }>;
  readContract: <T = unknown>(
    chain: Chain,
    contractAddress: Address,
    functionSig: string,
    args: unknown[],
    retryConfig?: ContextFunctionRetryConfig,
  ) => Promise<T>;
  simulate: (
    chain: Chain,
    contractAddress: Address,
    functionSig: string,
    args: unknown[],
    retryConfig?: ContextFunctionRetryConfig,
  ) => Promise<{ hash: string }>;
}

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: string;
  topics: string[];
  transactionHash: string;
  logIndex: number;
}

export interface FetchConfig {
  method?: string;
  headers?: Record<string, string>;
  body?: Record<string, unknown> | string;
}

export type TaskContext = {
  env: Record<string, string>;
  callTask: <Args = Record<string, unknown>, T = unknown>(
    taskName: string,
    args: Args,
    retryConfig?: ContextFunctionRetryConfig,
  ) => Promise<T>;
  fetch: <T = unknown>(
    url: string,
    fetchConfigOrRetryConfig?: FetchConfig | ContextFunctionRetryConfig,
    retryConfig?: ContextFunctionRetryConfig,
  ) => Promise<T | undefined>;
  logEvent: (
    event: LogEvent,
    retryConfig?: ContextFunctionRetryConfig,
  ) => Promise<void>;
  evm: {
    chains: Record<string, Chain>;
    wallet: (config: WalletConfig) => Promise<IWallet>;
  };
  collection: <T>(
    name: string,
    indexes?: CollectionIndexSpec[],
  ) => Promise<Collection<T>>;
};

Next Steps