v0.2.11-pre.011
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 281
|
||||
# version: 282
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.11-pre.10.fix.1"
|
||||
version = "0.2.11-pre.11"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// file: crates/ksp-offchain-transport-lib/tests/market_price_live_smoke.rs
|
||||
// version: 1
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//! Opt-in live SOL/USD smokes for the V1 Off-chain Transport market-price providers.
|
||||
|
||||
use std::io::IsTerminal; // rust-rules: trait-import
|
||||
|
||||
const DEXSCREENER_SOL_USDC_ORCA_PAIR: &str = "7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm";
|
||||
|
||||
fn smoke_error(message: &'static str) -> ksp_core_lib::Error {
|
||||
return ksp_core_lib::Error::new(ksp_offchain_transport_lib::ERROR_CODE_MARKET_PRICE_REFRESH_INVALID, message);
|
||||
}
|
||||
|
||||
fn keyless_setups() -> ksp_core_lib::Result<std::vec::Vec<ksp_offchain_transport_lib::MarketPriceProviderSetup>> {
|
||||
let mut setups = std::vec::Vec::with_capacity(7);
|
||||
let coinbase = match ksp_offchain_transport_lib::MarketPriceCoinbaseExchangeSettings::new(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinbaseExchange(coinbase));
|
||||
let coingecko = match ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::keyless(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinGecko(coingecko));
|
||||
let coinmarketcap = match ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::keyless(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinMarketCap(coinmarketcap));
|
||||
let coinpaprika = match ksp_offchain_transport_lib::MarketPriceCoinPaprikaSettings::new(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinPaprika(coinpaprika));
|
||||
let dexscreener =
|
||||
match ksp_offchain_transport_lib::MarketPriceDexScreenerSettings::new(true, std::option::Option::Some(DEXSCREENER_SOL_USDC_ORCA_PAIR.to_owned())) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::DexScreener(dexscreener));
|
||||
let jupiter = match ksp_offchain_transport_lib::MarketPriceJupiterSettings::keyless(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::Jupiter(jupiter));
|
||||
let kraken = match ksp_offchain_transport_lib::MarketPriceKrakenSettings::new(true) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::Kraken(kraken));
|
||||
return std::result::Result::Ok(setups);
|
||||
}
|
||||
|
||||
fn read_secret_line(label: &'static str) -> ksp_core_lib::Result<std::string::String> {
|
||||
let mut secret = std::string::String::new();
|
||||
match std::io::stdin().read_line(&mut secret) {
|
||||
std::result::Result::Ok(_) => {},
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(smoke_error("keyed market-price smoke stdin is unreadable")),
|
||||
}
|
||||
let secret = secret.trim().to_owned();
|
||||
if secret.is_empty() {
|
||||
return std::result::Result::Err(smoke_error(label));
|
||||
}
|
||||
return std::result::Result::Ok(secret);
|
||||
}
|
||||
|
||||
fn keyed_setups() -> ksp_core_lib::Result<std::vec::Vec<ksp_offchain_transport_lib::MarketPriceProviderSetup>> {
|
||||
if std::io::stdin().is_terminal() {
|
||||
return std::result::Result::Err(smoke_error("pipe four provider API keys to the keyed market-price smoke on stdin"));
|
||||
}
|
||||
let birdeye_key = match read_secret_line("Birdeye API key from smoke stdin must not be empty") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let coingecko_key = match read_secret_line("CoinGecko Demo API key from smoke stdin must not be empty") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let coinmarketcap_key = match read_secret_line("CoinMarketCap Basic API key from smoke stdin must not be empty") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let jupiter_key = match read_secret_line("Jupiter Free API key from smoke stdin must not be empty") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut setups = std::vec::Vec::with_capacity(4);
|
||||
let birdeye = match ksp_offchain_transport_lib::MarketPriceBirdeyeSettings::new(true, std::option::Option::Some(birdeye_key)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::Birdeye(birdeye));
|
||||
let coingecko = match ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::demo(true, std::option::Option::Some(coingecko_key)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinGecko(coingecko));
|
||||
let coinmarketcap = match ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::basic(true, std::option::Option::Some(coinmarketcap_key)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinMarketCap(coinmarketcap));
|
||||
let jupiter = match ksp_offchain_transport_lib::MarketPriceJupiterSettings::free(true, std::option::Option::Some(jupiter_key)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
setups.push(ksp_offchain_transport_lib::MarketPriceProviderSetup::Jupiter(jupiter));
|
||||
return std::result::Result::Ok(setups);
|
||||
}
|
||||
|
||||
fn assert_live_outcomes(
|
||||
registry: &ksp_offchain_transport_lib::MarketPriceProviderRegistry,
|
||||
outcomes: &[ksp_offchain_transport_lib::MarketPriceRefreshOutcome],
|
||||
) {
|
||||
let mut failures = std::vec::Vec::new();
|
||||
for outcome in outcomes {
|
||||
let provider_id = outcome.provider_id();
|
||||
let observation = match outcome.observation() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
failures.push(std::format!("{}:{:?}", provider_id.as_str(), outcome.state().availability()));
|
||||
continue;
|
||||
},
|
||||
};
|
||||
assert_eq!(observation.provider_id(), provider_id);
|
||||
assert_eq!(observation.pair(), ksp_offchain_transport_lib::MarketPricePair::SolUsd);
|
||||
assert!(observation.price().coefficient() > 0);
|
||||
assert!(observation.received_at() >= observation.request_started_at());
|
||||
let descriptor = match registry.descriptor(provider_id) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
failures.push(std::format!("{}:missing_registry_descriptor", provider_id.as_str()));
|
||||
continue;
|
||||
},
|
||||
};
|
||||
assert_eq!(observation.semantics(), descriptor.semantics());
|
||||
}
|
||||
assert!(failures.is_empty(), "live market-price providers did not all refresh successfully after every provider was attempted: {failures:?}");
|
||||
return;
|
||||
}
|
||||
|
||||
async fn run_live_smoke(setups: std::vec::Vec<ksp_offchain_transport_lib::MarketPriceProviderSetup>) -> ksp_core_lib::Result<()> {
|
||||
let service = match ksp_offchain_transport_lib::MarketPriceService::new(setups) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let registry = service.registry();
|
||||
let outcomes = match service.refresh_all().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
assert_eq!(outcomes.len(), registry.len());
|
||||
assert_live_outcomes(®istry, outcomes.as_slice());
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live SOL/USD smoke for seven keyless V1 providers; performs external HTTPS requests"]
|
||||
async fn keyless_market_price_providers_live_smoke() -> ksp_core_lib::Result<()> {
|
||||
let setups = match keyless_setups() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return run_live_smoke(setups).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live SOL/USD smoke for four free keyed V1 modes; reads API keys from stdin in Birdeye/CoinGecko/CoinMarketCap/Jupiter order"]
|
||||
async fn keyed_market_price_providers_live_smoke() -> ksp_core_lib::Result<()> {
|
||||
let setups = match keyed_setups() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return run_live_smoke(setups).await;
|
||||
}
|
||||
192
deltas/0.2.11/pre.011.md
Normal file
192
deltas/0.2.11/pre.011.md
Normal file
@@ -0,0 +1,192 @@
|
||||
<!-- file: deltas/0.2.11/pre.011.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.2.11-pre.011` — Gate technique et live final
|
||||
|
||||
## 1. Base requise
|
||||
|
||||
Cette tranche s'applique exclusivement après `0.2.11-pre.010-fix.001`, dont le gate opérateur complet est PASS.
|
||||
|
||||
Version Cargo attendue à l'entrée :
|
||||
|
||||
```text
|
||||
0.2.11-pre.10.fix.1
|
||||
```
|
||||
|
||||
Version Cargo de sortie :
|
||||
|
||||
```text
|
||||
0.2.11-pre.11
|
||||
```
|
||||
|
||||
## 2. Mission
|
||||
|
||||
`pre.011` ne développe plus de fonctionnalité Off-chain Transport. Elle possède uniquement le gate technique/live final avant la réconciliation documentaire de `pre.012` :
|
||||
|
||||
```text
|
||||
réaudit officiel des huit providers V1
|
||||
harness live keyless durable
|
||||
harness live keyed durable
|
||||
validation de non-court-circuit du batch
|
||||
vérification finale dependency graph / absence SDK provider
|
||||
rejeu des gates workspace
|
||||
```
|
||||
|
||||
Aucun endpoint, DTO wire, parser, rate-limit, Config ou comportement de `MarketPriceService` n'est modifié.
|
||||
|
||||
## 3. Réaudit provider du `2026-08-26`
|
||||
|
||||
Les contrats codés restent compatibles avec la documentation provider courante :
|
||||
|
||||
```text
|
||||
CoinGecko public keyless + Demo ; Simple Price + last_updated_at ; Demo 100/min
|
||||
CoinMarketCap Simple Price V2 ; /public-api keyless ; Basic gratuit disponible
|
||||
CoinPaprika 10 requêtes/s/IP ; Free 20k/mois ; données pouvant dater jusqu'à 10 min
|
||||
Kraken Ticker public SOLUSD ; cadence sûre 1 appel/s ou moins
|
||||
Coinbase Exchange /products/SOL-USD/ticker ; 10 requêtes/s/IP, burst 15
|
||||
Jupiter Price V3 api.jup.ag ; keyless/Free ; usdPrice + blockId
|
||||
Birdeye Standard ; 1 RPS ; 30 000 CU ; Price Single 3 CU
|
||||
DexScreener direct pair endpoint ; 300 requêtes/min ; aucune discovery KSP
|
||||
```
|
||||
|
||||
La documentation CoinPaprika est contradictoire sur la fraîcheur Free : la référence ticker mentionne environ 5 minutes en moyenne alors que Plans/Pricing annonce jusqu’à 10 minutes. Cela ne modifie pas KSP : aucune cadence provider n'est présentée comme freshness authoritative et aucun scheduler V1 n'est dérivé de cette information.
|
||||
|
||||
## 4. Smoke keyless
|
||||
|
||||
Nouveau test ignoré :
|
||||
|
||||
```text
|
||||
crates/ksp-offchain-transport-lib/tests/market_price_live_smoke.rs
|
||||
keyless_market_price_providers_live_smoke
|
||||
```
|
||||
|
||||
Il couvre en une seule exécution séquentielle :
|
||||
|
||||
```text
|
||||
Coinbase Exchange
|
||||
CoinGecko keyless
|
||||
CoinMarketCap keyless
|
||||
CoinPaprika
|
||||
DexScreener
|
||||
Jupiter keyless
|
||||
Kraken
|
||||
```
|
||||
|
||||
DexScreener utilise exclusivement la paire publique explicite Orca SOL/USDC :
|
||||
|
||||
```text
|
||||
7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm
|
||||
```
|
||||
|
||||
Cette paire a été revalidée au `2026-08-26` avec WSOL comme base. Elle n'est ni une configuration runtime par défaut, ni une découverte de pool.
|
||||
|
||||
Le smoke valide uniquement des invariants non volatils :
|
||||
|
||||
```text
|
||||
une observation produite pour chaque provider
|
||||
provider_id identique à l'outcome
|
||||
MarketPricePair::SolUsd
|
||||
prix exact positif déjà validé par MarketPriceDecimal
|
||||
received_at >= request_started_at
|
||||
sémantique identique au descriptor du registry
|
||||
```
|
||||
|
||||
Aucun prix exact n'est figé.
|
||||
|
||||
Les sept outcomes sont collectés avant l'assertion finale. Un provider externe en défaut ne peut donc pas empêcher les providers suivants d'être tentés ; le smoke échoue ensuite en listant seulement les IDs et availability génériques en échec.
|
||||
|
||||
## 5. Smoke keyed
|
||||
|
||||
Second test ignoré :
|
||||
|
||||
```text
|
||||
keyed_market_price_providers_live_smoke
|
||||
```
|
||||
|
||||
Il couvre :
|
||||
|
||||
```text
|
||||
Birdeye Standard
|
||||
CoinGecko Demo
|
||||
CoinMarketCap Basic
|
||||
Jupiter Free
|
||||
```
|
||||
|
||||
Les quatre API keys sont lues depuis `stdin` dans cet ordre. Elles ne sont jamais des arguments CLI, ne sont jamais imprimées et ne sont jamais lues directement depuis l'environnement par `ksp-offchain-transport-lib`.
|
||||
|
||||
La provenance `KSP_SECRET_*` de la composition de production reste vérifiée séparément par les tests déterministes `ksp-config-lib`.
|
||||
|
||||
Si l'opérateur ne possède pas les quatre credentials gratuits, ce smoke doit être consigné `SKIP opérateur`; il ne doit pas être déclaré PASS sans exécution.
|
||||
|
||||
## 6. Gate opérateur déterministe
|
||||
|
||||
```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 -p ksp-config-lib
|
||||
cargo test --workspace
|
||||
cargo tree -p ksp-offchain-transport-lib --edges normal
|
||||
```
|
||||
|
||||
Le `cargo tree` doit confirmer l'absence de SDK provider et l'absence de dépendance `ksp-offchain-transport-lib -> ksp-config-lib`.
|
||||
|
||||
## 7. Gate live keyless obligatoire
|
||||
|
||||
```bash
|
||||
cargo test -p ksp-offchain-transport-lib \
|
||||
--test market_price_live_smoke \
|
||||
keyless_market_price_providers_live_smoke \
|
||||
-- --ignored --exact --nocapture --test-threads=1
|
||||
```
|
||||
|
||||
Ce gate effectue des requêtes HTTPS externes réelles et doit être exécuté immédiatement avant `pre.012`.
|
||||
|
||||
## 8. Gate live keyed conditionnel
|
||||
|
||||
Si les quatre credentials sont disponibles dans le shell opérateur :
|
||||
|
||||
```bash
|
||||
printf '%s\n%s\n%s\n%s\n' \
|
||||
"$KSP_SECRET_BIRDEYE_API_KEY" \
|
||||
"$KSP_SECRET_COINGECKO_DEMO_API_KEY" \
|
||||
"$KSP_SECRET_COINMARKETCAP_API_KEY" \
|
||||
"$KSP_SECRET_JUPITER_API_KEY" \
|
||||
| cargo test -p ksp-offchain-transport-lib \
|
||||
--test market_price_live_smoke \
|
||||
keyed_market_price_providers_live_smoke \
|
||||
-- --ignored --exact --nocapture --test-threads=1
|
||||
```
|
||||
|
||||
Ne jamais coller les secrets dans le delta ou dans un log retourné. Si les quatre credentials ne sont pas disponibles, consigner `SKIP opérateur`.
|
||||
|
||||
## 9. Fichiers ajoutés/modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
crates/ksp-offchain-transport-lib/tests/market_price_live_smoke.rs
|
||||
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
|
||||
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
|
||||
deltas/0.2.11/pre.011.md
|
||||
```
|
||||
|
||||
## 10. Contrôles sandbox avant livraison
|
||||
|
||||
```text
|
||||
General Rust rule audit: clean
|
||||
Rust export completeness audit: 0 candidate(s)
|
||||
KSP workspace Rust rule audit: clean
|
||||
Markdown table audit: clean (125 tables / 120 files)
|
||||
Rust > 160 colonnes dans le nouveau smoke: 0
|
||||
```
|
||||
|
||||
Cargo n'est pas disponible dans l'environnement de construction ; les gates Cargo et live restent donc opérateur.
|
||||
|
||||
## 11. Sortie attendue
|
||||
|
||||
`pre.011` n'est close qu'après retour opérateur du gate déterministe, du `cargo tree` et du smoke keyless. Le smoke keyed est PASS s'il est exécuté avec les quatre credentials, sinon `SKIP opérateur` explicite.
|
||||
|
||||
Après ce gate seulement, `pre.012` peut porter la réconciliation documentaire finale.
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md -->
|
||||
<!-- version: 18 -->
|
||||
<!-- version: 19 -->
|
||||
|
||||
# Plan `0.2.11` — Off-chain price transport SOL/USD multi-provider
|
||||
|
||||
**Statut courant : `0.2.11-pre.010-fix.001` corrige uniquement un canari de hardening qui dupliquait lexicalement le scanner global de ownership logging et faisait échouer `cargo test --workspace`. Le hardening fonctionnel de `pre.010` reste inchangé ; l'autorité de détection des bypass logging demeure `ksp-logging-lib/tests/ownership.rs`. Le gate du fix doit être rejoué avant `pre.011`.**
|
||||
**Statut courant : `0.2.11-pre.011` matérialise le gate technique/live final. Le gate complet de `pre.010-fix.001` est PASS. Les contrats officiels des huit providers sont réaudités au `2026-08-26`, un smoke keyless de sept providers et un smoke keyed de quatre modes gratuits sont désormais durables et opt-in ; leur exécution opérateur reste requise avant la réconciliation documentaire finale de `pre.012`.**
|
||||
|
||||
## 1. Base et autorité
|
||||
|
||||
@@ -192,7 +192,7 @@ Les huit providers ci-dessous satisfont le critère fonctionnel retenu : accès
|
||||
|-------------------|-----------------------|-----------------------|----------------------------------------------------|--------------------------------------------|---------|
|
||||
| CoinGecko | keyless ou Demo | aucune ou Demo key | keyless dynamique IP, Demo 100 par min et 10k mois | agrégateur marché CoinGecko | IN |
|
||||
| CoinMarketCap | keyless ou Basic | aucune ou API key | keyless dynamique IP, Basic 50 par min et 15k mois | agrégateur marché CoinMarketCap | IN |
|
||||
| CoinPaprika | Free | aucune | 10 par sec IP et 20k mois, refresh moyen 5 min | agrégateur marché CoinPaprika | IN |
|
||||
| CoinPaprika | Free | aucune | 10 par sec IP, 20k mois, données jusqu’à 10 min | agrégateur marché CoinPaprika | IN |
|
||||
| Kraken | public Spot REST | aucune | cadence sûre documentée à 1 par sec ou moins | dernier trade du marché Kraken SOL/USD | IN |
|
||||
| Coinbase Exchange | public Exchange REST | aucune | 10 par sec IP, burst 15 | dernier trade du produit Coinbase SOL-USD | IN |
|
||||
| Jupiter Price V3 | keyless ou Free | aucune ou API key | keyless 0.5 RPS, Free 1 RPS | heuristique de prix USD orientée Solana | IN |
|
||||
@@ -247,11 +247,30 @@ Réaudit `pre.006` : le Developer Platform Jupiter courant utilise `https://api.
|
||||
|
||||
Réaudit `pre.007` : Birdeye Standard reste gratuit à 30 000 compute units inclus et 1 RPS au niveau du compte. L'endpoint Price Single `GET /defi/price` consomme actuellement 3 compute units, exige `X-API-KEY`, accepte `x-chain: solana` et le mint canonique WSOL, et fournit `value` plus `updateUnixTime`. Le quota et le coût par requête sont des metadata informatives ; KSP ne décrémente jamais localement le quota comme une vérité authoritative.
|
||||
|
||||
### 6.1 Gratuité technique et conditions d'usage
|
||||
### 6.1 Réaudit final provider du 2026-08-26
|
||||
|
||||
Le gate technique final reconfirme les contrats externes effectivement encodés par KSP :
|
||||
|
||||
```text
|
||||
CoinGecko public keyless toujours documenté ; Demo toujours 100 appels/min
|
||||
CoinMarketCap Simple Price V2 toujours documenté et disponible keyless via /public-api ; Basic gratuit toujours disponible
|
||||
CoinPaprika limite globale 10 requêtes/s/IP et Free 20k appels/mois ; la documentation tarifaire annonce des données pouvant dater jusqu’à 10 min
|
||||
Kraken endpoints Spot publics : 1 appel/s ou moins reste la cadence sûre documentée ; ticker SOLUSD inchangé
|
||||
Coinbase Exchange ticker public dernier trade inchangé ; 10 requêtes/s/IP avec burst 15
|
||||
Jupiter Price V3 surface api.jup.ag conservée, modes keyless/Free et usdPrice/blockId inchangés
|
||||
Birdeye Standard gratuit, 1 RPS, 30 000 CU, Price Single 3 CU
|
||||
DexScreener endpoint direct pair inchangé, 300 requêtes/min pour la famille pairs/tokens
|
||||
```
|
||||
|
||||
Les sources officielles CoinPaprika ne formulent pas toutes la fraîcheur de la même manière : la référence ticker mentionne encore environ 5 minutes en moyenne pour Free, tandis que la page Plans/Pricing annonce un intervalle pouvant aller jusqu’à 10 minutes. KSP ne pilote aucun scheduler depuis cette information provider : le runtime conserve le timestamp réel quand il existe et ne fabrique aucune fraîcheur. Cette divergence documentaire n'impose donc aucun changement de code.
|
||||
|
||||
Pour le smoke DexScreener final, le canari opt-in utilise uniquement la paire Orca SOL/USDC `7qbRF6YsyGuLUVs6Y1q64bdVrfe4ZcUUz1JRdoVNUJnm`, vérifiée active au `2026-08-26` avec WSOL comme base. Cette paire est une donnée de test publique, pas un default runtime ni un mécanisme de découverte.
|
||||
|
||||
### 6.2 Gratuité technique et conditions d'usage
|
||||
|
||||
Le verdict `IN` signifie ici qu'un accès HTTP gratuit permet techniquement de développer et live-tester le provider dans `0.2.11`. Il ne constitue pas une affirmation de gratuité commerciale permanente.
|
||||
|
||||
L'audit du 2026-08-25 relève notamment :
|
||||
Le réaudit final du 2026-08-26 relève notamment :
|
||||
|
||||
```text
|
||||
CoinGecko keyless est présenté pour faible volume, expérimentation/prototypage et usages assimilés ; le plan Demo porte ses propres conditions et attribution
|
||||
@@ -645,7 +664,9 @@ ne compare pas à un prix exact figé
|
||||
n'échoue pas tout le batch parce qu'un provider externe est indisponible
|
||||
```
|
||||
|
||||
Les providers keyed gratuits peuvent nécessiter des credentials opérateur via Config. Les smokes keyless restent séparables pour ne pas obliger à posséder toutes les clés au même instant.
|
||||
`pre.011` matérialise deux tests ignorés séparés. Le smoke keyless couvre Coinbase Exchange, CoinGecko, CoinMarketCap, CoinPaprika, DexScreener, Jupiter et Kraken en un `refresh_all` séquentiel. Il accumule tous les outcomes avant d'échouer afin de prouver qu'un provider externe en défaut ne court-circuite pas les suivants. Le smoke keyed couvre Birdeye Standard, CoinGecko Demo, CoinMarketCap Basic et Jupiter Free. Les quatre clés sont lues sur `stdin`, dans cet ordre, et ne sont ni des arguments CLI ni des données affichées.
|
||||
|
||||
Le smoke keyed caractérise directement les adapters ; la provenance Config des mêmes secrets reste couverte par les tests déterministes de `ksp-config-lib`. Si l'opérateur ne possède pas l'une des quatre clés gratuites, le smoke keyed est enregistré `SKIP opérateur` plutôt qu'interprété comme un échec produit. Le smoke keyless reste le gate live sans credential obligatoire.
|
||||
|
||||
## 19. Hors périmètre `0.2.11`
|
||||
|
||||
@@ -776,7 +797,7 @@ Ajout de `cfg.std.offchain_transport` / `schema.std.offchain_transport`, du docu
|
||||
|
||||
### `pre.010` — Hardening et complétude technique
|
||||
|
||||
**Statut : réalisé ; `pre.010-fix.001` corrige un faux positif du canari local de hardening, gate du fix à rejouer.**
|
||||
**Statut : réalisé ; `pre.010-fix.001` validé par gate opérateur complet PASS.**
|
||||
|
||||
Durcissement de la façade publique avec `#[non_exhaustive]` sur les enums évolutives, absorption future-safe de ces enums par l'adapter Config, canaries de complétude exacte des huit providers, tests adversariaux numeric/timestamps/provenance/redaction et vérification de l'absence d'URL/rate-limit provider configurables. Les premiers `README.md` / `USAGE.md` d'Off-chain Transport et les ajouts Config associés sont des brouillons techniques ; `pre.012` reste seule propriétaire de la réconciliation documentaire finale. Le contrat `refresh_many`/`refresh_all` V1 est explicitement réaffirmé séquentiel et déterministe.
|
||||
|
||||
@@ -784,9 +805,9 @@ Durcissement de la façade publique avec `#[non_exhaustive]` sur les enums évol
|
||||
|
||||
### `pre.011` — Gate technique et live final
|
||||
|
||||
**Statut : planifié.**
|
||||
**Statut : implémenté ; gate déterministe et smokes opérateur à exécuter.**
|
||||
|
||||
Smokes gratuits possibles, graphes Cargo, validations techniques finales et caractérisation actuelle des providers.
|
||||
Nouvel audit final des huit contrats provider, ajout de `tests/market_price_live_smoke.rs`, smoke keyless de sept providers, smoke keyed des quatre modes gratuits avec secrets sur `stdin`, vérification des graphes Cargo et reprise du gate workspace complet. Aucun endpoint, parser, rate-limit, Config ou comportement de refresh n'est modifié par cette tranche.
|
||||
|
||||
### `pre.012` — Réconciliation documentaire finale
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md -->
|
||||
<!-- version: 17 -->
|
||||
<!-- version: 18 -->
|
||||
|
||||
# Validation `0.2.11` — Off-chain price transport SOL/USD
|
||||
|
||||
@@ -315,42 +315,64 @@ La tranche n'ajoute aucune URL provider configurable, aucun SDK provider et aucu
|
||||
|
||||
## 3.13 Gate `0.2.11-pre.010`
|
||||
|
||||
| Critère | Statut | Preuve |
|
||||
|------------------------------------------------------------------------|---------|-----------------------------------------------------|
|
||||
| version workspace `0.2.11-pre.10.fix.1` | PASS | `Cargo.toml` racine |
|
||||
| enums publiques évolutives marquées `#[non_exhaustive]` | PASS | provider/access modes/setup + canari completeness |
|
||||
| adapter Config future-safe face aux access modes non exhaustifs | PASS | branches wildcard vers erreur effective sûre |
|
||||
| inventaire exact des huit providers et sémantiques verrouillé | PASS | `tests/release_completeness.rs` |
|
||||
| aucun environnement lu par les modules `market_price_*` | PASS | `tests/security_hardening.rs` |
|
||||
| aucun `as_f64` utilisé comme vérité de prix `market_price` | PASS | `tests/security_hardening.rs` |
|
||||
| aucun bypass logging direct dans le workspace | PASS | scanner global `ksp-logging-lib/tests/ownership.rs` |
|
||||
| Debug keyed settings/service ne divulgue pas les API keys | PASS | canari secret déterministe |
|
||||
| Config rejette URL provider et override de rate limit | PASS | schema strict + test Config adversarial |
|
||||
| timestamps RFC3339/pre-epoch/overflow durcis | PASS | unit test `market_price_adapter` |
|
||||
| JSON numeric pathologique/null/non-numérique rejeté | PASS | unit test `market_price_decimal` |
|
||||
| provenance accepte la borne et rejette trim/control/oversize | PASS | unit test `market_price_observation` |
|
||||
| `refresh_many` / `refresh_all` V1 documentés séquentiels/déterministes | PASS | plan + `README.md` / `USAGE.md` Off-chain |
|
||||
| README/USAGE techniques créés sans usurper la réconciliation finale | PASS | crate Off-chain + compléments Config |
|
||||
| audit Rust sandbox | PASS | clean, 0 candidate export |
|
||||
| audit Markdown sandbox | PASS | clean sur le fix |
|
||||
| gate brut `pre.010` | FAIL | workspace ownership logging : 1 canari FAIL |
|
||||
| fix `pre.010-fix.001` statique | PASS | doublon lexical retiré, audits sandbox |
|
||||
| gate Cargo complet du fix | PLANNED | à rejouer avant `pre.011` |
|
||||
| Critère | Statut | Preuve |
|
||||
|------------------------------------------------------------------------|--------|-----------------------------------------------------|
|
||||
| version workspace `0.2.11-pre.10.fix.1` | PASS | `Cargo.toml` racine |
|
||||
| enums publiques évolutives marquées `#[non_exhaustive]` | PASS | provider/access modes/setup + canari completeness |
|
||||
| adapter Config future-safe face aux access modes non exhaustifs | PASS | branches wildcard vers erreur effective sûre |
|
||||
| inventaire exact des huit providers et sémantiques verrouillé | PASS | `tests/release_completeness.rs` |
|
||||
| aucun environnement lu par les modules `market_price_*` | PASS | `tests/security_hardening.rs` |
|
||||
| aucun `as_f64` utilisé comme vérité de prix `market_price` | PASS | `tests/security_hardening.rs` |
|
||||
| aucun bypass logging direct dans le workspace | PASS | scanner global `ksp-logging-lib/tests/ownership.rs` |
|
||||
| Debug keyed settings/service ne divulgue pas les API keys | PASS | canari secret déterministe |
|
||||
| Config rejette URL provider et override de rate limit | PASS | schema strict + test Config adversarial |
|
||||
| timestamps RFC3339/pre-epoch/overflow durcis | PASS | unit test `market_price_adapter` |
|
||||
| JSON numeric pathologique/null/non-numérique rejeté | PASS | unit test `market_price_decimal` |
|
||||
| provenance accepte la borne et rejette trim/control/oversize | PASS | unit test `market_price_observation` |
|
||||
| `refresh_many` / `refresh_all` V1 documentés séquentiels/déterministes | PASS | plan + `README.md` / `USAGE.md` Off-chain |
|
||||
| README/USAGE techniques créés sans usurper la réconciliation finale | PASS | crate Off-chain + compléments Config |
|
||||
| audit Rust sandbox | PASS | clean, 0 candidate export |
|
||||
| audit Markdown sandbox | PASS | clean sur le fix |
|
||||
| gate brut `pre.010` | FAIL | workspace ownership logging : 1 canari FAIL |
|
||||
| fix `pre.010-fix.001` statique | PASS | doublon lexical retiré, audits sandbox |
|
||||
| gate Cargo complet du fix | PASS | gate opérateur complet du 2026-08-26 |
|
||||
|
||||
`pre.010` ne fait aucun smoke live, ne change aucun endpoint provider et n'introduit ni fallback, ni consensus, ni scheduler. Les documents crate créés ici sont des brouillons techniques courants ; `pre.012` reste seule propriétaire de la réconciliation documentaire finale après le gate technique/live de `pre.011`. Le gate brut de `pre.010` a passé audits, `cargo check`, Clippy et tests ciblés, puis `cargo test --workspace` a échoué dans `ksp-logging-lib/tests/ownership.rs` parce que `tests/security_hardening.rs` contenait littéralement le chemin logging interdit dans une assertion destinée à le bannir. `pre.010-fix.001` supprime ce scanner local redondant et conserve `ksp-logging-lib/tests/ownership.rs` comme autorité workspace unique.
|
||||
`pre.010` ne fait aucun smoke live, ne change aucun endpoint provider et n'introduit ni fallback, ni consensus, ni scheduler. Les documents crate créés ici sont des brouillons techniques courants ; `pre.012` reste seule propriétaire de la réconciliation documentaire finale après le gate technique/live de `pre.011`. Le gate brut de `pre.010` a passé audits, `cargo check`, Clippy et tests ciblés, puis `cargo test --workspace` a échoué dans `ksp-logging-lib/tests/ownership.rs` parce que `tests/security_hardening.rs` contenait littéralement le chemin logging interdit dans une assertion destinée à le bannir. `pre.010-fix.001` supprime ce scanner local redondant et conserve `ksp-logging-lib/tests/ownership.rs` comme autorité workspace unique. Le gate rejoué sur `0.2.11-pre.10.fix.1` est ensuite intégralement PASS : audits Rust/Markdown, `cargo check --workspace`, Clippy, 58 unitaires + 6 boundary + 7 public API + 2 completeness + 3 hardening Off-chain, 120 unitaires Config et `cargo test --workspace`.
|
||||
|
||||
## 3.14 Gate `0.2.11-pre.011`
|
||||
|
||||
| Critère | Statut | Preuve / condition opérateur |
|
||||
|--------------------------------------------------------------|---------|---------------------------------------------------------------|
|
||||
| version workspace `0.2.11-pre.11` | PASS | `Cargo.toml` racine |
|
||||
| réaudit officiel des huit providers au `2026-08-26` | PASS | sources provider courantes, aucun contrat wire à modifier |
|
||||
| CoinPaprika fraîcheur contradictoire sans faux scheduler | PASS | référence ~5 min / plans jusqu’à 10 min, runtime conservateur |
|
||||
| smoke keyless durable sur sept providers | PASS | test ignoré `keyless_market_price_providers_live_smoke` |
|
||||
| smoke keyed durable sur quatre modes gratuits | PASS | test ignoré `keyed_market_price_providers_live_smoke` |
|
||||
| secrets keyed lus sur `stdin`, jamais en argument CLI | PASS | `tests/market_price_live_smoke.rs` |
|
||||
| DexScreener utilise une paire publique explicite sans search | PASS | Orca SOL/USDC canari, WSOL base |
|
||||
| un échec provider ne court-circuite pas le batch live | PASS | outcomes collectés avant assertion finale |
|
||||
| aucun prix exact live figé | PASS | validation identité/pair/sémantique/prix positif uniquement |
|
||||
| audit Rust sandbox | PASS | clean, 0 candidate export |
|
||||
| audit Markdown sandbox | PASS | clean, 125 tables / 120 fichiers |
|
||||
| gate Cargo déterministe complet | PENDING | opérateur |
|
||||
| `cargo tree` Off-chain | PENDING | opérateur, aucun SDK provider / aucune dépendance Config |
|
||||
| smoke keyless réel | PENDING | obligatoire avant `pre.012` |
|
||||
| smoke keyed réel | PENDING | exécuter si les quatre credentials gratuits sont disponibles |
|
||||
|
||||
La tranche `pre.011` ne change aucune logique réseau de production. Elle rend uniquement le gate live durable et réaudite les contrats externes avant stable. Le smoke keyless est le gate live sans credential obligatoire. Le smoke keyed peut être `SKIP opérateur` si les quatre clés ne sont pas disponibles ; ce statut doit être consigné explicitement dans la validation finale et ne doit jamais être transformé en PASS fictif.
|
||||
|
||||
## 4. Matrice provider prévue
|
||||
|
||||
| Provider | SOL/USD V1 | Gratuit V1 | Mode auth prévu | Test déterministe | Smoke live | Statut courant |
|
||||
|-------------------|------------|------------|----------------------|-------------------|------------|----------------|
|
||||
| CoinGecko | Oui | Oui | keyless ou Demo key | PASS | PLANNED | IMPLEMENTED |
|
||||
| CoinMarketCap | Oui | Oui | keyless ou Basic key | PASS | PLANNED | IMPLEMENTED |
|
||||
| CoinPaprika | Oui | Oui | aucune | PASS | PLANNED | IMPLEMENTED |
|
||||
| Kraken | Oui | Oui | aucune | PASS | PLANNED | IMPLEMENTED |
|
||||
| Coinbase Exchange | Oui | Oui | aucune | PASS | PLANNED | IMPLEMENTED |
|
||||
| Jupiter Price V3 | Oui | Oui | keyless ou Free key | PASS | PLANNED | IMPLEMENTED |
|
||||
| Birdeye | Oui | Oui | API key Standard | PASS | PLANNED | IMPLEMENTED |
|
||||
| DexScreener | Oui | Oui | aucune | PASS | PLANNED | IMPLEMENTED |
|
||||
| CoinGecko | Oui | Oui | keyless ou Demo key | PASS | PENDING | IMPLEMENTED |
|
||||
| CoinMarketCap | Oui | Oui | keyless ou Basic key | PASS | PENDING | IMPLEMENTED |
|
||||
| CoinPaprika | Oui | Oui | aucune | PASS | PENDING | IMPLEMENTED |
|
||||
| Kraken | Oui | Oui | aucune | PASS | PENDING | IMPLEMENTED |
|
||||
| Coinbase Exchange | Oui | Oui | aucune | PASS | PENDING | IMPLEMENTED |
|
||||
| Jupiter Price V3 | Oui | Oui | keyless ou Free key | PASS | PENDING | IMPLEMENTED |
|
||||
| Birdeye | Oui | Oui | API key Standard | PASS | PENDING | IMPLEMENTED |
|
||||
| DexScreener | Oui | Oui | aucune | PASS | PENDING | IMPLEMENTED |
|
||||
|
||||
`AUDITED` signifie uniquement que la documentation officielle actuelle satisfait le gate d'entrée. Ce statut ne vaut pas preuve d'un smoke réseau KSP.
|
||||
|
||||
@@ -482,10 +504,10 @@ Cette section valide l'architecture préparée par `0.2.11`; l'application elle-
|
||||
| `cargo test --workspace` | PLANNED |
|
||||
| tests `ksp-offchain-transport-lib` | PLANNED |
|
||||
| tests Config | PLANNED |
|
||||
| cargo tree Off-chain Transport | PLANNED |
|
||||
| absence de dépendances provider SDK | PLANNED |
|
||||
| smokes keyless gratuits | PLANNED |
|
||||
| smokes keyed gratuits disponibles à l'opérateur | PLANNED |
|
||||
| cargo tree Off-chain Transport | PENDING |
|
||||
| absence de dépendances provider SDK | PENDING |
|
||||
| smokes keyless gratuits | PENDING |
|
||||
| smokes keyed gratuits disponibles à l'opérateur | PENDING |
|
||||
| réconciliation README/USAGE/plan/validation | PLANNED |
|
||||
|
||||
## 13. Hors scope validé
|
||||
|
||||
Reference in New Issue
Block a user