#!/usr/bin/env node /** * Minimal tests for CCIP Relay Service. * Run with: npm test * Does not start the relay or connect to RPC. * For full tests, set PRIVATE_KEY (or RELAYER_PRIVATE_KEY) and run; config load will then validate. */ import { existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { ethers } from 'ethers'; import { MessageQueue } from './src/MessageQueue.js'; import { RelayService } from './src/RelayService.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); function assert(condition, message) { if (!condition) { console.error('FAIL:', message); process.exit(1); } } console.log('Relay service structure tests...'); assert(existsSync(join(__dirname, 'src', 'config.js')), 'src/config.js should exist'); assert(existsSync(join(__dirname, 'src', 'RelayService.js')), 'src/RelayService.js should exist'); assert(existsSync(join(__dirname, 'src', 'healthServer.js')), 'src/healthServer.js should exist'); assert(existsSync(join(__dirname, 'index.js')), 'index.js should exist'); const logger = { info() {}, warn() {}, error() {}, debug() {}, }; const relay = new RelayService({ sourceChain: { name: 'Chain 138', chainId: 138, rpcUrl: 'http://example.invalid', routerAddress: '0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817', bridgeAddress: '0x152ed3e9912161b76bdfd368d0c84b7c31c10de7', }, destinationChain: { name: 'Ethereum Mainnet', chainId: 1, rpcUrl: 'http://example.invalid', relayRouterAddress: '0x416564Ab73Ad5710855E98dC7bC7Bff7387285BA', relayBridgeAddress: '0x2bF74583206A49Be07E0E8A94197C12987AbD7B5', relayBridgeAllowlist: ['0x2bF74583206A49Be07E0E8A94197C12987AbD7B5'], chainSelector: 5009297550715157269n, deliveryMode: 'router', }, tokenMapping: {}, sourceChainSelector: 138n, relayer: { privateKey: '', address: '' }, monitoring: { startBlock: 'latest', pollInterval: 5000, confirmationBlocks: 1, finalityDelayBlocks: 2, replayWindowBlocks: 32, }, retry: { maxRetries: 3, retryDelay: 5000 }, skipMessageIds: new Set(), }, logger); const cwReceiver = ethers.AbiCoder.defaultAbiCoder().encode( ['address'], ['0x2bF74583206A49Be07E0E8A94197C12987AbD7B5'] ); const scoped = relay.evaluateMessageScope({ sender: '0x152ed3e9912161b76bdfd368d0c84b7c31c10de7', receiver: cwReceiver, }); assert(scoped.inScope === true, 'cW message should match the configured worker scope'); assert( scoped.targetBridge === '0x2bF74583206A49Be07E0E8A94197C12987AbD7B5', 'cW target bridge should decode from receiver bytes' ); const wethReceiver = ethers.AbiCoder.defaultAbiCoder().encode( ['address'], ['0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939'] ); const rejected = relay.evaluateMessageScope({ sender: '0xcacfd227A040002e49e2e01626363071324f820a', receiver: wethReceiver, }); assert(rejected.inScope === false, 'WETH message should be rejected by the cW worker scope'); const queue = new MessageQueue(logger); const queuedMessage = { messageId: '0xtest-message', sender: '0x152ed3e9912161b76bdfd368d0c84b7c31c10de7', receiver: cwReceiver, data: '0x', tokenAmounts: [] }; await queue.add(queuedMessage); const inFlightMessage = await queue.getNext(); assert(inFlightMessage?.messageId === queuedMessage.messageId, 'getNext should return the queued message'); await queue.retry(queuedMessage.messageId); const retriedMessage = await queue.getNext(); assert(retriedMessage?.messageId === queuedMessage.messageId, 'retry should requeue the original message payload'); console.log('OK: relay service structure valid'); process.exit(0);