Skip to content

Conversation

@FionnL
Copy link

@FionnL FionnL commented Oct 21, 2025

Closes #STRUCT-3512

https://smartcontract-it.atlassian.net/browse/STRUCT-3512

Description

Please also see the Jira ticket for the changes made.

Changes

  • API Migration: Updated from Mobula v1 firehose approach to v2 targeted subscriptions using asset_ids for more efficient WebSocket connections

  • Asset Mapping System: Added comprehensive includes.json with 211+ DeFi token mappings (EZETH, CBETH, LBTC, GHO, etc.) enabling symbol-to-asset-id resolution

  • Hardcoded Quote Currencies: Implemented direct support for 6 major crypto quote currencies (BTC, ETH, SOL, HYPE, S, BBSOL) without requiring includes.json entries

  • Framework Integration: Migrated from basic Adapter to PriceAdapter with CryptoPriceEndpoint to leverage EA framework v3 features and type safety

  • Critical Bug Fix: Resolved composite key reverse mapping issue using baseID-quoteID keys to prevent quote currency overwrites (e.g., EZETH/ETH vs EZETH/USD collision)

  • Comprehensive Testing: Added 18 integration tests covering includes.json mappings, hardcoded quotes, override functionality, direct asset ID usage, and error cases

  • Framework Update: Updated to @chainlink/external-adapter-framework v2.8.0 for CI compatibility and latest features

  • Enhanced Flexibility: Added support for request-level base/quote overrides and direct asset ID usage alongside symbol-based requests

  • Result: Transformed EA from symbol-based firehose to efficient, targeted asset-id subscriptions.

Steps to Test

Run Integration & Unit Tests

Run all tests for mobula-state adapter:
yarn test mobula-state

Run only integration tests:
yarn test:integration mobula-state

Run only unit tests:
yarn test:unit mobula-state

Manual Testing - Start EA

cd packages/sources/mobula-state
API_KEY=your_api_key yarn start

Sample EA Request Payloads

1. Basic Includes.json Mappings

EZETH/USD (includes.json mapping):

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "EZETH", "quote": "USD"}}'

CBETH/ETH (includes.json + hardcoded ETH quote):

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "CBETH", "quote": "ETH"}}'

LBTC/BTC (includes.json + hardcoded BTC quote):

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "LBTC", "quote": "BTC"}}'

2. Hardcoded Quote Currencies

Test all supported hardcoded quotes:

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "EZETH", "quote": "ETH"}}'
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "LBTC", "quote": "BTC"}}'
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "MSOL", "quote": "SOL"}}'

3. Override Functionality

Base override - use custom symbol mapped to EZETH asset ID:

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "MYCOIN", "quote": "USD", "overrides": {"mobula-state": {"MYCOIN": "102478632"}}}}'

Dual override - both base and quote:

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "MYEZETH", "quote": "MYETH", "overrides": {"mobula-state": {"MYEZETH": "102478632", "MYETH": "100004304"}}}}'

4. Direct Asset ID Usage

Direct asset IDs (no symbol resolution needed):

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "102478632", "quote": "USD"}}'
curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "102478632", "quote": "100004304"}}'

5. Funding Rate Endpoint

curl -X POST http://localhost:8080/ -H "Content-Type: application/json" -d '{"data": {"base": "BTC", "exchange": "binance", "endpoint": "funding-rate"}}'

Expected Results

  • EZETH/USD: ~$4,200 (current USD price)
  • EZETH/ETH: ~1.06 (ETH ratio, NOT USD price)
  • LBTC/BTC: ~0.998 (BTC ratio)
  • Override requests: Same prices as direct symbol requests
  • Direct asset IDs: Same prices as symbol requests
  • Error cases: 400/504 errors for unmapped symbols

Key Validations

  • Composite Key Fix: Verify EZETH/ETH returns ~1.06 (ETH ratio) not ~4200 (USD price)
  • Override Priority: Overrides take precedence over includes.json
  • Asset ID Resolution: Check meta.metrics.feedId shows correct asset IDs
  • Quote Currency Support: USD, BTC, ETH, SOL, HYPE, S, BBSOL all work
  • Comprehensive Coverage: 211+ DeFi tokens supported via includes.json

Quality Assurance

  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant infra-k8s configuration file.
  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant adapter-secrets configuration file or update the soak testing blacklist.
  • If a new adapter was made, or a new endpoint was added, update the test-payload.json file with relevant requests.
  • The branch naming follows git flow (feature/x, chore/x, release/x, hotfix/x, fix/x) or is created from Jira.
  • This is related to a maximum of one Jira story or GitHub issue.
  • Types are safe (avoid TypeScript/TSLint features like any and disable, instead use more specific types).
  • All code changes have 100% unit and integration test coverage. If testing is not applicable or too difficult to justify doing, the reasoning should be documented explicitly in the PR.

FionnL added 11 commits October 15, 2025 22:59
- Replace symbol-based firehose with targeted asset_id subscriptions
- Add includes.json for symbol-to-asset-ID mapping (EZETH→102478632, GHO→2921, ETH→100004304)
- Switch to PriceAdapter + CryptoPriceEndpoint for proper includes support
- Implement WebsocketReverseMappingTransport for parameter matching
- Support multi-quote pairs (USD, ETH)
- Real-time WebSocket streaming from wss://production-feed.mobula.io

Tested pairs:
- EZETH/USD ✅
- GHO/USD ✅
- ETH/USD ✅
- EZETH/ETH ✅
- Fixed issue where multiple quote currencies for same base asset overwrote each other
- Changed from baseID-only key to baseID-quoteID composite key
- Prevents EZETH/ETH returning USD price instead of ETH ratio
- Now EZETH/ETH correctly returns ~1.06 instead of ~4,233
- Added 207 new symbol mappings to includes.json (211 total entries)
- Added BBSOL (Bybit Staked SOL) hardcoded quote currency: 102484775
- Supports major DeFi pairs: CBETH/ETH, LBTC/BTC, SOLVBTC/BTC, OS/S, etc.
- Added 7 BBSOL pairs: VSOL/BBSOL, MSOL/BBSOL, JITOSOL/BBSOL, etc.
- Comprehensive coverage of liquid staking tokens, Bitcoin variants, Solana ecosystem
- Ready for production with 6 quote currencies: USD, BTC, ETH, SOL, HYPE, S, BBSOL
…pport

- Updated integration tests to validate v2 functionality instead of old ETH/USD pairs
- Added mock WebSocket responses with baseID/quoteID for composite key testing
- Added test cases for includes.json mapping (EZETH, CBETH, LBTC, GHO)
- Added test cases for hardcoded quote currencies (BTC, ETH, SOL validation)
- Added override functionality tests (base/quote overrides with custom asset IDs)
- Added direct asset ID tests (bypass symbol resolution entirely)
- Updated test-payload.json with realistic v2 examples
- Comprehensive coverage: static mappings, dynamic overrides, direct IDs
- Tests all 3 asset resolution methods + 6 quote currencies + error handling
…as requirement

Clarifies that 'market' must be in quote aliases due to TypeScript type constraints
in CryptoPriceEndpoint, not runtime validation. This is a compile-time requirement
from the framework.
- Fix timestamp values in test fixtures (use 4040ms instead of Sep 2024 timestamps)
- Update Jest snapshots to match v2 response format changes
- Remove debug logging for clean production code
- All 18 integration tests now pass with proper snapshot validation

Fixes CI job 53290359217 failures
- Update all 18 integration test snapshots for v2 API format
- Fix timestamp handling in test fixtures using Date.now() + offset
- All snapshot tests now pass successfully
- Resolve job 53315497853 failures

Tests: 18 passed, 7 snapshots updated, 11 passed
- Use timestamp 1514764861500 that meets framework minimum requirements
- Update all 18 integration test snapshots for consistent CI/local behavior
- All tests pass: 18 passed, 7 snapshots updated, 11 passed
- Resolves job 53316053810 snapshot failures

The timestamp warnings are expected due to FakeTimer test environment but don't affect test success.
- Update @chainlink/external-adapter-framework from 2.7.0 → 2.8.0 to match CI
- Update 9 Jest snapshots to exclude meta field when METRICS_ENABLED=false
- All 18 integration tests now pass and match CI environment exactly
- Resolves job 53316053810 snapshot failures

Framework v2.8.0 + METRICS_ENABLED=false removes meta field from test responses,
matching the CI environment behavior and eliminating snapshot mismatches.
@changeset-bot
Copy link

changeset-bot bot commented Oct 21, 2025

🦋 Changeset detected

Latest commit: 814dfa3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@chainlink/mobula-state-adapter Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@FionnL FionnL requested a review from Copilot October 21, 2025 21:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates the Mobula State adapter from v1 firehose WebSocket approach to v2 targeted subscriptions using asset IDs. The migration introduces a comprehensive symbol-to-asset-ID mapping system via includes.json with 211+ DeFi token mappings, hardcoded support for 6 major crypto quote currencies, and fixes a critical composite key reverse mapping bug that prevented correct quote currency handling.

Key changes:

  • Replaced firehose subscription with targeted asset_id-based WebSocket subscriptions
  • Added includes.json with mappings for 211+ DeFi tokens to their Mobula asset IDs
  • Implemented composite key (baseID-quoteID) for reverse mapping to prevent quote currency overwrites
  • Migrated from basic Adapter to PriceAdapter with framework v3 type safety
  • Added 18 comprehensive integration tests covering all mapping scenarios

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/sources/mobula-state/src/transport/price.ts Core transport migration to WebsocketReverseMappingTransport with asset ID resolution logic, hardcoded quote currencies, and composite key reverse mapping
packages/sources/mobula-state/src/endpoint/price.ts Updated to CryptoPriceEndpoint with modified input parameter aliases for framework compatibility
packages/sources/mobula-state/src/index.ts Changed from Adapter to PriceAdapter and added includes.json import
packages/sources/mobula-state/src/config/includes.json New comprehensive mapping file with 211+ DeFi token symbol-to-asset-ID entries
packages/sources/mobula-state/test/integration/fixtures.ts Expanded mock WebSocket responses to cover all test scenarios including overrides and direct asset IDs
packages/sources/mobula-state/test/integration/adapter.test.ts Added 18 integration tests for includes.json mappings, hardcoded quotes, overrides, and direct asset ID usage
packages/sources/mobula-state/test/integration/snapshots/adapter.test.ts.snap Updated snapshots with expected results for all new test cases
packages/sources/mobula-state/test-payload.json Updated test payloads to demonstrate new capabilities
.changeset/large-cycles-admire.md Changeset documenting the major version bump

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

… safety

1. Fix SOL asset ID inconsistency:
   - Correct SOL mapping from 102484775 → 100010811 in includes.json
   - 102484775 is BBSOL (Bybit Staked SOL), not SOL (Solana)
   - Ensures consistency between base currency and quote currency mappings

2. Improve TypeScript type safety:
   - Replace 'any' type with proper MobulaSubscribeMessage interface
   - Define structure for WebSocket subscription messages
   - Maintains type safety throughout transport logic

All hardcoded quote currencies now match includes.json mappings:
✅ BTC: 100001656, ETH: 100004304, SOL: 100010811
@FionnL FionnL force-pushed the feature/update-mobula-ea-v2 branch from c880948 to 8ffbbf6 Compare October 21, 2025 21:55
@FionnL FionnL requested a review from Copilot October 21, 2025 21:55
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant