Library

The Mimic Protocol Library (@mimicprotocol/lib-ts) is a lightweight standard library for building blockchain automation tasks. It provides typed primitives and safe bindings to create deterministic tasks to interact with multiple blockchain networks.

Why AssemblyScript?

Tasks are written in AssemblyScript (which compiles to WebAssembly) to ensure:

  • Deterministic execution: Same inputs always produce same outputs

  • Portability: Runs consistently across different environments

  • Security: Sandboxed execution with no system access

  • Performance: Near-native execution speed

We're planning to expand language support beyond AssemblyScript to include other WebAssembly-compatible languages like Rust. This will bring more flexibility in how tasks can be written while maintaining all the security and deterministic execution benefits of WebAssembly.


1. Core Concepts

1.1. Intents

Intents are declarations of what you want to happen. Instead of executing transactions directly, you create intents that describe your desired outcome. The protocol then finds the best way to fulfill them.

Three types of intents are available:

  • Call: Execute smart contract functions

  • Swap: Exchange tokens across DEXs

  • Transfer: Move tokens between addresses

1.2. Environment

The Environment provides access to external data and execution capabilities:

  • Price feeds and oracles

  • Token information

  • Contract interactions

  • Intent execution


2. Getting Started

2.1. Installation

# Install dependencies
yarn add @mimicprotocol/lib-ts

# If creating a new project, you'll also need the CLI tools
yarn add @mimicprotocol/cli

2.2. Basic task structure

Every task follows this structure:

import { /* required imports */ } from '@mimicprotocol/lib-ts'

export default function main(): void {
  // 1. Setup: Define tokens, addresses, parameters
  // 2. Logic: Query data, make decisions
  // 3. Action: Create and send intents
}

2.3. Project setup

Create a basic task project:

# Initialize a new Mimic project
mimic init -d my-automation-task && cd my-automation-task

# Generate types from ABIs (if using custom contracts or external inputs)
mimic codegen

# Compile your task
mimic compile

3. API Reference

3.1. Core types

3.1.1. Primitives

// Addresses
const userAddress = Address.fromString('0x...')
const zeroAddress = Address.zero()

// Big integers for precise arithmetic
const value = BigInt.fromString('1000000000000000000') // 1 ETH in wei
// or...
const value2 = BigInt.fromI32(1).pow(18) // Also 1 ETH in wei
const calculated = value.times(BigInt.fromI32(2))

// Bytes for contract data
const data = Bytes.fromHexString('0x095ea7b3...')
const emptyData = Bytes.empty()

3.1.2. Tokens and Amounts

// Create a token reference
const USDC = Ethereum.USDC
const ETH = Ethereum.ETH
const customToken = ERC20Token.fromString('0xCustomTokenAddress', Ethereum.CHAIN_ID, 18, 'CSTM')

// Work with token amounts
const amount = TokenAmount.fromStringDecimal(USDC, '1000.50') // 1000.5 USDC
const nativeAmount = TokenAmount.fromI32(ETH, 5) // 5 ETH

3.2. Queries

3.2.1. Price queries

Price queries allow you to retrieve token prices in USD. Results are represented with 18 decimals:

import { environment, Ethereum, USD } from '@mimicprotocol/lib-ts'

// Get current token price in USD
const price = environment.getPrice(Ethereum.USDC)

// Get historical price
const historicalPrice = environment.getPrice(Ethereum.USDC, new Date(1640995200000))

// Get raw price samples from multiple sources (USD[])
const rawPrices = environment.getRawPrice(Ethereum.USDC)

// Convert between USD and tokens
const usdAmount = USD.fromStringDecimal('1000')
const tokenEquivalent = usdAmount.toTokenAmount(Ethereum.WBTC)

3.2.2. Relevant tokens

The relevant tokens query allows you to find all the token balances for a specific account. Handful filters are provided to restrict chains, tokens allow or deny lists, or minimum USD value:

const userTokens = environment.getRelevantTokens(
  userAddress,
  [ChainId.ETHEREUM, ChainId.POLYGON], // chains to check
  USD.fromStringDecimal('100'),        // minimum USD value
  [unwantedToken],                     // excluded tokens
  ListType.DenyList                    // list type
)

3.2.3. Contract calls

Contract calls allows you to read contract information from the chain:

const result = environment.contractCall(
  Address.fromString('0xcontractAddress'),
  ChainId.ETHEREUM,
  '0x70a08231'
)

3.2.4. Subgraph queries

Subgraph queries allows you to read information from subgraphs:

import { environment, ChainId } from '@mimicprotocol/lib-ts'

const response = environment.subgraphQuery(
  ChainId.ETHEREUM,
  'QmSubgraphId',
  '{ tokens { id symbol } }'
)

3.2.5. Context

The context query tells you the current execution context for the task. This includes user, settler, timestamp, and config ID:

import { environment, ChainId } from '@mimicprotocol/lib-ts'

const ctx = environment.getContext()
const user = ctx.user
const settlerForEth = ctx.findSettler(ChainId.ETHEREUM)
const timestampMs = ctx.timestamp

3.3. Intent builders

3.3.1. Transfer

Move tokens between addresses:

import { Address, ChainId, Ethereum, TokenAmount, TransferBuilder } from '@mimicprotocol/lib-ts'

const fee = TokenAmount.fromStringDecimal(Ethereum.USDC, '1')

const transferIntent = TransferBuilder.forChain(ChainId.ETHEREUM)
  .addTransferFromStringDecimal(Ethereum.USDC, '1000', Address.fromString('0xrecipientAddress'))
  .addTransferFromStringDecimal(Ethereum.WBTC, '0.5', Address.fromString('0xrecipientAddress'))
  .addTransferFromStringDecimal(Ethereum.USDT, '500', Address.fromString('0xotherRecipientAddress'))
  .addMaxFee(fee)
  .build()

transferIntent.send()

3.3.2. Swap

Exchange tokens across DEXs:

import { Address, ChainId, Ethereum, SwapBuilder, TokenAmount } from '@mimicprotocol/lib-ts'

const fee = TokenAmount.fromStringDecimal(Ethereum.USDC, '1')

const swapIntent = SwapBuilder.forChains(ChainId.ETHEREUM, ChainId.ETHEREUM) // same chain swap
  .addTokenInFromStringDecimal(Ethereum.USDC, '1')
  .addTokenOutFromStringDecimal(Ethereum.USDT, '0.99', Address.fromString('0xrecipientAddress')) // 1% slippage
  .addMaxFee(fee)
  .build()

swapIntent.send()

3.3.3. Call

Execute smart contract functions:

import { Address, BigInt, Bytes, CallBuilder, ChainId, Ethereum, evm, EvmEncodeParam, TokenAmount } from '@mimicprotocol/lib-ts'

const encodedData = Bytes.fromHexString(
  '0xselector' +
  evm.encode([
    EvmEncodeParam.fromValue('address', Address.zero()),
    EvmEncodeParam.fromValue('uint256', BigInt.zero()),
  ])
) // encoded function(address,uint256)

const fee = TokenAmount.fromStringDecimal(Ethereum.USDC, '1')

const callIntent = CallBuilder.forChain(ChainId.ETHEREUM)
  .addCall(Address.fromString('0xcontractAddress'), encodedData)
  .addMaxFee(fee)
  .build()

callIntent.send()

3.4. EVM Utilities

import { Address, BigInt, evm, EvmDecodeParam, EvmEncodeParam } from '@mimicprotocol/lib-ts'

// Encode function parameters
const encoded = evm.encode([
  EvmEncodeParam.fromValue('address', Address.zero()),
  EvmEncodeParam.fromValue('uint256', BigInt.zero()),
])

// Decode contract responses
const decoded = evm.decode(new EvmDecodeParam('string', response))

// Generate hashes
const hash = evm.keccak('some data')

4. Using generated ABI wrappers

If you are using mimic codegen to generate contract wrappers, refer to the ABI wrappers guide for how read/write methods, tuple classes, arrays, and events are produced and used:

mimic codegen generates strongly-typed AssemblyScript wrappers for your contract ABIs. For every ABI declared in your manifest.yaml, it emits a file src/types/<ContractName>.ts that includes:

  • A ContractName class: ergonomic read/write methods that encode calls and decode responses

  • A ContractNameUtils helper class: static encodeX/decodeX helpers per function

  • Tuple classes for tuple/struct params and returns

  • Event classes <EventName>Event with a static decode(topics, data)

The generated code targets @mimicprotocol/lib-ts primitives and utilities: Address, BigInt, Bytes, environment, evm, EvmEncodeParam, EvmDecodeParam, CallBuilder.

4.1. When it runs

  • mimic codegen reads manifest.yaml

    • inputssrc/types/index.ts

    • abissrc/types/<ContractName>.ts

Example manifest excerpt:

abis:
  ERC20: './abis/IERC20.json'

4.2. Generated class layout

Every contract file exports export class <ContractName> with:

  • Constructor: (address: Address, chainId: ChainId, timestamp: Date | null = null)

  • Getters: address, chainId, timestamp

  • One method per ABI function. Read methods call environment.contractCall; write methods return a CallBuilder and never call the environment directly.

Additionally, export class <ContractName>Utils exposes encode<Fn> and decode<Fn> helpers used by the main class.

export class ERC20 {
  private _address: Address
  private _chainId: ChainId
  private _timestamp: Date | null

  constructor(address: Address, chainId: ChainId, timestamp: Date | null = null) { /* ... */ }
  get address(): Address { /* ... */ }
  get chainId(): ChainId { /* ... */ }
  get timestamp(): Date | null { /* ... */ }

  // ...
}

export class ERC20Utils {
  // static encode<Name>(...): Bytes
  // static decode<Name>(response: string): <MappedType>
}

4.3. Type mapping rules

  • address → Address

  • bool → bool

  • string → string

  • bytes / bytesN → Bytes

  • intN/uintN → BigInt for N ≥ 24; i8/u8/i16/u16/i32/u32/i64/u64 for smaller widths

  • Arrays preserve depth: address[][]Address[][]

  • Tuples generate classes; arrays of tuples become arrays of those classes

Unknown or unextracted tuple types are conservatively mapped to unknown (and a warning may be emitted during generation).

4.4. Method naming and overloading

  • Functions keep their ABI name; reserved words are suffixed with _ (e.g., constructor_).

  • Overloads are suffixed incrementally: _1, _2, ...

  • Capitalized names are used in helpers: encodeGetBalance, decodeGetBalance.

4.5. Read calls

Read methods perform: encode → environment.contractCall → decode.

// ABI: function balanceOf(address owner) view returns (uint256)
const erc20 = new ERC20(token, ChainId.ETHEREUM)
const balance: BigInt = erc20.balanceOf(Address.fromString('0x...'))

// Under the hood (simplified):
// const encoded = ERC20Utils.encodeBalanceOf(owner)
// const resp = environment.contractCall(this._address, this._chainId, this._timestamp, encoded.toHexString())
// return ERC20Utils.decodeBalanceOf(resp)

Void-returning reads just perform the call without decoding.

4.6. Write calls

Write methods return a CallBuilder so you can compose multiple calls and send them via intents later.

// ABI: function approve(address spender, uint256 amount) nonpayable
const approve = erc20.approve(spender, amount) // CallBuilder
// approve.addMaxFee(...).build().send() // typical flow

Encoding converts primitives as needed:

// bool → Bytes.fromBool
// u8 → BigInt.fromU8, etc.
// tuples → EvmEncodeParam.fromValues('()', tuple.toEvmEncodeParams())
// arrays → nested EvmEncodeParam.fromValues with map

4.7. Tuples and structs

The generator extracts tuple/struct definitions from inputs, outputs, and multi-return functions.

  • If internalType includes a struct name (e.g., struct UserContract.UserInfo), that name is used

  • Otherwise it falls back to Tuple<N>

  • For functions with multiple outputs, a synthetic <FunctionName>Outputs class is created

Tuple classes provide:

  • static parse(data: string): <Class> to parse decoded strings

  • toEvmEncodeParams(): EvmEncodeParam[] for encoding

// Example return tuple
const info: UserInfo = erc20.getUserInfo()

// Example tuple param
const created = contract.createUser(new UserInfo(id, name, active)) // CallBuilder

4.8. Arrays and nested arrays

Arrays are supported at any depth for both params and returns. Encoding/decoding uses JSON strings under the hood for nested structures and handles empty arrays.

// ABI: function getHolders() view returns (address[])
const holders: Address[] = contract.getHolders()

// ABI: function batchTransfer(address[][] recipients, uint256[][] amounts)
const cb = contract.batchTransfer(addressMatrix, valueMatrix)

4.9. Events

For each event, <EventName>Event is generated with a static decode(topics: string[], data: string).

// ABI: event Transfer(address indexed from, address indexed to, uint256 amount, bool confirmed)
const evt = TransferEvent.decode(topics, data)
// evt.from: Address, evt.to: Address, evt.amount: BigInt, evt.confirmed: bool

Indexed parameters are decoded from topics[1..]; non-indexed parameters are decoded from data. For multiple non-indexed params, the data is treated as an encoded tuple.

4.10. Imports and dependencies

Imports are automatically deduplicated and sorted based on what your ABI requires. Typical imports include:

import { Address, BigInt, Bytes, CallBuilder, ChainId, EvmDecodeParam, EvmEncodeParam, JSON, environment, evm } from '@mimicprotocol/lib-ts'

You do not need to manage these imports manually; they are generated with the file.

4.11. Example

Given this ABI excerpt:

[
  { "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "owner", "type": "address" }], "outputs": [{ "name": "balance", "type": "uint256" }] },
  { "type": "function", "name": "transfer", "stateMutability": "nonpayable", "inputs": [{ "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }], "outputs": [] },
  { "type": "event", "name": "Transfer", "inputs": [ {"name": "from", "type": "address", "indexed": true}, {"name": "to", "type": "address", "indexed": true}, {"name": "amount", "type": "uint256"} ] }
]

You can write:

const token = new ERC20(Address.fromString('0x...'), ChainId.ETHEREUM)

const bal = token.balanceOf(Address.fromString('0xuser')) // BigInt

const call = token.transfer(Address.fromString('0xrecipient'), BigInt.fromString('1000000000000000000')) // CallBuilder
// call.addMaxFee(...).build().send()

const decoded = TransferEvent.decode(topics, data)

4.12. Notes and limitations

  • Only function and event ABI items are processed; others are ignored

  • Overloads are supported via numeric suffixes in method names

  • If a tuple class name cannot be inferred, a warning is emitted and unknown may appear in mapped types

  • Generated files are meant to be committed; do not edit manually (they include a notice)

Last updated