Building Gateway Connectors¶
Overview¶
This guide walks you through the process of building new Gateway connectors for decentralized exchanges (DEXs). Gateway connectors enable Hummingbot to interact with blockchain-based trading protocols through a standardized REST API interface.
Gateway supports three types of DEX connectors:
- Router: DEX aggregators that find optimal swap routes
- AMM: Traditional V2-style constant product pools
- CLMM: Concentrated liquidity market makers with custom price ranges
Prerequisites¶
Before building a Gateway connector, ensure you have:
Development Environment¶
- Node.js 18+ and pnpm
- TypeScript knowledge
- Understanding of the target blockchain and DEX protocol
Protocol Knowledge¶
- Familiarity with the DEX's smart contracts
- Understanding of the protocol's SDK or API
- Knowledge of liquidity pool mechanics
Gateway Setup¶
- Gateway development environment configured
- Ability to run and test Gateway locally
Connector Architecture¶
Gateway connectors follow a modular architecture:
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)
Implementation Steps¶
Step 1: Choose Connector Type¶
Determine which trading types your DEX supports:
Type | Use Case | Key Methods |
---|---|---|
Router | DEX aggregators, swap-only protocols | quote , trade , estimateGas |
AMM | V2-style pools with LP tokens | poolPrice , addLiquidity , removeLiquidity |
CLMM | Concentrated liquidity with ranges | openPosition , addLiquidity , collectFees |
Step 2: Create Connector Class¶
Create the main connector class with singleton pattern:
// 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;
}
}
Step 3: Implement Trading Methods¶
Based on your connector type, implement the required methods:
Router Methods¶
// 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 Methods¶
// 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 Methods¶
// 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;
}
Step 4: Create Route Handlers¶
Create route handler files for your supported operations:
// 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
});
Step 5: Add Configuration¶
Create configuration files for your connector:
Schema Definition¶
// 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"]
}
Default Configuration¶
# src/templates/connectors/mydex.yml
allowedSlippage: 1.0
gasLimitEstimate: 300000
ttl: 30
contractAddresses:
mainnet:
routerAddress: '0x...'
factoryAddress: '0x...'
testnet:
routerAddress: '0x...'
factoryAddress: '0x...'
Step 6: Register the Connector¶
Register your connector in the main connector routes:
// 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);
Step 7: Write Tests¶
Create comprehensive tests for your connector:
// 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
});
Adding Chain Support¶
Chain Support Status
Gateway is currently not accepting pull requests for new blockchain implementations. The framework currently supports: - EVM chains: Ethereum and EVM-compatible chains (Arbitrum, Optimism, Base, Polygon, BSC, Avalanche, etc.) - SVM chains: Solana and SVM-compatible chains
If your connector requires a chain built on either EVM or SVM architecture, you can proceed with the implementation below. For entirely new blockchain architectures, please check the GitHub repository for updates on when new chain support will be accepted.
If your connector requires a new blockchain:
Step 1: Create Chain Implementation¶
// 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
}
}
Step 2: Create Chain Routes¶
// 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);
});
Step 3: Add Chain Configuration¶
# 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'
Testing Requirements¶
All Gateway connectors must meet these testing standards:
- Code Coverage: Minimum 75% coverage
- Unit Tests: Test all public methods
- Integration Tests: Test API endpoints
- Error Handling: Test failure scenarios
- Mock Data: Use realistic test data
Running Tests¶
# Run all tests
pnpm test
# Run tests for specific connector
pnpm test -- mydex
# Check coverage
pnpm test:coverage
Code Quality Standards¶
Linting and Formatting¶
Gateway uses ESLint and Prettier for code quality:
TypeScript Best Practices¶
- Strong Typing: Use explicit types, avoid
any
- Error Handling: Implement proper error classes
- Async/Await: Use modern async patterns
- Documentation: Add JSDoc comments for public methods
Submission Checklist¶
Before submitting your connector:
- All tests pass with >75% coverage
- Code passes linting and formatting checks
- Configuration files added for all supported networks
- API endpoints follow schema specifications
- Documentation includes usage examples
- Error handling covers edge cases
- Performance tested with realistic loads
- Security review completed
Next Steps¶
- Test locally: Run Gateway with your connector and test all operations
- Create examples: Add sample scripts showing connector usage
- Submit PR: Create pull request to Gateway repository
- Governance proposal: Submit New Connector Proposal if required