# Plan `0.2.11` — Off-chain price transport SOL/USD multi-provider **Statut courant : `0.2.11-pre.001` ouvre la release par le gate obligatoire de lecture, réaudit externe, comparaison des sémantiques de prix, threat model, sizing et planification. Aucun client provider n'est encore implémenté. Le scope V1 est volontairement limité à SOL/USD via HTTP REST `reqwest`, sans SDK provider, avec huit providers gratuits retenus pour implémentation progressive.** ## 1. Base et autorité Base stable autoritaire : ```text v0.2.10 ``` État vérifié dans l'archive Gitea fournie : ```text workspace.package.version = 0.2.10 deltas/0.2.10/rel.001.md présent prompts/016-V0_2_11_START_PROMPT.md présent ksp-offchain-transport-lib absente metadata .git absente de l'archive Gitea ``` Cette tranche ouvre : ```text workspace.package.version = 0.2.11-pre.1 commit attendu = v0.2.11-pre.001 aucun tag prerelease ``` Ordre d'autorité pendant `0.2.11` : ```text règles normatives KSP archive stable v0.2.10 et code réellement livré décisions opérateur consignées dans le présent plan validation 014 et deltas immuables de 0.2.11 prompt 016 pour les contraintes non supersédées sources provider officielles actuelles pour les faits externes ``` ## 2. Supersessions explicites du prompt d'ouverture Le brainstorming opérateur du 2026-08-25 remplace plusieurs hypothèses préparatoires du prompt `016`. ```text minimum V1 SOL/USD uniquement nombre de providers un ou plusieurs, sans limite artificielle à un provider initial providers ciblés tous les candidats gratuits retenus par le gate pre.001 transport provider HTTP REST avec reqwest uniquement SDK provider interdit dans 0.2.11 SOL/EUR hors scope de cette première release fallback automatique hors scope consensus/agrégation KSP hors scope future application ksp-app-solprices-desk en 0.2.12 rôle de l'application HID pure, aucune connaissance d'un provider DexScreener V1 un client SOL/USD sur une paire configurée et validée ``` Le retrait de SOL/EUR n'interdit pas son ajout ultérieur. Il évite de forcer dès V1 une conversion fiat externe ou de privilégier artificiellement les providers multi-fiat. ## 3. Mission opérationnelle `0.2.11` doit créer `ksp-offchain-transport-lib` comme propriétaire de la récupération de prix off-chain SOL/USD et des différences opérationnelles entre providers. La surface commune doit permettre à un consumer de : ```text lister les providers de prix connus du service observer leur description et leur sémantique générique observer leur disponibilité runtime observer leur capacité de refresh et le prochain instant admissible rafraîchir un provider par identifiant opaque rafraîchir plusieurs providers sans violer leurs limites respectives obtenir une observation SOL/USD typée et sa provenance obtenir une absence/erreur/cooldown sans connaissance du fournisseur ``` La crate possède les exceptions provider. Un consumer ne doit pas coder : ```text URL provider header d'auth provider ID CoinGecko ou CoinMarketCap mint Jupiter/Birdeye paire Kraken/Coinbase pair address DexScreener rate limit provider forme JSON wire provider mapping de status HTTP provider ``` ## 4. Frontière avec la future `ksp-app-solprices-desk` La release suivante doit créer : ```text ksp-app-solprices-desk ``` Son rôle est **purement HID**. Elle dépend de la façade publique de `ksp-offchain-transport-lib` et ne connaît aucun provider individuellement. Son premier tableau pourra afficher génériquement : ```text nom d'affichage provider prix SOL/USD fraîcheur disponible état runtime prochain refresh possible bouton refresh ``` Un bouton de refresh multiple/all appelle une opération générique du service. L'application ne fait aucun `sleep`, aucun token bucket, aucun retry provider et aucun dispatch par nom de provider. La composition Rust de l'application peut utiliser Config pour obtenir les settings puis construire le service Off-chain Transport, mais elle ne matche pas les variantes provider pour exécuter une requête. ## 5. Baseline technique interne L'inventaire de `v0.2.10` confirme que les primitives génériques nécessaires existent déjà au workspace : ```text reqwest ^0.13 serde ^1.0 serde_json ^1.0 tokio ^1.53 http ^1.5 chrono ^0.4 si parsing timestamp réellement nécessaire ``` Décision de dépendances V1 : ```text aucun SDK CoinGecko aucun SDK CoinMarketCap aucun SDK CoinPaprika aucun SDK Kraken aucun SDK Coinbase aucun SDK Jupiter aucun SDK Birdeye aucun SDK DexScreener aucune bibliothèque decimal provider/externe ajoutée par facilité ``` Les adapters utilisent `reqwest` et des DTOs wire privés KSP. `0.2.11-pre.001` n'ajoute encore aucune crate ni dépendance. ## 6. Audit provider actuel du 2026-08-25 Les huit providers ci-dessous satisfont le critère fonctionnel retenu : accès gratuit actuel, HTTP REST simple et possibilité de produire SOL/USD sans SDK provider. | Provider | Accès gratuit V1 | Auth V1 | Limite ou quota à modéliser | Sémantique SOL/USD | Verdict | |-------------------|-----------------------|-----------------------|----------------------------------------------------|--------------------------------------------|---------| | 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 | | 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 | | Birdeye | Standard | X-API-KEY obligatoire | 1 RPS compte, 30k CU, Price Single 3 CU | spot USD token Solana Birdeye | IN | | DexScreener | API publique gratuite | aucune | 300 par min pour pairs/tokens | priceUsd de la paire DEX Solana configurée | IN | Sources officielles principales auditées : ```text https://docs.coingecko.com/docs/keyless-public-api https://docs.coingecko.com/reference/simple-price https://www.coingecko.com/en/api/pricing https://coinmarketcap.com/api/documentation/pro-api-reference/keyless-public-api https://coinmarketcap.com/api/documentation/pro-api-reference/cryptocurrency https://coinmarketcap.com/api/pricing https://docs.coinpaprika.com/api-reference/rest-api/introduction https://docs.coinpaprika.com/api-reference/tickers/get-ticker-for-a-specific-coin https://docs.coinpaprika.com/api-plans https://support.kraken.com/articles/206548367-what-are-the-api-rate-limits- https://support.kraken.com/hc/articles/360000919986-public-endpoint-examples-you-can-try-them-directly-in-a-web-browser- https://docs.kraken.com/api/docs/rest-api/get-ticker-information https://docs.cdp.coinbase.com/exchange/rest-api/rate-limits https://docs.cdp.coinbase.com/api-reference/exchange-api/rest-api/products/get-product-ticker https://docs.cdp.coinbase.com/api-reference/exchange-api/rest-api/products/get-single-product https://developers.jup.ag/docs/portal/migration https://developers.jup.ag/pricing https://developers.jup.ag/docs/price https://docs.birdeye.so/docs/pricing https://docs.birdeye.so/docs/rate-limiting https://docs.birdeye.so/reference/get-defi-price https://docs.birdeye.so/docs/compute-unit-cost https://docs.dexscreener.com/api/reference https://docs.dexscreener.com/ ``` Les chiffres externes sont une photographie d'audit, pas une constante éternelle. Les adapters doivent rester capables de classer `429` et les informations serveur sans supposer que la documentation ne changera jamais. ### 6.1 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 : ```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 CoinMarketCap keyless est présenté pour évaluation/prototypage ; un plan authentifié reste la voie documentée pour un usage avec allowance dédiée CoinPaprika Free est annoncé pour usage personnel ; l'usage commercial relève des offres payantes actuelles ``` Conséquence KSP : ```text les adapters gratuits restent implémentables en V1 aucune documentation stable KSP ne promet une gratuité commerciale durable les conditions d'usage et plans gratuits sont réaudités au gate live final avant publication stable un changement de plan/licence peut rendre un provider administrativement indisponible sans casser le contrat provider-neutral ``` ## 7. Providers écartés de V1 ### Pyth Hermes Pyth reste intéressant comme oracle mais la transition Pyth Core annoncée pour le 2026-08-26 rend son modèle d'accès immédiatement mouvant et ne garantit pas un accès gratuit durable après la période d'essai. Décision : ```text Pyth = OUT de 0.2.11 V1 ``` Un audit ultérieur pourra l'ajouter comme sémantique oracle distincte. ### Binance Aucune assimilation implicite de SOL/USDT à SOL/USD n'est admise. Décision : ```text Binance = OUT tant qu'un produit SOL/USD direct et pertinent n'est pas retenu explicitement ``` ## 8. Identité et sémantique de la paire V1 Le contrat public V1 ne devient pas un moteur générique de symboles. La seule paire fonctionnelle exposée est : ```text SOL/USD ``` Les identifiants propriétaires restent privés aux adapters ou aux settings provider : ```text CoinGecko coin id solana CoinMarketCap asset id 5426 CoinPaprika coin id sol-solana Kraken pair SOLUSD Coinbase product SOL-USD Jupiter mint SOL canonique attendu par Price V3 Birdeye mint SOL canonique attendu par Price Single DexScreener pair address configurée, base SOL vérifiée ``` Le résultat commun ne prétend pas que les prix sont équivalents. Le descriptor générique expose une classe de sémantique, par exemple : ```text AggregatedMarket ExchangeLastTrade SolanaHeuristic SolanaSpot DexPairUsd ``` Le nom final des variantes appartient à `pre.002`, mais la distinction sémantique est obligatoire. ## 9. Cas DexScreener V1 Décision opérateur : `0.2.11` implémente bien DexScreener, mais uniquement comme client SOL/USD. Le client V1 : ```text utilise un pair address Solana configuré appelle l'endpoint pair direct vérifie chainId = solana vérifie que la paire correspond au SOL attendu lit uniquement priceUsd pour le contrat commun conserve la pair address comme provenance provider sûre ne recherche pas automatiquement la meilleure pool ne sélectionne pas par liquidité ne construit aucun consensus entre pools ``` Le choix d'une paire explicite évite d'introduire silencieusement une politique de sélection DEX. Une future version pourra remplacer cette stratégie sans modifier le contrat HID de `ksp-app-solprices-desk`. ## 10. Représentation numérique Le gate rejette `f64` comme représentation canonique publique du prix. Direction retenue pour `pre.002` : type décimal KSP borné, sérialisable sans perte, construit depuis la représentation textuelle du provider. Forme de travail : ```text coefficient positif u128 scale bornée normalisation des zéros terminaux parsing décimal et notation scientifique bornés aucun NaN aucun Inf overflow rejeté zéro/négatif rejetés pour un prix réussi serialization canonique décimale en chaîne ``` Le wire d'un provider peut être JSON number ou string. L'adapter le convertit vers le type KSP sans utiliser `as_f64()` comme vérité canonique. Cette solution évite d'ajouter une crate decimal uniquement pour V1 et prépare le passage frontend sans perte IEEE-754. ## 11. Observation commune et fraîcheur Une observation SOL/USD commune doit distinguer : ```text provider_id opaque prix décimal KSP sémantique de prix instant de départ requête KSP instant de réception KSP instant provider optionnel lorsqu'il existe réellement provenance provider sûre et bornée ``` Règles : ```text ne jamais inventer un timestamp provider ne jamais interpréter un timestamp de création token comme timestamp de prix ne jamais transformer block id, pair creation time ou autre champ en faux update time absence de prix != prix zéro stale != transport indisponible ``` ## 12. Registry et état runtime `ksp-offchain-transport-lib` possède un registry runtime des providers de prix activés/configurés. Deux surfaces génériques doivent rester distinctes : ```text capabilities statiques auditées état runtime courant ``` Le descriptor statique peut exposer : ```text provider id opaque nom d'affichage sémantique mode d'auth générique limitation générique support SOL/USD ``` L'état runtime peut représenter au minimum : ```text ready cooling down avec prochain instant admissible temporarily unavailable authentication unavailable quota unavailable misconfigured disabled ``` Les noms Rust finaux sont à fixer avec le code, mais aucun consumer ne doit parser des strings d'erreur pour connaître cet état. ## 13. Rate limiting et refresh multiple Le rate limiting appartient entièrement à `ksp-offchain-transport-lib`. Le modèle doit savoir représenter : ```text cadence fixe burst si documenté fenêtre par seconde ou minute quota long terme informatif limite dynamique/server-driven scope IP, compte ou organisation lorsque pertinent ``` Les quotas mensuels provider ne sont pas décrémentés comme une vérité authoritative locale : un autre processus peut consommer le même compte. Ils sont exposés comme capacités documentées ; les réponses provider restent la vérité runtime. ### Refresh individuel Un `refresh(provider_id)` : ```text vérifie l'éligibilité locale exécute si autorisé met à jour observation et état classe 429 et Retry-After ne contourne jamais le cooldown ``` ### Refresh multiple/all Un refresh multiple : ```text évalue chaque provider indépendamment lance concurremment les providers éligibles ne bloque pas Coinbase parce que Jupiter est en cooldown ne dort pas pour attendre tous les providers non éligibles retourne un résultat générique par provider expose le prochain instant admissible pour les providers différés ``` La future UI peut donc désactiver/annoter un bouton à partir de l'état retourné sans connaître la règle provider. ## 14. HTTP commun et résilience Les adapters REST réutilisent les patterns sûrs déjà éprouvés dans KSP sans dépendre de `ksp-onchain-transport-lib`. Décisions V1 : ```text reqwest uniquement HTTPS officiel fixe lorsque le provider le supporte redirect désactivé proxy implicite désactivé selon le pattern KSP retenu connect timeout borné request timeout borné GET uniquement pour les prix V1 body réponse borné JSON attendu et validé 429 classé explicitement Retry-After honoré lorsqu'il est exploitable 5xx classé transitoire 401/403 classé auth/access URL retirée des erreurs reqwest avant contexte KSP aucun body remote brut dans KspError ``` Il n'existe pas d'URL provider arbitraire dans la Config V1. Cela évite SSRF, redirection de credential et confusion de provenance. ## 15. Config provider-capability-aware Config reste l'unique propriétaire des documents/env/secrets. Off-chain Transport expose ses settings publics ; Config adapte un futur document standard dédié, pressenti : ```text file_id = std.offchain_transport ``` Les branches de configuration correspondent aux capacités réelles de chaque provider. | Provider | Champs spécifiques V1 attendus | |-------------------|------------------------------------------------------------| | CoinGecko | enabled, access_mode keyless ou demo, api_key optionnelle | | CoinMarketCap | enabled, access_mode keyless ou basic, api_key optionnelle | | CoinPaprika | enabled | | Kraken | enabled | | Coinbase Exchange | enabled | | Jupiter | enabled, access_mode keyless ou free, api_key optionnelle | | Birdeye | enabled, api_key requise si activé | | DexScreener | enabled, sol_usd_pair_address requise si activé | Le schema final doit conditionner la présence des credentials au mode choisi. Aucun secret n'est versionné. Toute variable d'environnement ajoutée respecte `KSP_SECRET_*` et est inventoriée dans `.env.example`. Les limites provider ne deviennent pas des valeurs libres permettant à Config/UI d'augmenter la cadence. Une future policy pourra permettre un throttle **plus strict** si un besoin réel apparaît. ## 16. Ownership des erreurs et indisponibilités Une panne d'un provider ne doit pas rendre tous les autres inexploitables. Le service classe par provider : ```text configuration manquante/invalide DNS/connect/timeout TLS HTTP 4xx HTTP 429 HTTP 5xx JSON invalide ou schema drift asset/pair mismatch prix absent prix numérique invalide staleness provider si mesurable ``` Un refresh multiple retourne donc des outcomes partiels. Le service peut conserver un provider dans l'inventaire avec un état indisponible, au lieu de forcer l'application à connaître pourquoi Birdeye ou un autre provider manque. ## 17. Threat model V1 | Risque | Contrôle prévu | |------------------------------------------|-----------------------------------------------------------------| | fuite API key | Config propriétaire, header privé, redaction logs/debug/erreurs | | SSRF ou exfiltration vers URL configurée | origines provider fixes en V1, aucun endpoint arbitraire | | redirect credential | redirects HTTP désactivés | | proxy environnement implicite | policy no-proxy cohérente avec les clients KSP sensibles | | réponse JSON énorme | limite de taille avant désérialisation | | prix NaN, Inf, négatif ou overflow | parser décimal KSP borné | | faux asset par collision de symbole | IDs provider fixes et validations d'identité | | mauvaise paire DexScreener | pair address explicite et identité SOL vérifiée | | 429 répétés | limiter local, Retry-After, cooldown provider | | provider down | état provider séparé, autres providers continuent | | stale data | timestamps réels conservés, aucune fraîcheur inventée | | schema drift | DTOs wire privés stricts et erreur provider classée | | quota partagé hors processus | quota local non présenté comme authoritative | | différences de prix | sémantique/provenance visible, aucun consensus KSP V1 | | log d'une réponse sensible | aucun payload remote brut dans les erreurs/logs | ## 18. Tests et smokes attendus ### Tests déterministes Prévoir : ```text parsing décimal string/JSON number/scientifique limites et overflow mapping de chaque wire provider asset/pair mismatch prix null/absent HTTP status classification 429 et Retry-After rate limiter par policy cooldown états unavailable/auth/quota refresh single refresh all partiel un provider en échec n'empêche pas les autres redaction credential Config branches provider public API sans type wire provider ``` ### Smokes live opt-in Une tranche technique dédiée en fin de release doit tester les adapters réellement retenus avec un accès gratuit courant. Le smoke : ```text ne hardcode aucun secret n'affiche aucun secret interroge uniquement SOL/USD vérifie prix positif et parseable vérifie provider et identité attendue respecte chaque cadence 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. ## 19. Hors périmètre `0.2.11` ```text SOL/EUR autres fiat prix d'autres assets ou SPL tokens publics Price Desk Tauri modification Wallet Desk provider SDK WebSocket/SSE price streaming OHLCV/candles historique/backfill prix order books quotes/routing/swap metadata token/IPFS/Arweave consensus multi-provider moyenne/médiane KSP fallback automatique choix automatique de pool DexScreener cache durable nouvelle abstraction réseau universelle Pyth Binance sans SOL/USD direct retenu ``` Le fait que Jupiter/Birdeye/DexScreener soient capables de couvrir d'autres tokens ne les met pas dans la surface publique V1. ## 20. Forecast souple des prereleases Chaque tranche technique vise environ 15 à 20 minutes de travail effectif, hors build lent ou attente provider. Le découpage pourra être réorganisé par delta si la réalité l'exige. | Prerelease | Objectif principal | |------------|----------------------------------------------------------------------------------------------------------------| | `pre.001` | audit externe, sémantiques, scope SOL/USD, architecture HID, threat model, numeric design, sizing et plan | | `pre.002` | création crate, modèle SOL/USD, décimal KSP, descriptors/settings/états provider-neutral | | `pre.003` | client HTTP REST commun, classification d'erreurs, body/timeouts/redaction, limiter/cooldown générique | | `pre.004` | adapters CoinGecko, CoinMarketCap et CoinPaprika avec tests wire | | `pre.005` | adapters Kraken et Coinbase Exchange avec sémantique exchange et tests | | `pre.006` | adapters Jupiter et DexScreener, pair SOL/USD DexScreener explicite et validée | | `pre.007` | adapter Birdeye, auth provider, registry complet et états d'indisponibilité | | `pre.008` | service refresh single/multiple, orchestration rate-limit-aware et tests cross-provider | | `pre.009` | document Config `std.offchain_transport`, schema/examples/env secrets et adapter Config -> Off-chain Transport | | `pre.010` | hardening public API, tests adversariaux/completeness, README/USAGE draft technique sans réconciliation finale | | `pre.011` | gate technique/live final, smokes gratuits possibles, graphes Cargo et caractérisation finale des providers | | `pre.012` | réconciliation documentaire finale plan/validation/README/USAGE/architecture et références durables | | `pre.013` | préparation publication minimale : prompt `0.2.12`, CHANGELOG, ROADMAP et version mécanique | | `rel.001` | publication stable `v0.2.11` | Les responsabilités de clôture restent strictement séparées : ```text pre.011 = technique/live pre.012 = documentation finale pre.013 = prompt suivant + CHANGELOG + ROADMAP seulement ``` ## 21. Gate de sizing `pre.001` Verdict : **GO sans split de release**. Justification : ```text huit adapters REST sont petits et indépendants aucun SDK provider n'est nécessaire les primitives HTTP/serde/tokio sont déjà au workspace la seule paire publique est SOL/USD le contrat commun reste volontairement petit la complexité réelle est isolée dans registry/rate limiting/availability les adapters peuvent être livrés par groupes de prereleases courtes ``` `0.2.11` reste toutefois une release multi-provider et ne doit pas compresser artificiellement les gates de sécurité/live.