v0.2.11-pre.004
This commit is contained in:
402
deltas/0.2.11/pre.004.md
Normal file
402
deltas/0.2.11/pre.004.md
Normal file
@@ -0,0 +1,402 @@
|
||||
<!-- 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
|
||||
```
|
||||
Reference in New Issue
Block a user