Files
khadhroony-solana-project/deltas/0.2.11/pre.004.md
2026-08-25 21:47:47 +02:00

403 lines
10 KiB
Markdown

<!-- file: deltas/0.2.11/pre.004.md -->
<!-- version: 1 -->
# Delta `0.2.11-pre.004` — CoinGecko, CoinMarketCap et CoinPaprika
## 1. Base requise
Cette tranche s'applique exclusivement après :
```text
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 :
```text
0.2.11-pre.3.fix.1
```
La version Cargo de sortie est :
```text
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 :
```text
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 :
```text
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_*` :
```text
CoinGecko
CoinMarketCap
CoinPaprika
```
Le contrat reste :
```text
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 :
```text
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` :
```text
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 :
```text
MarketPriceCoinGeckoProvider
MarketPriceCoinGeckoSettings
MarketPriceCoinGeckoAccessMode
```
Modes V1 :
```text
Keyless
Demo
```
La configuration respecte les capacités réelles :
```text
Keyless -> aucun credential accepté
Demo activé -> API key obligatoire
Demo désactivé -> credential optionnel pour permettre une config inactive
```
Le request contract est fixe :
```text
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 :
```text
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 :
```text
MarketPriceCoinMarketCapProvider
MarketPriceCoinMarketCapSettings
MarketPriceCoinMarketCapAccessMode
```
Modes V1 :
```text
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 :
```text
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 :
```text
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 :
```text
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 :
```text
MarketPriceCoinPaprikaProvider
MarketPriceCoinPaprikaSettings
```
Il est keyless uniquement : aucun faux champ credential n'est introduit.
Request fixe :
```text
GET https://api.coinpaprika.com/v1/tickers/sol-solana
quotes=USD
```
Le descriptor conserve :
```text
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 :
```text
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 :
```text
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 :
```text
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
```text
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
```text
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
```text
aucun
```
## 15. Fichiers volontairement inchangés
```text
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
```text
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é
```bash
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é
```text
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
```