跳转至内容

构建网关连接器

概述

本指南将引导您完成为去中心化交易所(DEX)构建新网关连接器的过程。网关连接器使 Hummingbot 能够通过标准化的 REST API 接口与基于区块链的交易协议进行交互。

网关支持三种类型的 DEX 连接器:

  • Router:查找最佳兑换路线的 DEX 聚合器
  • AMM:传统的 V2 风格恒定乘积池
  • CLMM:具有自定义价格范围的集中流动性做市商

先决条件

在构建网关连接器之前,请确保您具备:

开发环境

  • Node.js 18+ 和 pnpm
  • TypeScript 知识
  • 对目标区块链和 DEX 协议的理解

协议知识

  • 对 DEX 智能合约的熟悉
  • 对协议 SDK 或 API 的理解
  • 流动性池机制的知识

网关设置

  • 已配置网关开发环境
  • 能够在本地运行和测试网关

连接器架构

网关连接器遵循模块化架构:

src/connectors/{protocol}/
├── {protocol}.ts           # Main connector class
├── {protocol}.config.ts    # Configuration interface
├── {protocol}.constants.ts # Protocol-specific constants
├── {protocol}.utils.ts     # Helper functions
├── router-routes/          # Router endpoints (if applicable)
├── amm-routes/            # AMM endpoints (if applicable)
└── clmm-routes/           # CLMM endpoints (if applicable)

实施步骤

步骤 1:选择连接器类型

确定您的 DEX 支持哪些交易类型:

类型 用例 关键方法
Router DEX 聚合器,仅兑换协议 quote, trade, estimateGas
AMM 具有 LP 代币的 V2 风格池 poolPrice, addLiquidity, removeLiquidity
CLMM 具有范围的集中流动性 openPosition, addLiquidity, collectFees

步骤 2:创建连接器类

使用单例模式创建主连接器类:

// src/connectors/mydex/mydex.ts
import { ConnectorBase } from '../../services/connector-base';

export class MyDex extends ConnectorBase {
  private static instances: Record<string, MyDex> = {};
  private chain: string;
  private network: string;

  private constructor(chain: string, network: string) {
    super(chain, network);
    this.chain = chain;
    this.network = network;
  }

  public static getInstance(chain: string, network: string): MyDex {
    const key = `${chain}:${network}`;
    if (!MyDex.instances[key]) {
      MyDex.instances[key] = new MyDex(chain, network);
    }
    return MyDex.instances[key];
  }

  // Core initialization
  public async init(): Promise<void> {
    // Initialize SDK, load contracts, etc.
  }

  // Required: Get connector name
  public get name(): string {
    return 'mydex';
  }

  // Required: Get router/factory address
  public get routerAddress(): string {
    return this.config.routerAddress;
  }
}

步骤 3:实现交易方法

根据您的连接器类型,实现所需方法:

路由方法

// Quote a swap
async quote(
  base: Token,
  quote: Token,
  amount: BigNumber,
  side: 'BUY' | 'SELL'
): Promise<SwapQuote> {
  // Implement quote logic
  return {
    route: optimalRoute,
    expectedOut: outputAmount,
    priceImpact: impact,
    gasEstimate: gasLimit
  };
}

// Execute a swap
async trade(
  wallet: Wallet,
  quote: SwapQuote,
  slippage: number
): Promise<Transaction> {
  // Build and execute transaction
  return transaction;
}

AMM 方法

// Get pool information
async poolInfo(
  base: Token,
  quote: Token
): Promise<PoolInfo> {
  // Fetch pool data
  return {
    reserves: [baseReserve, quoteReserve],
    fee: poolFee,
    liquidity: totalLiquidity
  };
}

// Add liquidity
async addLiquidity(
  wallet: Wallet,
  base: Token,
  quote: Token,
  baseAmount: BigNumber,
  quoteAmount: BigNumber,
  slippage: number
): Promise<Transaction> {
  // Add liquidity logic
  return transaction;
}

CLMM 方法

// Open a concentrated liquidity position
async openPosition(
  wallet: Wallet,
  pool: Pool,
  lowerPrice: number,
  upperPrice: number,
  baseAmount: BigNumber,
  quoteAmount: BigNumber
): Promise<Position> {
  // Create position NFT
  return position;
}

// Collect earned fees
async collectFees(
  wallet: Wallet,
  positionId: string
): Promise<Transaction> {
  // Collect fees logic
  return transaction;
}

步骤 4:创建路由处理器

为支持的操作创建路由处理器文件:

// src/connectors/mydex/router-routes/router.routes.ts
import { Router, Request, Response } from 'express';
import { MyDex } from '../mydex';
import { 
  QuoteSwapRequest,
  QuoteSwapResponse,
  ExecuteSwapRequest,
  ExecuteSwapResponse 
} from '../../../schemas/router-schema';

export const routerRoutes = Router();

routerRoutes.post('/quote-swap', async (req: Request, res: Response) => {
  const request = req.body as QuoteSwapRequest;
  const connector = MyDex.getInstance(request.chain, request.network);

  try {
    const quote = await connector.quote(
      request.base,
      request.quote,
      request.amount,
      request.side
    );

    const response: QuoteSwapResponse = {
      network: request.network,
      timestamp: Date.now(),
      latency: 0,
      base: request.base,
      quote: request.quote,
      amount: request.amount,
      expectedOut: quote.expectedOut,
      price: quote.price,
      gasEstimate: quote.gasEstimate,
      route: quote.route
    };

    res.status(200).json(response);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

routerRoutes.post('/execute-swap', async (req: Request, res: Response) => {
  // Implementation for swap execution
});

步骤 5:添加配置

为您的连接器创建配置文件:

模式定义

// src/templates/namespace/mydex-schema.json
{
  "type": "object",
  "properties": {
    "allowedSlippage": {
      "type": "number",
      "description": "Maximum slippage percentage",
      "default": 1.0
    },
    "gasLimitEstimate": {
      "type": "number",
      "description": "Estimated gas limit",
      "default": 300000
    },
    "ttl": {
      "type": "number",
      "description": "Quote time-to-live in seconds",
      "default": 30
    },
    "contractAddresses": {
      "type": "object",
      "patternProperties": {
        "^[a-z]+$": {
          "type": "object",
          "properties": {
            "routerAddress": { "type": "string" },
            "factoryAddress": { "type": "string" }
          }
        }
      }
    }
  },
  "required": ["allowedSlippage", "gasLimitEstimate", "ttl"]
}

默认配置

# src/templates/connectors/mydex.yml
allowedSlippage: 1.0
gasLimitEstimate: 300000
ttl: 30

contractAddresses:
  mainnet:
    routerAddress: '0x...'
    factoryAddress: '0x...'
  testnet:
    routerAddress: '0x...'
    factoryAddress: '0x...'

步骤 6:注册连接器

在主连接器路由中注册您的连接器:

// src/connectors/connector.routes.ts
import { Router } from 'express';
import { routerRoutes as mydexRouterRoutes } from './mydex/router-routes/router.routes';
import { ammRoutes as mydexAmmRoutes } from './mydex/amm-routes/amm.routes';

export const connectorRoutes = Router();

// Add your connector routes
connectorRoutes.use('/mydex/router', mydexRouterRoutes);
connectorRoutes.use('/mydex/amm', mydexAmmRoutes);

步骤 7:编写测试

为您的连接器创建全面的测试:

// test/connectors/mydex/mydex.test.ts
import { MyDex } from '../../../src/connectors/mydex/mydex';

describe('MyDex Connector', () => {
  let connector: MyDex;

  beforeEach(() => {
    connector = MyDex.getInstance('ethereum', 'mainnet');
  });

  describe('quote', () => {
    it('should return valid quote for token swap', async () => {
      const quote = await connector.quote(
        mockTokenA,
        mockTokenB,
        BigNumber.from('1000000'),
        'SELL'
      );

      expect(quote).toBeDefined();
      expect(quote.expectedOut).toBeGreaterThan(0);
      expect(quote.priceImpact).toBeLessThan(0.1);
    });

    it('should handle insufficient liquidity', async () => {
      await expect(
        connector.quote(
          mockTokenA,
          mockTokenB,
          BigNumber.from('999999999999'),
          'SELL'
        )
      ).rejects.toThrow('Insufficient liquidity');
    });
  });

  // Add tests for all methods
});

添加链支持

链支持状态

Gateway 目前不接受新的区块链实现的拉取请求。该框架目前支持:- EVM 链:以太坊和 EVM 兼容链(Arbitrum、Optimism、Base、Polygon、BSC、Avalanche 等)- SVM 链:Solana 和 SVM 兼容链

如果您的连接器需要基于 EVM 或 SVM 架构的链,您可以继续进行以下实现。对于全新的区块链架构,请查看 GitHub 仓库 了解何时将接受新链支持的更新。

如果您的连接器需要新的区块链:

步骤 1:创建链实现

// src/chains/mychain/mychain.ts
import { ChainBase } from '../../services/chain-base';

export class MyChain extends ChainBase {
  private static instances: Record<string, MyChain> = {};

  public static getInstance(network: string): MyChain {
    if (!MyChain.instances[network]) {
      MyChain.instances[network] = new MyChain(network);
    }
    return MyChain.instances[network];
  }

  // Implement required methods
  async getWallet(address: string): Promise<Wallet> {
    // Wallet implementation
  }

  async getBalance(address: string): Promise<Balance> {
    // Balance checking logic
  }

  async getTokens(symbols: string[]): Promise<Token[]> {
    // Token resolution logic
  }
}

步骤 2:创建链路由

// src/chains/mychain/routes/mychain.routes.ts
import { Router } from 'express';
import { MyChain } from '../mychain';

export const chainRoutes = Router();

chainRoutes.get('/balance', async (req, res) => {
  const { address } = req.query;
  const chain = MyChain.getInstance(req.query.network);
  const balance = await chain.getBalance(address);
  res.json(balance);
});

步骤 3:添加链配置

# src/templates/chains/mychain.yml
networks:
  mainnet:
    rpcUrl: 'https://rpc.mychain.io'
    chainId: 1234
    nativeCurrency: 'MYCOIN'
  testnet:
    rpcUrl: 'https://testnet-rpc.mychain.io'
    chainId: 5678
    nativeCurrency: 'TESTCOIN'

测试要求

所有 Gateway 连接器必须满足这些测试标准:

  1. 代码覆盖率:最低 75% 覆盖率
  2. 单元测试:测试所有公共方法
  3. 集成测试:测试 API 端点
  4. 错误处理:测试失败场景
  5. 模拟数据:使用真实测试数据

运行测试

# Run all tests
pnpm test

# Run tests for specific connector
pnpm test -- mydex

# Check coverage
pnpm test:coverage

代码质量标准

代码检查和格式化

Gateway 使用 ESLint 和 Prettier 进行代码质量控制:

# Run linting
pnpm lint

# Auto-fix linting issues
pnpm lint:fix

# Format code
pnpm format

TypeScript 最佳实践

  1. 强类型:使用显式类型,避免 any
  2. 错误处理:实现正确的错误类
  3. Async/Await:使用现代异步模式
  4. 文档:为公共方法添加 JSDoc 注释

提交检查清单

提交您的连接器之前:

  • 所有测试通过,覆盖率 >75%
  • 代码通过代码检查和格式化检查
  • 为所有支持的网络添加了配置文件
  • API 端点遵循架构规范
  • 文档包含使用示例
  • 错误处理涵盖边缘情况
  • 使用真实负载进行性能测试
  • 安全审查已完成

下一步

  1. 本地测试:使用您的连接器运行 Gateway 并测试所有操作
  2. 创建示例:添加显示连接器用法的示例脚本
  3. 提交 PR:创建拉取请求到 Gateway 仓库
  4. 治理提案:如需要,提交 新连接器提案

资源