> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirador.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin System

> Extend trace functionality with plugins

## 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 — EVM transaction hints, Safe multisig tracking, Solana transaction hints, Relay (relay.link) bridge tracking, Canton (Daml Ledger API) transaction hints, and provider-based transaction sending.

### Setup

```typescript theme={null}
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:

```typescript theme={null}
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');

// Solana transaction methods — no chain arg, signature is the identifier.
// See concepts/solana-transactions.
trace.web3.solana.addTxHint(solanaSignature);
trace.web3.solana.addTxHint(solanaSignature, 'Jupiter swap');

// Relay (relay.link) bridge methods — record once, before the user deposits.
// The backend processor resolves the full quote from requestId and emits
// deposit → solver-committed → fill (or refund / failed / not-found) for
// you. See concepts/relay-bridges.
trace.web3.relay.addQuoteHint('rly_request_123');
trace.web3.relay.addQuoteHint('rly_request_456', 'queued from swap modal');

// Canton (Daml Ledger API) methods — correlate by ledger updateId.
// partyId is optional (omit for observer co-hosts). See concepts/canton-transactions.
trace.web3.canton.addTxHint(updateId);
trace.web3.canton.addTxHint(updateId, 'Alice::1220...', 'IOU transfer');
```

### Method Chaining

Web3 methods return the trace instance for chaining:

```typescript theme={null}
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:

| Hook             | When Called            | Use Case                                          |
| ---------------- | ---------------------- | ------------------------------------------------- |
| `onFlush`        | Before each flush      | Inject plugin-managed data into the flush payload |
| `onClose`        | When trace is closed   | Clean up resources, finalize pending operations   |
| `hasPendingData` | Before close completes | Signal that the plugin has unflushed data         |

## Creating Custom Plugins

A plugin implements the `MiradorPlugin` interface:

```typescript theme={null}
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

```typescript theme={null}
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:

```typescript theme={null}
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;
}
```

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

### FlushBuilder

The `FlushBuilder` passed to `onFlush` lets plugins contribute data to the outgoing flush:

```typescript theme={null}
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

<CardGroup cols={2}>
  <Card title="Transaction Hints" icon="cube" href="/concepts/transaction-hints">
    EVM transaction hints via the Web3 plugin
  </Card>

  <Card title="Solana Transactions" icon="wave-pulse" href="/concepts/solana-transactions">
    Solana signature hints — no chain argument
  </Card>

  <Card title="Safe Multisig" icon="vault" href="/concepts/safe-multisig">
    Safe message and transaction tracking
  </Card>

  <Card title="Relay Bridges" icon="bridge" href="/concepts/relay-bridges">
    Intent-based cross-chain bridges
  </Card>
</CardGroup>
