Components

  1. Plugin Entry Point: The main entry point that registers the plugin with Eliza OS.
  2. PayID Service: Core service that communicates with the PayID backend API.
  3. Actions: Defines actions that agents can execute through the Eliza runtime.
  4. Event Handlers: Handles system events and PayID-specific events.
  5. Types: TypeScript interfaces for PayID-specific data structures.

Component Diagram

+---------------------+     +------------------------+
|                     |     |                        |
|    Eliza OS Core    |     |   PayID Backend API    |
|                     |     |                        |
+----------+----------+     +-----------+------------+
           ^                            ^
           |                            |
           |                            |
+----------+-----------------+          |
|                            |          |
|     PayID Plugin Entry     +----------+
|                            |
+---+---------+--------+-----+
    |         |        |
    v         v        v
+---+---+ +---+---+ +--+----+
|       | |       | |       |
|Actions| |Service| | Types |
|       | |       | |       |
+-------+ +---+---+ +-------+
              |
              v
        +-----+------+
        |            |
        |   Events   |
        |            |
        +------------+

Core Components

Plugin Entry Point (index.ts)

The entry point defines plugin metadata and registers components with the Eliza runtime.

import { ElizaPlugin, PluginContext } from '@eliza/plugin-sdk';
import { PayIDService } from './services/payid.service';
import { registerActions } from './actions';
import { registerEventHandlers } from './events';
import { PayIDPluginConfig } from './types';

export default class PayIDPlugin implements ElizaPlugin {
  name = 'payid-plugin';
  version = '1.0.0';
  description = 'PayID functionality for Eliza OS agents';

  async initialize(context: PluginContext): Promise<void> {
    // Extract config
    const config = context.config as PayIDPluginConfig;

    // Validate configuration
    if (!config.apiKey) {
      throw new Error('PayID API key is required');
    }

    // Register the PayID service
    const payIDService = new PayIDService(config.apiKey, config.baseUrl || '<https://api.reveel.id/v1>');
    context.registerService('payid', payIDService);

    // Register actions
    registerActions(context);

    // Register event handlers
    registerEventHandlers(context);

    console.log('PayID plugin initialized successfully');
  }
}

PayID Service (services/payid.service.ts)

The core service that handles communication with the PayID API.

import axios, { AxiosInstance } from 'axios';
import {
  ClaimPayIdRequest,
  SearchPayIdsRequest,
  CheckPriceRequest,
  InitTransactionRequest,
  CreateRouteRequest,
  TransactionActivity,
  PayIdRoute,
  SupportedToken,
  SupportedNetwork
} from '../types';

export class PayIDService {
  private client: AxiosInstance;

  constructor(private apiKey: string, private baseUrl: string) {
    this.client = axios.create({
      baseURL: baseUrl,
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      }
    });
  }

  /**
   * Claim a PayID for a user
   */
  async claimPayId(params: ClaimPayIdRequest): Promise<any> {
    try {
      const response = await this.client.post('/pay-ids', {
        name: params.name,
        userId: params.userId
      });

      const { data } = response.data;
      return data;
    } catch (error) {
      if (error.response?.status === 409) {
        throw new Error('PayID already taken');
      }
      throw new Error(`Failed to claim PayID: ${error.message}`);
    }
  }

  /**
   * Search for PayIDs
   */
  async searchPayIds(params: SearchPayIdsRequest): Promise<any> {
    try {
      const response = await this.client.get('/pay-ids/search', {
        params: {
          q: params.query,
          limit: params.limit || 10,
          activeOnly: params.activeOnly || true
        }
      });

      const { data } = response.data;
      return data.payIds;
    } catch (error) {
      throw new Error(`Failed to search for PayIDs: ${error.message}`);
    }
  }

  /**
   * Initialize a transaction
   */
  async initTransaction(params: InitTransactionRequest): Promise<any> {
    try {
      const payload: any = {
        userId: params.userId,
        amount: params.amount,
        token: params.token,
        network: params.network
      };

      if (params.recipientPayId) {
        payload.recipientPayId = params.recipientPayId;
      } else if (params.walletAddress) {
        payload.walletAddress = params.walletAddress;
      } else {
        throw new Error('Either recipientPayId or walletAddress must be provided');
      }

      if (params.message) {
        payload.message = params.message;
      }

      const response = await this.client.post('/transactions', payload);

      const { data } = response.data;
      return data;
    } catch (error) {
      throw new Error(`Failed to initialize transaction: ${error.message}`);
    }
  }

  /**
   * Create a payment route
   */
  async createRoute(params: CreateRouteRequest): Promise<PayIdRoute> {
    try {
      const response = await this.client.post('/routes', {
        userId: params.userId,
        name: params.name,
        incomingNetworks: params.incomingNetworks,
        incomingTokens: params.incomingTokens,
        incomingWallets: params.incomingWallets || [],
        swapNetwork: params.swapNetwork || null,
        swapToken: params.swapToken || null,
        outgoingWallet: params.outgoingWallet
      });

      const { data } = response.data;
      return data.route;
    } catch (error) {
      throw new Error(`Failed to create route: ${error.message}`);
    }
  }

  /**
   * Get routes for a user
   */
  async getRoutes(userId: string): Promise<PayIdRoute[]> {
    try {
      const response = await this.client.get(`/routes/${userId}`);

      const { data } = response.data;
      return data.routes;
    } catch (error) {
      throw new Error(`Failed to get routes: ${error.message}`);
    }
  }

  /**
   * Get transaction history for a user
   */
  async getTransactionHistory(userId: string, page: number = 1, pageSize: number = 10): Promise<{activities: TransactionActivity[], pagination: any}> {
    try {
      const response = await this.client.get(`/transactions/users/${userId}/activities`, {
        params: {
          page,
          pageSize
        }
      });

      const { data } = response.data;
      return data;
    } catch (error) {
      throw new Error(`Failed to get transaction history: ${error.message}`);
    }
  }

  /**
   * Get supported tokens and networks
   */
  getSupportedTokens(): SupportedToken[] {
    return ['USDT', 'USDC', 'ETH', 'BNB', 'POL'];
  }

  getSupportedNetworks(): SupportedNetwork[] {
    return ['ETH', 'POL', 'OP', 'BNB', 'BASE'];
  }

  /**
   * Validate token/network compatibility
   */
  isValidTokenNetworkPair(token: SupportedToken, network: SupportedNetwork): boolean {
    // Based on the API documentation, these combinations are not supported
    const invalidCombinations = [
      { token: 'POL', network: 'OP' },
      { token: 'BNB', network: 'OP' },
      { token: 'USDT', network: 'BASE' },
      { token: 'BNB', network: 'BASE' },
      { token: 'POL', network: 'BASE' }
    ];

    return !invalidCombinations.some(
      combo => combo.token === token && combo.network === network
    );
  }
}

Actions (actions/index.ts)

Defines actions that agents can execute through the Eliza runtime.

import { PluginContext } from '@eliza/plugin-sdk';
import {
  ClaimPayIdRequest,
  SearchPayIdsRequest,
  InitTransactionRequest,
  CreateRouteRequest
} from '../types';

export function registerActions(context: PluginContext) {
  // Action to claim a PayID
  context.registerAction('claimPayId', async (params: ClaimPayIdRequest) => {
    const payIdService = context.getService('payid');
    return await payIdService.claimPayId(params);
  });

  // Action to search for PayIDs
  context.registerAction('searchPayIds', async (params: SearchPayIdsRequest) => {
    const payIdService = context.getService('payid');
    return await payIdService.searchPayIds(params);
  });

  // Action to initialize a transaction
  context.registerAction('initTransaction', async (params: InitTransactionRequest) => {
    const payIdService = context.getService('payid');
    return await payIdService.initTransaction(params);
  });

  // Action to create a payment route
  context.registerAction('createRoute', async (params: CreateRouteRequest) => {
    const payIdService = context.getService('payid');
    return await payIdService.createRoute(params);
  });

  // Action to get routes for a user
  context.registerAction('getRoutes', async (userId: string) => {
    const payIdService = context.getService('payid');
    return await payIdService.getRoutes(userId);
  });

  // Action to get transaction history
  context.registerAction('getTransactionHistory', async (userId: string, page?: number, pageSize?: number) => {
    const payIdService = context.getService('payid');
    return await payIdService.getTransactionHistory(userId, page, pageSize);
  });

  // Utility actions
  context.registerAction('getSupportedTokens', () => {
    const payIdService = context.getService('payid');
    return payIdService.getSupportedTokens();
  });

  context.registerAction('getSupportedNetworks', () => {
    const payIdService = context.getService('payid');
    return payIdService.getSupportedNetworks();
  });

  context.registerAction('validateTokenNetworkPair', (token, network) => {
    const payIdService = context.getService('payid');
    return payIdService.isValidTokenNetworkPair(token, network);
  });
}

Event Handlers (events/index.ts)