Skip to main content

Overview

Plugins extend trace functionality by adding namespaced methods and lifecycle hooks. The SDK ships with a built-in Web3Plugin for blockchain integration, and you can create custom plugins for your own needs.

Web3Plugin

The Web3Plugin provides all blockchain-related methods — transaction hints, Safe multisig tracking, and provider-based transaction sending.

Setup

import { Client, Web3Plugin } from '@miradorlabs/web-sdk';

const client = new Client('api-key', {
  plugins: [Web3Plugin({ provider: window.ethereum })]
});

Namespaced Methods

Once installed, Web3 methods are available on every trace under the web3 namespace:
const trace = client.trace({ name: 'Swap' });

// EVM transaction methods
trace.web3.evm.addTxHint(txHash, 'ethereum');
trace.web3.evm.addTxHint(txHash, 'ethereum', { input: calldata, details: 'Swap' });
trace.web3.evm.addTxInputData(calldata);
trace.web3.evm.addTx(tx);
trace.web3.evm.addTx(tx, 'polygon');
const hash = await trace.web3.evm.sendTransaction(txParams);

// Safe multisig methods
trace.web3.safe.addMsgHint(msgHash, 'ethereum');
trace.web3.safe.addMsgHint(msgHash, 'ethereum', 'Approval');
trace.web3.safe.addTxHint(safeTxHash, 'ethereum');
trace.web3.safe.addTxHint(safeTxHash, 'ethereum', 'Execution');

Method Chaining

Web3 methods return the trace instance for chaining:
const trace = client.trace({ name: 'SafeTransfer' })
  .addAttribute('safeAddress', '0xSafe...')
  .addTags(['safe', 'multisig']);

trace.web3.safe.addMsgHint(msgHash, 'ethereum', 'Approval');
trace.web3.evm.addTxHint(execTxHash, 'ethereum', 'Execution');

Plugin Lifecycle Hooks

Plugins can hook into the trace lifecycle:
HookWhen CalledUse Case
onFlushBefore each flushInject plugin-managed data into the flush payload
onCloseWhen trace is closedClean up resources, finalize pending operations
hasPendingDataBefore close completesSignal that the plugin has unflushed data

Creating Custom Plugins

A plugin implements the MiradorPlugin interface:
import type { MiradorPlugin, TraceContext, PluginSetupResult, FlushBuilder } from '@miradorlabs/web-sdk';

interface MyMethods {
  doSomething(value: string): void;
}

function MyPlugin(config: MyConfig): MiradorPlugin<MyMethods> {
  return {
    name: 'my-plugin',
    setup(context: TraceContext): PluginSetupResult<MyMethods> {
      let pendingItems: string[] = [];

      return {
        // Methods merged onto the trace under the plugin name
        methods: {
          doSomething(value: string) {
            if (context.isClosed()) return;
            pendingItems.push(value);
            context.scheduleFlush();
          }
        },

        // Lifecycle hooks
        onFlush(builder: FlushBuilder) {
          for (const item of pendingItems) {
            builder.addEvent({
              name: 'my_plugin:action',
              details: item,
              timestamp: new Date()
            });
          }
          pendingItems = [];
        },

        onClose() {
          pendingItems = [];
        },

        hasPendingData() {
          return pendingItems.length > 0;
        }
      };
    }
  };
}

Using Custom Plugins

const client = new Client('api-key', {
  plugins: [MyPlugin({ option: 'value' })]
});

const trace = client.trace({ name: 'Example' });
trace.my_plugin.doSomething('hello');

TraceContext

The TraceContext object passed to setup() provides read/write access to the trace:
interface TraceContext {
  addEvent(name: string, details?: string | object, options?: AddEventOptions): void;
  addAttribute(key: string, value: string | number | boolean | object): void;
  addAttributes(attrs: Record<string, string | number | boolean | object>): void;
  addTag(tag: string): void;
  addTags(tags: string[]): void;
  getTraceId(): string;
  isClosed(): boolean;
  scheduleFlush(): void;
  logger: Logger;
}
Always check context.isClosed() before starting async work (timers, listeners, network calls) in your plugin — the trace may have been closed between setup and your callback firing.

FlushBuilder

The FlushBuilder passed to onFlush lets plugins contribute data to the outgoing flush:
interface FlushBuilder {
  addHint<T extends HintTypeName>(type: T, data: HintDataMap[T]): void;
  addHint(type: string, data: Record<string, unknown>): void;
  addEvent(event: { name: string; details?: string; timestamp: Date; severity?: Severity }): void;
  addAttribute(key: string, value: string): void;
  addTag(tag: string): void;
}

Next Steps

Transaction Hints

EVM transaction hints via the Web3 plugin

Safe Multisig

Safe message and transaction tracking

Types

Plugin type definitions