DEV Community

Cover image for Meet Thyme Flow
Jakhongir Tashpulatov for Thyme Labs

Posted on

Meet Thyme Flow

A lot of onchain apps need something to happen on a clock: harvest rewards, rebalance a vault, top up a balance, close an expired position. The usual options are a cron box holding a hot private key, or a keeper network you have to fund and trust with your contracts.

We built Thyme Flow so you don't have to pick either. You write a TypeScript task that decides what to do. Flow runs it on a schedule and sends the calls from a Safe you own, through a role you can revoke whenever you want. We never hold the keys to your account.

How it works

1. Write a task

A task is plain TypeScript. It reads live chain state and returns the calls to send, or decides to skip this run. You never sign anything in your code.

import { defineTask, z } from '@thyme-labs/sdk'
import { encodeFunctionData, parseAbi } from 'viem'

const abi = parseAbi([
  'function harvest()',
  'function pendingRewards() view returns (uint256)',
])

export default defineTask({
  schema: z.object({
    vault: z.address(),
    minRewards: z.coerce.bigint(),
  }),
  async run(ctx) {
    const { vault, minRewards } = ctx.args
    const pending = await ctx.client.readContract({
      address: vault, abi, functionName: 'pendingRewards',
    })

    // Skip this run: not worth the gas yet.
    if (pending < minRewards) {
      return { canExec: false, message: `${pending} pending` }
    }

    return {
      canExec: true,
      calls: [{
        to: vault,
        data: encodeFunctionData({ abi, functionName: 'harvest' }),
      }],
    }
  },
})
Enter fullscreen mode Exit fullscreen mode

The task context gives you what you need:

  • ctx.args: typed inputs, validated against your Zod schema
  • ctx.client: a viem client for chain reads
  • ctx.secrets: only the project secrets bound to this executable
  • ctx.storage: JSON state that persists between runs

A run that returns canExec: false sends no transaction. One run can return up to 32 calls, which are executed atomically in one transaction.

2. Run it locally

npm install -g @thyme-labs/cli
thyme init my-automation
thyme run harvest-rewards --simulate
Enter fullscreen mode Exit fullscreen mode

The CLI runs your task in a local sandbox and simulates the returned calls against a real RPC. Nothing is signed or broadcast.

3. Upload it

thyme upload harvest-rewards --tag v1
Enter fullscreen mode Exit fullscreen mode

This bundles the task into an immutable, checksummed release. A tag can't be overwritten, so you always know which code is running.

4. Schedule it

In the console, create an executable. You pick the Safe it runs from, its arguments and secrets, and when it runs:

  • Cron: any standard cron expression. The console reads it back in plain words before you save.
  • Interval: from every 30 seconds up to once a day.
  • Webhook: up to ten named, secret URLs per executable. Each request body is validated against your task's schema.

Flow handles the schedule, the signing and the gas from there. Every run shows up in the execution logs, with its decision, calls and transaction hash.

How it stays safe

Automation that can move funds needs a tight trust model. Here's ours.

Your Safe stays yours

Your wallet owns the Safe. Flow gets a scoped executor role through the Zodiac Roles module, and that role can only:

  • call the contract addresses and functions you allowlist
  • send calls with zero native value, and never delegatecall

The executor can't widen its own role or administer your Safe. Only you, as the owner, can approve a broader scope, and you sign that change yourself.

Revoke any time

Remove the role or the module from your Safe and Flow can't submit anything, ever. That's a normal Safe transaction, and it doesn't need our permission. Pausing an executable in the console stops runs too.

Check before you sign

When you set up a Safe profile, the console checks the deployed contract code against pinned hashes before it asks for your signature.

You don't have to take our word for it, either. thyme verify roles-profile independently rebuilds the setup from pinned Safe and Zodiac Roles versions, then checks the signature request or the deployed contracts against the allowlist you intended. It never calls the Thyme API.

Declare what your code may call

A task can ship a permissions.json that lists the contracts and functions it needs:

{
  "calls": [
    { "target": { "arg": "vault" }, "function": "harvest()" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Flow checks that your Safe's role covers these calls before you can enable an executable. At runtime, any call outside the declaration is rejected before it's submitted. The manifest isn't a grant: only your signed Safe scope can give access.

Secrets stay out of your code

Secrets live in a vault, not in your bundle. You bind each one to the executables that need it, and Flow redacts known secret values from logs.

Isolated runs

Every cloud run executes in an isolated sandbox with a 30-second timeout. Each executable's storage is versioned and locked, so overlapping runs can't overwrite each other's state.

An honest caveat

Roles scopes restrict the target contract and function, not the arguments. If you allow transfer or approve, the task chooses the recipient and amount. Only allow functions you'd trust your task with, and keep only the funds the automation needs in the Safe.

Works with your coding agent

Paste one prompt into Claude Code, Codex, Cursor or another coding agent, and it learns the thyme CLI. It can write and test a task, upload it, create the executable, set the schedule and read run logs.

Creating a Safe profile, or widening what it may call, still needs your signature in the console.

Try it

Flow is part of thyme/labs, which also offers RPC (Gate) and hosted subgraphs (Atlas) in the same workspace.

What would you automate first? Tell us in the comments. 👇

Top comments (1)

Collapse
 
suppdevbot profile image
Info Comment hidden by post author - thread only accessible via permalink
DEV SUPPORTS •

You need to verify your account.

Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to

Some comments have been hidden by the post's author - find out more