10 KiB
Delta 0.2.11-pre.004 — CoinGecko, CoinMarketCap et CoinPaprika
1. Base requise
Cette tranche s'applique exclusivement après :
v0.2.10
+ 0.2.11-pre.001
+ 0.2.11-pre.001-fix.001
+ 0.2.11-pre.002
+ 0.2.11-pre.002-fix.001
+ 0.2.11-pre.002-fix.002
+ 0.2.11-pre.002-fix.003
+ 0.2.11-pre.003
+ 0.2.11-pre.003-fix.001
La version Cargo attendue à l'entrée est :
0.2.11-pre.3.fix.1
La version Cargo de sortie est :
0.2.11-pre.4
2. Gate d'entrée et reprise volontaire de RUST-FMT-108
L'opérateur a validé 0.2.11-pre.003-fix.001 le 2026-08-25 avec :
cargo fmt --all exécuté
python3 scripts/audit_rust_workspace_rules.py 1 violation RUST-FMT-108 uniquement
python3 scripts/audit_markdown_tables.py clean, 117 tables / 107 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-offchain-transport-lib PASS, 22 unit + 2 boundary + 2 public API
RUST-FMT-108 provenait uniquement du staging #[cfg(test)] des modules http_* imposé en pre.003-fix.001 tant qu'aucun consumer de production n'existait. L'opérateur a explicitement demandé de ne pas créer un fix artificiel supplémentaire puisque pre.004 est précisément la tranche qui active ces modules avec leurs premiers consumers runtime.
pre.004 retire donc le staging crate-root de :
http_admission
http_client
http_settings
La cause structurelle de RUST-FMT-108 disparaît sans exception ni allow.
3. Objectif
Implémenter les trois premiers providers SOL/USD de la famille market_price_* :
CoinGecko
CoinMarketCap
CoinPaprika
Le contrat reste :
SOL/USD uniquement
reqwest direct
aucun SDK provider
endpoints officiels fixes
DTOs wire privés
prix public exact, aucun canon f64
credentials absents des Debug/logs/errors
aucun Config dans Off-chain Transport
aucun registry global ni refresh multiple avant leurs tranches dédiées
4. Pont HTTP -> Market Price
market_price_adapter.rs matérialise uniquement les mécanismes réellement partagés par les adapters de prix :
admission non bloquante
capture des timestamps KSP
GET JSON borné via le client HTTP commun
feedback 429 / Retry-After vers le cooldown local
conversion RFC3339 ou Unix seconds vers MarketPriceTimestamp
erreurs de schéma provider sûres sans copie du payload distant
construction du runtime HTTP + admission depuis le descriptor provider
Les primitives http_* restent crate-wide et crate-private afin d'être réutilisables plus tard par d'autres familles telles que swap_quote_*, sans devenir un client HTTP générique public pour les consumers.
5. Credential market-price partagé
market_price_api_key.rs ajoute un holder privé MarketPriceApiKey :
longueur maximale 512 octets
valeur vide rejetée
whitespace externe rejeté
caractères de contrôle rejetés
Debug toujours redacted
aucun logging de la valeur
Ce holder ne lit aucun environnement. Config reste l'unique futur propriétaire de la résolution des secrets.
6. CoinGecko
Le provider public est :
MarketPriceCoinGeckoProvider
MarketPriceCoinGeckoSettings
MarketPriceCoinGeckoAccessMode
Modes V1 :
Keyless
Demo
La configuration respecte les capacités réelles :
Keyless -> aucun credential accepté
Demo activé -> API key obligatoire
Demo désactivé -> credential optionnel pour permettre une config inactive
Le request contract est fixe :
GET https://api.coingecko.com/api/v3/simple/price
ids=solana
vs_currencies=usd
include_last_updated_at=true
header x-cg-demo-api-key uniquement en mode Demo
Le descriptor conserve :
Keyless -> auth none, rate limit dynamique scope IP
Demo -> API key requise, 100 requêtes / 60 s scope account, 10k credits / mois
semantics -> AggregatedMarket
La réponse exige solana.usd et solana.last_updated_at. Le prix est lu via RawValue puis MarketPriceDecimal; le timestamp provider Unix est conservé.
7. CoinMarketCap
Le provider public est :
MarketPriceCoinMarketCapProvider
MarketPriceCoinMarketCapSettings
MarketPriceCoinMarketCapAccessMode
Modes V1 :
Keyless
Basic
La configuration suit la même discipline : keyless refuse un credential et Basic activé exige une API key.
Le réaudit pre.004 retient la surface Simple Price V2 actuelle et n'introduit pas la surface V1 deprecated.
Requests fixes :
Keyless -> GET https://pro-api.coinmarketcap.com/public-api/v2/simple/price
Basic -> GET https://pro-api.coinmarketcap.com/v2/simple/price
ids=5426
convert=USD
include_last_updated=true
header x-cmc_pro_api_key uniquement en mode Basic
Le descriptor conserve :
Keyless -> auth none, rate limit dynamique scope IP
Basic -> API key requise, 50 requêtes / 60 s scope account, 15k credits / mois
semantics -> AggregatedMarket
Le parser vérifie :
status.error_code = 0
un seul item SOL id 5426
symbol = SOL
une seule quote USD
price exact via RawValue
last_updated RFC3339 valide
La provenance normalisée conserve explicitement la génération v2.
8. CoinPaprika
Le provider public est :
MarketPriceCoinPaprikaProvider
MarketPriceCoinPaprikaSettings
Il est keyless uniquement : aucun faux champ credential n'est introduit.
Request fixe :
GET https://api.coinpaprika.com/v1/tickers/sol-solana
quotes=USD
Le descriptor conserve :
auth none
10 requêtes / seconde scope IP
20k requests / mois
semantics -> AggregatedMarket
Le parser vérifie id = sol-solana, symbol = SOL, lit quotes.USD.price exactement via RawValue et conserve last_updated RFC3339.
9. Numeric safety
MarketPriceDecimal gagne un parser crate-private de serde_json::value::RawValue.
Il accepte :
nombre JSON exact
ou chaîne JSON contenant un décimal valide
Il ne passe jamais par f64. Les tests couvrent notamment une mantisse dont les derniers chiffres seraient perdus par une conversion flottante intermédiaire.
10. Availability et rate limiting dans cette tranche
Chaque provider possède son HttpAdmissionController local construit depuis son descriptor.
Le chemin runtime :
fetch_sol_usd
-> enabled check
-> try_admit non bloquant
-> request timestamp
-> GET provider
-> si 429 : Retry-After enregistré dans le cooldown
-> receive timestamp
-> parser typed provider
-> MarketPriceObservation
Un provider localement non admissible retourne http_admission_deferred avec un délai safe ; aucun sleep n'est introduit dans l'adapter.
Le registry global et la projection générique d'availability restent réservés à pre.007, et l'orchestration multiple à pre.008.
11. Tests ajoutés ou étendus
Les tests déterministes couvrent :
modes keyless/keyed et credentials requis/interdits
Debug settings sans secret
headers sensibles présents uniquement dans le bon mode
URLs et query fixes
capabilities descriptors et quotas
réponses SOL/USD valides
prix JSON exact sans f64
provider timestamps
identités provider incorrectes rejetées
schema/provider status incohérents rejetés
endpoint CMC V2 exact
absence de SDK provider
absence de Config et tracing direct
HTTP production actif mais non exporté publiquement
Aucun smoke réseau n'est ajouté dans cette tranche.
12. Fichiers ajoutés
crates/ksp-offchain-transport-lib/src/market_price_adapter.rs
crates/ksp-offchain-transport-lib/src/market_price_api_key.rs
crates/ksp-offchain-transport-lib/src/market_price_coingecko.rs
crates/ksp-offchain-transport-lib/src/market_price_coinmarketcap.rs
crates/ksp-offchain-transport-lib/src/market_price_coinpaprika.rs
crates/ksp-offchain-transport-lib/unit_tests/market_price_coingecko.rs
crates/ksp-offchain-transport-lib/unit_tests/market_price_coinmarketcap.rs
crates/ksp-offchain-transport-lib/unit_tests/market_price_coinpaprika.rs
deltas/0.2.11/pre.004.md
13. Fichiers modifiés
Cargo.toml
crates/ksp-offchain-transport-lib/Cargo.toml
crates/ksp-offchain-transport-lib/src/error.rs
crates/ksp-offchain-transport-lib/src/http_admission.rs
crates/ksp-offchain-transport-lib/src/http_client.rs
crates/ksp-offchain-transport-lib/src/http_settings.rs
crates/ksp-offchain-transport-lib/src/lib.rs
crates/ksp-offchain-transport-lib/src/market_price_decimal.rs
crates/ksp-offchain-transport-lib/tests/dependency_boundary.rs
crates/ksp-offchain-transport-lib/tests/public_api.rs
crates/ksp-offchain-transport-lib/unit_tests/http_admission.rs
crates/ksp-offchain-transport-lib/unit_tests/market_price_decimal.rs
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
14. Fichiers supprimés
aucun
15. Fichiers volontairement inchangés
CHANGELOG.md
ROADMAP.md
README.md
.env.example
config/**
crates/ksp-config-lib/**
crates/ksp-onchain-transport-lib/**
prompts/**
ROADMAP.md et CHANGELOG.md restent hors de cette prerelease conformément à leur ownership de release.
16. Validations exécutées dans le sandbox
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
clean, 118 tables / 108 files
contrôle lignes Rust > 160 sur ksp-offchain-transport-lib
PASS, 0 ligne
Le sandbox de génération ne possède pas Cargo/Rust. cargo fmt, cargo check, cargo clippy et les tests Cargo ne sont donc pas déclarés exécutés ici.
17. Gate opérateur demandé
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-offchain-transport-lib
cargo test --workspace
Les smokes live restent hors de pre.004.
18. Scope non avancé
Kraken
Coinbase Exchange
Jupiter
DexScreener
Birdeye
registry provider global
availability service global
refresh single/multiple générique
Config std.offchain_transport
.env.example secrets provider
ksp-app-solprices-desk
fallback automatique
consensus de prix
SOL/EUR
prix SPL générique