SDK

The Mimic Protocol SDK provides a TypeScript client to interact with the Mimic Protocol blockchain automation platform. It offers a developer-friendly interface for managing tasks, configurations, exe

Installation

yarn add @mimicprotocol/sdk

Getting Started

Basic Client Initialization

import { Client } from '@mimicprotocol/sdk'

// Basic client with default configuration
const client = new Client()

// Client with custom base URL
const client = new Client({
  baseUrl: 'https://api-protocol.mimic.fi'
})

Client with Authentication

import { Client, ApiKeyAuth, BearerAuth } from '@mimicprotocol/sdk'

// Using API key authentication
const client = new Client({
  auth: new ApiKeyAuth('your-api-key')
})

// Using Bearer token authentication
const client = new Client({
  auth: new BearerAuth('your-bearer-token')
})

Client with Signer

import { Client, EthersSigner } from '@mimicprotocol/sdk'
import { ethers } from 'ethers'

// Using private key
const signer = EthersSigner.fromPrivateKey('0x...')
const client = new Client({
  signer
})

// Using JSON-RPC signer (e.g., MetaMask)
const provider = new ethers.BrowserProvider(window.ethereum)
const jsonRpcSigner = await provider.getSigner()
const signer = EthersSigner.fromJsonRpcSigner(jsonRpcSigner)
const client = new Client({
  signer
})

// Using browser wallet directly
import { WindowEthereumSigner } from '@mimicprotocol/sdk'

const signer = new WindowEthereumSigner('0x...') // wallet address
const client = new Client({
  signer
})

Domain Clients

The SDK is organized into domain-specific clients, each handling a specific aspect of the protocol.

Balances

Query balance entries and compute totals.

// List balances (optionally filter by address)
const balances = await client.balances.get({
  address: '0x...',
  limit: 20,
  offset: 0,
})

// Example item
// {
//   address: '0x...',
//   amount: 12.34,
//   description: 'Monthly quota top-up',
//   createdAt: new Date(),
//   expiresAt: new Date() // optional
// }

// Get total balance for a specific address
const total = await client.balances.getTotal('0x...')
console.log(total.address, total.balance)

// Pagination
const nextPage = await client.balances.get({ offset: 20, limit: 20 })

Tasks

Manage WASM tasks and their manifests.

// List all tasks
const tasks = await client.tasks.get()

// List tasks with filters
const tasks = await client.tasks.get({
  creator: '0x...',
  limit: 10,
  offset: 0
})

// Get specific task by CID
const task = await client.tasks.getByCid('Qm...')

// Get task manifest
const manifest = await client.tasks.getManifest('Qm...')

// Download WASM binary
const wasmBlob = await client.tasks.getWasm('Qm...')

// Create new task (requires API key authentication)
const newTask = await client.tasks.create({
  manifestFile: new File([manifestJson], 'manifest.json'),
  wasmFile: new File([wasmBytes], 'wasm.wasm')
})

Configs

Manage task configurations and their lifecycle. Configs define how and when tasks should be executed.

// List all configurations
const configs = await client.configs.get()

// List configs with filters
const configs = await client.configs.get({
  sigs: ['0x...', '0x...'],           // specific signatures
  taskCid: 'Qm...',                   // filter by task
  signer: '0x...',                    // filter by creator
  active: true,                       // filter by status
  createdAfter: new Date('2024-01-01'),
  createdBefore: new Date('2024-12-31'),
  offset: 0,
  limit: 20,
})

// Get specific configuration by signature
const config = await client.configs.getBySignature('0x...')

// Example config structure:
// {
//   sig: '0x...',
//   taskCid: 'Qm...',
//   signer: '0x...',
//   version: '1.0.0',
//   active: true,
//   description: 'My automation task',
//   createdAt: new Date(),
//   input: { token: '0x...', amount: '1000000000000000000' },
//   trigger: { type: 'cron', schedule: '0 0 * * *', delta: '1h', endDate: 0 },
//   executionFeeLimit: '1000000000000000000',
//   minValidations: 1,
//   types: { Input: [...], Trigger: [...], Config: [...] }
// }

// Create and sign a new configuration (requires signer)
const newConfig = await client.configs.signAndCreate({
  description: 'Daily DCA automation',
  taskCid: 'Qm...',
  version: '1.0.0',
  manifest: { /* task manifest with inputs definition */ },
  trigger: {
    type: 'cron',                     // or 'event'
    schedule: '0 0 * * *',            // cron expression (daily at midnight)
    delta: '1h',                      // execution window
    endDate: 0                        // 0 = no end date
  },
  input: { 
    token: '0x...', 
    amount: '1000000000000000000'     // must match manifest inputs
  },
  executionFeeLimit: '1000000000000000000',  // max fee in wei
  minValidations: 1                   // minimum validations required
})

// Deactivate a configuration (requires signer)
const deactivatedConfig = await client.configs.signAndDeactivate('0x...')

// Check if configuration is expired
const isExpired = client.configs.isExpired(config)

// Get next execution timestamp for cron triggers
const nextExecution = client.configs.getNextExecutionTimestamp('0 0 * * *', Date.now())

Trigger Types

Cron Trigger:

{
  type: 'cron',
  schedule: '0 0 * * *',    // cron expression
  delta: '1h',              // execution window (e.g., '30m', '2h', '1d')
  endDate: 0                // timestamp or 0 for no end
}

Event Trigger:

{
  type: 'event',
  chainId: 1,               // blockchain chain ID
  contract: '0x...',        // contract address to monitor
  topics: ['0x...'],        // event topics (1-4 topics)
  delta: '1h',              // execution window
  endDate: 0                // timestamp or 0 for no end
}

Executions

Query task execution history and results. Each execution includes inputs (oracle responses), outputs (intents), fee breakdown, and validations.

// List executions (with optional filters)
const executions = await client.executions.get({
  configSig: '0x...',                 // by configuration signature
  createdAfter: new Date('2024-01-01'),
  createdBefore: new Date('2024-12-31'),
  offset: 0,
  limit: 50,
})

// Inspect an execution
for (const ex of executions) {
  console.log(ex.hash, ex.timestamp, ex.status)
  console.log('Result:', ex.result)             // succeeded | failed
  console.log('Relayer:', ex.relayer)
  console.log('Fuel used:', ex.fuelUsed)

  // Optional fee breakdown
  if (ex.fee) {
    console.log('Fee total:', ex.fee.total)
  }

  // Inputs (oracle responses)
  for (const input of ex.inputs) {
    console.log('Oracle input:', input)
  }

  // Outputs (intents emitted)
  for (const out of ex.outputs) {
    console.log('Intent hash:', out.hash)
  }

  // Optional validations
  if (ex.validations) {
    for (const v of ex.validations) {
      console.log('Validation:', v.succeeded, v.signature)
    }
  }
}

// Get specific execution by hash
const execution = await client.executions.getByHash('0x...')

// Example execution structure:
// {
//   hash: '0x...',
//   configSig: '0x...',
//   timestamp: new Date(),
//   fuelUsed: 1234,
//   logs: ['...'],
//   inputs: [ /* oracle responses */ ],
//   outputs: [ { hash: '0x...', /* intent fields */ } ],
//   signature: '0x...',
//   result: 'succeeded' | 'failed',
//   relayer: '0x...',
//   status: 'enqueued' | 'submitted' | 'succeeded' | 'failed',
//   createdAt: new Date(),
//   fee?: { trigger, relayer, oracles, validator, intents, protocol, total },
//   validations?: [ { signature: '0x...', succeeded: true, description?: string } ]
// }

Intents

Query, encode and decode intents and proposals associated to executions.

// List intents (filters are optional)
const intents = await client.intents.get({
  user: '0x...',                 // user address
  settler: '0x...',              // settler address
  deadlineAfter: BigInt(1714600000),
  deadlineBefore: BigInt(1735600000),
  offset: 0,
  limit: 20,
})

// Get a specific intent by hash
const intent = await client.intents.getByHash('0x...')

// Intent structure example:
// {
//   hash: '0x...',
//   executionHash: '0x...',
//   op: 0 | 1 | 2,             // swap | transfer | call
//   user: '0x...',
//   settler: '0x...',
//   nonce: '0x...',
//   deadline: '1699999999',     // epoch seconds (string)
//   data: '0x...',              // ABI-encoded data
//   status: 'enqueued' | 'discarded' | 'submitted' | 'succeeded' | 'failed' | 'expired',
//   proposals: [
//     {
//       solver: '0x...',
//       data: '0x...',
//       deadline: '1699999999',
//       fees: ['100', '200'],    // raw bigint strings
//       feeUsd: 12.34,
//       signatures: ['0x...'],
//       transactionHash?: '0x...',
//       destTransactionHash?: '0x...',
//       status: 'received' | 'discarded' | 'submitted' | 'succeeded' | 'failed',
//       description?: string,
//     },
//   ],
//   logs: [ { level: 'info' | 'success' | 'error', data: '0x...', createdAt: Date } ]
// }

// Type guards and decoders
if (client.intents.isSwap(intent)) {
  const swap = client.intents.decodeSwapIntent(intent)
  console.log(swap.sourceChain, swap.targetChain)
} else if (client.intents.isTransfer(intent)) {
  const transfer = client.intents.decodeTransferIntent(intent)
  console.log(transfer.chainId, transfer.token)
} else if (client.intents.isCall(intent)) {
  const call = client.intents.decodeCallIntent(intent)
  console.log(call.chainId, call.target)
}

// ChainId helper
const chainId = client.intents.getChainId(intent)

// Proposal decoder (supports swap proposals)
const decoded = client.intents.decodeProposal(intent.proposals[0])

// Encoders
const encodedIntent = client.intents.encodeIntent({ /* Intent fields */ })
const encodedProposal = client.intents.encodeProposal({ /* Proposal fields */ }, intent)
const swapDataHex = client.intents.encodeSwapIntentData({ /* SwapIntentData */ })
const transferDataHex = client.intents.encodeTransferIntentData({ /* TransferIntentData */ })
const callDataHex = client.intents.encodeCallIntentData({ /* CallIntentData */ })

Error Handling

The SDK uses structured error handling with the ApiError class.

import { ApiError } from '@mimicprotocol/sdk'

try {
  const config = await client.configs.getBySignature('invalid-signature')
} catch (error) {
  if (error instanceof ApiError) {
    console.error('API Error:', error.message)
    console.error('Status:', error.status)
    console.error('Code:', error.code)
    console.error('Details:', error.details)
  } else {
    console.error('Unexpected error:', error)
  }
}

TypeScript Types

The SDK exports comprehensive TypeScript types for all data structures:

import type {
  // Core types
  Client,
  InitOptions,
  
  // Domain types
  Config,
  Task,
  Execution,
  Intent,
  Balance,
  
  // Authentication types
  Signer,
  EthersSigner,
  WindowEthereumSigner,
  ApiKeyAuth,
  BearerAuth,
  
  // Utility types
  Address,
  Signature,
  ChainId,
  Hash
} from '@mimicprotocol/sdk'

Configuration Options

Client Configuration

interface InitOptions {
  domains?: Partial<CoreConfig>  // Per-domain configuration
  baseUrl?: string              // Global base URL
  auth?: AuthStrategy           // Global authentication
  signer?: Signer              // Global signer
}

Per-Domain Configuration

const client = new Client({
  domains: {
    configs: {
      baseUrl: 'https://...',
      auth: new ApiKeyAuth('configs-key'),
      timeoutMs: 30000
    },
    tasks: {
      baseUrl: 'https://...',
      auth: new BearerAuth('tasks-token')
    }
  }
})

Best Practices

  1. Error Handling: Always wrap SDK calls in try-catch blocks and handle ApiError instances appropriately.

  2. Type Safety: Leverage TypeScript types for better development experience and compile-time error checking.

  3. Configuration Management: Use environment variables for sensitive data like API keys and private keys.

  4. Signer Management: In production, use secure signer implementations and never expose private keys in client-side code.

Last updated