构建网关连接器¶
概述¶
本指南将引导您完成为去中心化交易所(DEX)构建新网关连接器的过程。网关连接器使 Hummingbot 能够通过标准化的 REST API 接口与基于区块链的交易协议进行交互。
网关支持三种类型的 DEX 连接器:
- 路由器:寻找最优兑换路径的 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 支持哪些交易类型:
| 类型 | 使用场景 | 关键方法 | 
|---|---|---|
| 路由 | DEX 聚合器、仅支持兑换的协议 | quote、trade、estimateGas | 
| AMM | 带有流动性代币(LP tokens)的 V2 风格资金池 | poolPrice、addLiquidity、removeLiquidity | 
| CLMM | 支持价格区间的集中流动性 | openPosition、addLiquidity、collectFees | 
步骤 2:创建连接器类¶
创建主连接器类——如果你正在构建基于以太坊的去中心化交易所(DEX)连接器,请参考 Uniswap;如果你正在构建基于 Solana 的 DEX 连接器,请参考 Raydium。
// src/connectors/mydex/mydex.ts
export class MyDex {
  private static instances: Record<string, MyDex> = {};
  public solana: Solana; // or Ethereum
  public sdk: MyDEXSDK;
  public config: MyDexConfig.RootConfig;
  private constructor() {
    this.config = MyDexConfig.config;
    this.txVersion = TxVersion.V0;
  }
  // Gets singleton instance
  public static getInstance(network: string): MyDex {
    if (!MyDex._instances) {
      MyDex._instances = {};
    }
    if (!MyDex._instances[network]) {
      const instance = new MyDex();
      await instance.init(network);
      MyDex._instances[network] = instance;
    }
    return MyDex._instances[network];
  }
  // Initializes instance
  private async init(network: string) {
    try {
      this.solana = await Solana.getInstance(network);
      this.sdk = await MyDEXSDK.load({
        connection: this.solana.connection,
        blockhashCommitment: 'confirmed',
      });
      logger.info('MyDEX initialized successfully');
    } catch (error) {
      logger.error('MyDEX initialization failed:', error);
      throw error;
    }
  }
}
步骤 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": {
    "slippagePct": {
      "type": "number",
      "description": "Maximum slippage percentage",
      "default": 1.0
    },
    "customParam": {
      "type": "number",
      "description": "Custom parameter",
    },
  },
  "required": ["slippagePct"]
}
默认配置¶
步骤 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 连接器必须满足以下测试标准:
- 代码覆盖率:最低 75% 覆盖率
- 单元测试:测试所有公共方法
- 集成测试:测试 API 端点
- 错误处理:测试失败场景
- 模拟数据:使用真实的测试数据
运行测试¶
# Run all tests
pnpm test
# Run tests for specific connector
pnpm test -- mydex
# Check coverage
pnpm test:coverage
代码质量标准¶
代码检查与格式化¶
网关使用 ESLint 和 Prettier 来保证代码质量:
TypeScript 最佳实践¶
- 强类型:使用明确的类型,避免使用 any
- 错误处理:实现正确的错误类
- 异步/等待:使用现代的异步模式
- 文档:为公共方法添加 JSDoc 注释
提交清单¶
在提交连接器之前:
- 所有测试通过且覆盖率超过 75%
- 代码通过代码检查和格式化检查
- 为所有支持的网络添加了配置文件
- API 端点符合模式规范
- 文档包含使用示例
- 错误处理覆盖边界情况
- 已使用真实负载进行性能测试
- 安全审查已完成
下一步¶
- 本地测试:使用你的连接器运行网关并测试所有操作
- 创建示例:添加展示连接器用法的示例脚本
- 提交 PR:向网关仓库创建拉取请求
- 治理提案:如需,提交新连接器提案
资源¶
- 网关 GitHub 仓库
- 网关 API 文档
- 连接器模式
- 示例连接器
- Discord 支持
