Typed wrappers from Solidity ABIs

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.


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'

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>
}

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. 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.


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.


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

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

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)

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.


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.


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)

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