---
name: thyme-migrate-mimic
description: >-
  Port Mimic Protocol functions to Thyme Flow. Use when a repository contains
  `@mimicprotocol/lib-ts`, `@mimicprotocol/cli` or `@mimicprotocol/sdk`, a
  `manifest.yaml` with `inputs:` and `abis:`, a `src/function.ts` that exports
  `main(): void`, `TransferBuilder`, `SwapBuilder` or `EvmCallBuilder` intents,
  `mimic compile` or `mimic deploy`, or when the user asks to move off Mimic
  automation. Covers finding the functions and their triggers, translating the
  AssemblyScript to a TypeScript task, deciding what has no equivalent, testing
  locally and handing over to the thyme-flow skill for upload and scheduling.
---

# Migrate Mimic Protocol functions to Thyme Flow

A Mimic function is AssemblyScript compiled to WebAssembly. It reads data
through Mimic's `environment` queries and emits **intents** (transfer, swap,
EVM call) that Mimic's solvers fulfil from the user's account. A Thyme Flow
task is TypeScript that reads the chain with viem and returns the **calls** to
send. Flow sends them from a Safe the user owns, through a Zodiac Roles role
limited to the contracts and functions they allow.

The decision logic carries over. The integration layer does not: the language,
the reads, the output and the account model all change, and some intents have
no Flow equivalent (section 5). Port by rewriting each function in TypeScript
rather than converting it line by line.

This skill covers the translation. For the `thyme` CLI itself (deployment,
login, upload, executables, profiles, secrets) read the **thyme-flow** skill:
`https://flow.thymelabs.io/docs/skills/thyme-flow/SKILL.md`. Its rules apply
here too: never print credentials, a person approves logins and signs profile
changes, and ask before anything that sends transactions.

This mapping was written from Mimic's public docs
(`https://docs.mimic.fi/skills/skill-function.md` and
`https://docs.mimic.fi/developers/library.md`). The protocol is in alpha and its
library may change. If the repository uses an API this skill does not list, read
those pages, then apply the same rule: reads become viem calls, intents become
returned calls.

## 1. Inventory before you change anything

Search the repository and report what you find before porting:

```bash
grep -rln --exclude-dir=node_modules -e '@mimicprotocol/lib-ts' -e '@mimicprotocol/cli' -e '@mimicprotocol/sdk' .
grep -rln --exclude-dir=node_modules -e 'TransferBuilder' -e 'SwapBuilder' -e 'EvmCallBuilder' .
grep -rn --exclude-dir=node_modules -e 'signAndCreate' -e 'TriggerType' -e 'mimic deploy' .
```

For each function (a directory with a `manifest.yaml` and `src/function.ts`),
list:

| Item | Where to find it |
|---|---|
| Inputs | `inputs:` in `manifest.yaml`, read as `inputs.<name>` in the code. |
| ABIs | `abis:` in the manifest, and the wrappers `mimic codegen` generated in `src/types/`. |
| Chain | Usually an `inputs.chainId` value, or a fixed class such as `Ethereum` or `Base`. |
| Reads | Every `environment.*` call and every generated contract read (`.balanceOf(...).unwrap()`). |
| Intents | Every `TransferBuilder`, `SwapBuilder` and `EvmCallBuilder`, and what each one moves or calls. |
| Storage | `storage.getData` and `storage.createSetDataCall` calls. |
| Trigger | Scripts that call `client.triggers.signAndCreate`, trigger configs, or ask the user. Cron, event or once. |
| Fees | `maxFee`, `.send(fee)` and `DenominationToken.USD()`. |
| Account | `ctx.user`, `inputs.smartAccount`, `.addUser(...)`, or an EIP-7702 upgrade script. |

Then tell the user the plan: which functions port directly, which need a
decision (see section 5), and which one you will port first.

## 2. Translate the code

Create a Flow project if the repository has none (`thyme init`, then
`thyme new <name>` per function; see thyme-flow). Port one function at a time
into `functions/<name>/index.ts`.

| Mimic | Thyme Flow |
|---|---|
| `export default function main(): void { … }` | `export default defineTask({ schema, async run(ctx) { … } })` |
| `inputs:` in `manifest.yaml` | A Zod `schema`, read as `ctx.args`. `address` becomes `z.address()`. Token amounts kept as decimal `string` stay strings, and you convert them with viem `parseUnits`. |
| `inputs.chainId` | Not an argument. One executable runs on one chain; read it with `await ctx.client.getChainId()` if you need it. |
| `abis:` and `mimic codegen` wrappers | `parseAbi([...])` or viem's `erc20Abi`, with `ctx.client.readContract` and `encodeFunctionData`. |
| `new ERC20(token, chainId).balanceOf(addr).unwrap()` | `await ctx.client.readContract({ address: token, abi: erc20Abi, functionName: 'balanceOf', args: [addr] })` |
| `Result<T, string>` and `.unwrap()` | A normal `await`. Wrap in `try`/`catch` and return `{ canExec: false, message }` when a read fails. |
| `environment.getNativeTokenBalance(chain, addr)` | `await ctx.client.getBalance({ address })` |
| `environment.getCode(chain, addr)` | `await ctx.client.getCode({ address })` |
| `environment.evmCallQuery(...)` | `ctx.client.call` or `readContract` |
| `environment.getContext().user` | `ctx.account`, the address of the executable's profile |
| `environment.getContext().timestamp` (milliseconds) | `Date.now()` |
| `environment.tokenPriceQuery(token)` and `TokenAmount.toUsd()` | No built-in price oracle. See section 5. |
| `environment.relevantTokensQuery(...)` | Not built in. See section 5. |
| `environment.subgraphQuery(chain, id, query)` | `fetch` the subgraph's GraphQL endpoint with a POST. Any subgraph works, including one hosted on Thyme Atlas. |
| `storage.getData(user, key, chain)` and `storage.createSetDataCall(...)` | `ctx.storage.key` and `ctx.storage.key = value`. Plain JSON, no fee. |
| `log.info(...)`, `log.warning(...)`, `log.error(...)` | `ctx.logger.info(...)`, `ctx.logger.warn(...)`, `ctx.logger.error(...)` |
| `log.critical(...)` | `return { canExec: false, message }`, or `throw` to fail the run. |
| `TokenAmount.fromStringDecimal(token, '1.5')` | `parseUnits('1.5', decimals)`, with `decimals` from `readContract` (`'decimals'`) or a constant. |
| `Tokens.USDC.on(chainId)` and `Ethereum.USDC` | An explicit token address as an argument, or a constant for the executable's chain. |
| `TransferBuilder … addTransferFromStringDecimal(token, '500', to)` | A `transfer` call: `{ to: token, data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, parseUnits('500', decimals)] }) }` |
| `EvmCallBuilder … addCall(address, data)` or a generated write wrapper | `{ to: address, data }` in `calls`. This one maps directly. |
| `.addUser(inputs.smartAccount)` | Remove it. The Safe behind the profile is the account. |
| `.build().send(maxFee)` | Return the calls. There is no max fee: mainnet gas is sponsored and charged to the workspace's dBank balance. |
| `.addDeadline`, `.addNonce`, `.addEvent` | No equivalent. Drop them, and say so in the report. |
| `mimic compile` then `mimic deploy` | `thyme upload <name> --tag v1`, then create an executable |
| `mimic test` | `thyme run <name>` with `functions/<name>/args.json`, then `--simulate` |
| `mimic login` with an API key | `thyme login`, approved by a person in the browser |

ABIs: a Mimic `abis:` entry points at a JSON file. Reuse it in the task with
`import abi from './abis/Foo.json'` only if `thyme run` and `thyme upload` both
succeed with it. Otherwise copy just the functions you call into a `parseAbi`
list.

Mimic enforces slippage in the intent: a swap's output is a minimum, and the
solver and settler guarantee it. A Flow call is sent exactly as returned, so
put any bound that mattered into the call itself, for example the router's
`amountOutMinimum`, so the contract enforces it when the transaction executes.
A check inside the task before it returns `canExec: true` only holds at the
moment the task runs, because the price can move before the transaction lands.

Storage written on Mimic does not move. If the function depends on a stored
value, ask the user for the last value and seed it in `storage.json` locally,
then with `thyme executables storage-set` after the executable exists.

## 3. Map the trigger

| Mimic trigger | Flow |
|---|---|
| Cron (`schedule`) | `{ "type": "cron", "cronspec": "<expression>" }`. Mimic's `delta` window and `endDate` have no equivalent: pause the executable when the schedule should end. |
| Event (`chainId`, contract, `topics`) | An interval trigger. Read logs since a block cursor kept in `ctx.storage` with `ctx.client.getContractEvents` or `getLogs`, return `canExec: false` when there are none, and advance the cursor in `onSuccess`. Say that runs are no longer per event. |
| Once (`startDate`) | Flow has no one-shot trigger. Use a webhook or `thyme executables run` (ask first), or an interval with `startAt` whose task sets a flag in `ctx.storage` and skips from then on. Tell the user to pause the executable once it has fired. |
| Event trigger on another function's output ("function chaining") | Merge the two functions into one task when they share a chain. Otherwise the first task's `onSuccess` can call the second executable's webhook with `fetch`. Test the callback against a mock endpoint as described below. Callbacks are best effort and not guaranteed to be delivered, so tell the user this is weaker than Mimic's chaining. |

For local chaining checks, point the callback at a mock endpoint that records
requests without starting an executable, then run
`thyme run <name> --callback onSuccess` (or choose success with
`--simulate-callbacks`). These commands fabricate the receipt but execute the
callback, including its real HTTP requests. A live executable webhook can
therefore send real transactions during this check. Ask for approval before
testing a live webhook; do not treat it as a dry run.

Mimic triggers are signed with the user's wallet. A Flow executable is created
in the console or with the CLI by a signed-in workspace member, and the Safe's
owner separately allows what it may call (section 4).

Keep each run bounded: a cloud run has about 30 seconds, and one run can return
up to 32 calls, sent as one atomic batch from a Roles profile.

## 4. Declare, test, ship

1. Write `functions/<name>/permissions.json` with every target and function
   signature the task can return.
2. Put the old manifest input values in `args.json`, set `RPC_URL` and
   `SIMULATE_ACCOUNT` in `.env`, and run `thyme run <name>` then
   `thyme run <name> --simulate`. Compare the decision with what the Mimic
   function would have done for the same state.
3. Hand over to thyme-flow for login, `thyme upload` and the profile. The user
   must allow each target and selector on their Safe profile in the Console;
   tell them exactly which ones. If the user has an EIP-7702 Mimic account,
   Flow does not use it: they create or connect a Safe.
4. Before creating the executable, explain that its schedule starts
   automatically once provisioning finishes and permissions are covered, and
   ask for approval to start live scheduled runs. Only after approval, create
   it with the trigger from section 3. Creation is not a paused preview;
   scheduled transactions can run before a later simulation or manual check.
5. Inspect the first scheduled execution.
   `thyme executables simulate EXECUTABLE_ID` previews another run but does not
   pause the active schedule. Ask before an additional real run with
   `thyme executables run`, or before resuming an executable that was paused.

## 5. Things that need the user's decision

Stop and explain these instead of guessing:

- **Swap and bridge intents.** Flow does not route swaps or bridges. A
  `SwapBuilder` on one chain becomes a call to a DEX router the user chooses
  (an `approve` call followed by the swap call, sent atomically), with the
  minimum output computed in the task and passed to the router call as
  `amountOutMinimum` or its equivalent. A cross-chain swap
  (`SwapBuilder.forChains`) cannot be one task, because one executable runs on
  one chain: split it, or keep that function on Mimic.
- **Price queries.** `tokenPriceQuery` and `toUsd()` return a median of Mimic's
  oracles. Flow has none built in. The user picks a source: an on-chain price
  feed read with `readContract`, or an HTTP price API called with `fetch` and a
  key in `ctx.secrets`. A different source can change the decision, so compare
  outputs.
- **Token discovery.** `relevantTokensQuery` has no equivalent. Pass the token
  list as an argument, or read it from an indexer.
- **Native token transfers.** Flow calls are `{ to, data }` only, with no value.
  A function that sent ETH or another native token needs a different design,
  such as moving the wrapped token.
- **Several chains in one function.** One executable runs on one chain.
- **Unsupported chains.** Mimic lists Ethereum, Gnosis, Sonic and Avalanche among
  its chains. Run `thyme chains list` to see what the deployment enables, and
  tell the user if their chain is missing.
- **What the role may do.** Flow's Safe role allows contracts and function
  selectors, not argument values in this release. Allowing `transfer`, `approve`
  or `transferFrom` lets the task choose recipients, spenders and amounts. Tell
  the user, and have them keep only the funds and approvals the automation
  needs in the Safe.

## 6. Report back

For every function, tell the user: what changed, the args and trigger you
chose, the `permissions.json` entries, the local run result, anything you
dropped (deadlines, nonces, events, max fee), and what they must still do
(approve login, create or connect a Safe, allow targets on the profile, top up
dBank credit for mainnet gas, pick a price source).
