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
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/cli2.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 compile3. 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 ETH3.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.timestamp3.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
ContractNameclass: ergonomic read/write methods that encode calls and decode responsesA
ContractNameUtilshelper class: staticencodeX/decodeXhelpers per functionTuple classes for
tuple/structparams and returnsEvent classes
<EventName>Eventwith a staticdecode(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 codegenreadsmanifest.yamlinputs→src/types/index.tsabis→src/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,timestampOne method per ABI function. Read methods call
environment.contractCall; write methods return aCallBuilderand 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 →
Addressbool →
boolstring →
stringbytes / bytesN →
BytesintN/uintN →
BigIntfor N ≥ 24;i8/u8/i16/u16/i32/u32/i64/u64for smaller widthsArrays 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 flowEncoding converts primitives as needed:
// bool → Bytes.fromBool
// u8 → BigInt.fromU8, etc.
// tuples → EvmEncodeParam.fromValues('()', tuple.toEvmEncodeParams())
// arrays → nested EvmEncodeParam.fromValues with map4.7. Tuples and structs
The generator extracts tuple/struct definitions from inputs, outputs, and multi-return functions.
If
internalTypeincludes a struct name (e.g.,struct UserContract.UserInfo), that name is usedOtherwise it falls back to
Tuple<N>For functions with multiple outputs, a synthetic
<FunctionName>Outputsclass is created
Tuple classes provide:
static parse(data: string): <Class>to parse decoded stringstoEvmEncodeParams(): EvmEncodeParam[]for encoding
// Example return tuple
const info: UserInfo = erc20.getUserInfo()
// Example tuple param
const created = contract.createUser(new UserInfo(id, name, active)) // CallBuilder4.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: boolIndexed 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
functionandeventABI items are processed; others are ignoredOverloads are supported via numeric suffixes in method names
If a tuple class name cannot be inferred, a warning is emitted and
unknownmay appear in mapped typesGenerated files are meant to be committed; do not edit manually (they include a notice)
Last updated