Skill: Writing a Function

A Mimic function is an AssemblyScript module compiled to WebAssembly. It runs inside a sandboxed environment on every trigger execution, receives typed inputs from the manifest, queries on-chain and off-chain data, and emits intents — declarations of what you want to happen on-chain.


Project anatomy

my-function/
├── manifest.yaml          # Inputs, ABIs, and metadata
├── src/
│   ├── function.ts        # Your function logic (entry point)
│   └── types/             # Auto-generated by `mimic codegen`
│       ├── index.ts       # Typed inputs
│       └── ERC20.ts       # Generated ABI wrapper (one per ABI)
└── build/
    ├── function.wasm      # Compiled output
    └── manifest.json      # Validated manifest

Manifest

The manifest describes your function's metadata, inputs, and contract ABIs. The CLI uses it to validate, generate types, compile, and deploy.

version: 1.0.0
name: My Automation Function
description: Swaps USDC to ETH when balance exceeds a threshold.
inputs:
  - chainId: int32
  - smartAccount: address
  - tokenIn: address
  - tokenOut: address
  - threshold: string
  - feeAmount: string
abis:
  - ERC20: "./abis/ERC20.json"

Inputs can optionally include a description:

All token amounts should be declared as string and converted in code with fromStringDecimal. This keeps the UI human-readable — a user types 1 for 1 USDC, not 1000000.

Input type mapping — manifest types and the AssemblyScript types they produce:

Manifest type
AssemblyScript type

int32

i32

int64

i64

uint8

u8

uint16

u16

uint32

u32

uint64

u64

uint256 / int256

BigInt

address

Address

bytes / bytesN

Bytes

string

string

  • inputs and abis are merged into maps during validation — duplicate keys are rejected.

  • ABI paths are resolved relative to the directory of your manifest.yaml.


Function structure

Every function must export a main function. The inputs type is auto-generated from your manifest.


Library reference (@mimicprotocol/lib-ts)

Primitives


Tokens and amounts

Pre-defined tokens

There are two ways to reference a known token depending on whether the chain is fixed or dynamic.

Chain namespaces — use when the chain is known at compile time:

Tokens class — use when the chain comes from inputs.chainId at runtime:

Available tokens in Tokens: USDC, USDT, DAI, WBTC, WETH, ETH, AVAX, WAVAX, POL, WPOL, BNB, WBNB, XDAI, WXDAI, SONIC, WSONIC.

ERC20Token.fromString — last resort for tokens not in the registry:

DenominationToken

DenominationToken.USD() is the standard way to express max fees. It is not a real on-chain token — it deducts from the user's Mimic credits. It can only be used with .addMaxFee(). You cannot transfer or swap it.

TokenAmount


Result type

All queries (except getContext) return Result<V, string>. Always handle errors.


Environment queries

Token price

Relevant token balances

Returns all token balances for an address, with optional chain, allow/deny list, and USD minimum filters.

EVM contract read (raw)

Use generated ABI wrappers (see below) instead of raw calls where possible.

Native token balance

Account code

Subgraph query

Execution context

getContext() does not return a Result — it cannot fail.


Generated ABI wrappers

After running mimic codegen, each ABI declared in the manifest produces a file in src/types/<ContractName>.ts containing four things:

  1. The contract class — instantiate with (address, chainId, timestamp?) to call methods

  2. A <ContractName>Utils static class — raw encode/decode helpers (rarely needed directly)

  3. Struct/tuple classes — one per Solidity struct or multi-output tuple

  4. Event classes — one per event, with a static decode(topics, data) method

Contract class — read and write methods

Struct classes

Solidity structs and multi-output tuples become generated classes with typed fields. You typically receive them as unwrapped return values from read methods — you don't construct them manually.

Event classes

Each event gets a class with a static decode(topics, data) method, matching the shape of the TriggerType.EVENT payload:

Utils class

The <ContractName>Utils static class exposes encodeX() and decodeX() methods for raw ABI encoding. Use these only when you need the raw encoded bytes — for example to pass a call directly to addCall() without instantiating the contract class.

Solidity → AssemblyScript type mapping

Solidity
AssemblyScript

address

Address

bool

bool

string

string

bytes / bytesN

Bytes

uint8uint16, int8int16

u8/u16/i8/i16

uint32uint64, int32int64

u32/u64/i32/i64

uint256 / int256

BigInt

T[]

T[]

tuple / struct

generated class


Intent builders

Intents are declarations of what you want to happen. The protocol finds the best way to fulfill them.

Transfer — move tokens between addresses

Swap — exchange tokens

The tokenOut amount is a minimum — relayers may deliver more.

EVM Call — execute contract functions

Common builder options

All intent builders support these optional methods before .build():

Method
Purpose

.addUser(address)

Override the user (defaults to ctx.user)

.addDeadline(timestamp)

Override the deadline (defaults to 5 minutes from now)

.addNonce(string)

Override the nonce (defaults to auto-generated)

.addEvent(topic, data)

Attach an on-chain event to the intent

.addEvents(events[])

Attach multiple events

When to use .addUser():

  • Transfer / Swap intentsctx.user is the default and is correct for most cases. ctx.user is the EOA that signed and triggered the function, so you normally do not need to call .addUser().

  • EVM Call intents — you must call .addUser(inputs.smartAccount). EVM Call intents execute generic contract calls, which require a smart account. ctx.user is an EOA and cannot fulfill this role.

Guarding against empty intents:

Never send an intent with no transfers, calls, or swaps. If you are conditionally adding items (e.g. iterating over a list), check the builder before sending. If you are unconditionally adding at least one item, no guard is needed.

Batching large numbers of items:

A single intent may not fit in one transaction if it contains too many calls, transfers, or swaps. If you are building a variable-length list, split it into batches of at most 20 items and send one intent per batch.


Persistent storage

Use the storage namespace to read and write arbitrary bytes to the user's on-chain storage via the Mimic Helper contract. Useful for maintaining state between executions.


Logging

Both styles work. Template literals are preferred for multi-variable messages; format strings are preferred when you need to call .toString() on custom types. Levels: DEBUG < INFO < WARNING < ERROR < CRITICAL.


EVM utilities


Putting it together — annotated example

Last updated