# Nouveau Client en Next.js et Silkaj v0.12.1

**URL:** https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437
**Category:** Duniter-v1
**Tags:** docker, silkaj
**Created:** [6 November 2025 13:14 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437 "2025-11-06T13:14:40Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [6 November 2025 13:14 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/1 "2025-11-06T13:14:40Z")

</div>

J’ai développé un nouveau Client destiné uniquement au réseau des épiceries 100% Ğ1 de la Drôme. C’est une appli qui sert de caisse pour chaque épicerie, permet la gestion des comptes portefeuille internes au réseau, et réalise les paiements via la Blockchain en utilisant en back Silkaj v0.12.1.

> **Résumé**
>
> # 🏪 PMA Guichet v1.5 - Présentation
> 
> **Application web de caisse pour les épiceries en 100% Ğ1 (blockchain Duniter)**
> 
> ## Architecture Technique
> 
> **Stack moderne** : Next.js 14 (App Router) + TypeScript + React 18, base de données MariaDB centralisée avec Prisma ORM, cache Redis distribué, containerisation Docker complète, intégration Silkaj v0.12.1 pour paiements blockchain Ğ1.
> 
> **Architecture cloud-native** : Données critiques (DEWIF, tickets) stockées en base de données centralisée, permettant déploiement multi-instances avec synchronisation automatique, volumes Docker persistants pour relevés CSV et backups.
> 
> ## Fonctions Principales
> 
> **Paiements Ğ1** : Système de paiement sécurisé via Silkaj avec validation PIN (4 chiffres), génération automatique de fichiers DEWIF (74 octets), double stockage pour compatibilité, failover multi-nœuds blockchain avec basculement automatique.
> 
> **Gestion de tickets** : Création multi-lignes avec support multi-devises (Ğ1 ou DU), conversion automatique, paiement immédiat depuis panier épicier ou interface admin, annulation avec permissions granulaires, historique complet cloud-native.
> 
> **Dividende Épicerie (DE)** : Calcul automatique via formule TRM, distribution équitable entre épiciers actifs, dashboard statistiques temps réel, versement automatique via scripts CRON.
> 
> **Communication interne** : Système de notifications push temps réel, messagerie privée PMA ↔ PMA (interface style WhatsApp), annonces réseau broadcast (SUPERADMIN), documents juridiques versionnés avec vote démocratique (seuil 60%), acceptation traçable avec IP.
> 
> **Gestion des comptes** : Création automatique avec génération clés Ğ1 (Diceware), génération cartes PMA en PDF (EAN-13), scanner codes-barres intégré (3 points), graphique d’évolution de solde (Recharts + CSV Silkaj), gestion multi-épiceries avec relations principales/secondaires.
> 
> **Administration** : 8 pages admin opérationnelles (comptes, épiceries, tickets, paiements, FAQ, configuration, monitoring, administrateurs), système multi-rôles (SUPERADMIN, ADMIN, ÉPICIER, VISITEUR) avec permissions granulaires, authentification JWT sécurisée avec déconnexion automatique, monitoring système complet avec logs Winston et métriques Redis.

Je peux vous en parler plus en détails si besoin mais j’ai une question purement technique concernant le réseau v1.

Pour un compte portefeuille en particulier, j’ai deux soldes différents :

- Avec Cesium : 1 207,60 Ğ1
- Avec Silkaj : 1 337.71 Ğ1

> **Sortie Silkaj**
>
> Balance of pubkey : 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL:9YE  
> Blockchain (unit|relative) │ 1207.6 Ğ1 | 105.19 UD Ğ1  
> Pending transaction (unit|relative) │ 130.11 Ğ1 | 11.33 UD Ğ1  
> Total balance (unit|relative) │ 1337.71 Ğ1 | 116.53 UD Ğ1

**Ma question** : Comment obtenir des détails sur cette transaction en pending ? De qui ? Quand ?

Une petite aide serait la bienvenue, @Moul , merci.

---

<div class="post-metadata">

### Author: ![Moul](https://forum.duniter.org/user_avatar/forum.duniter.org/moul/32/9145_2.png) [@Moul](https://forum.duniter.org/u/Moul)
#### Post date: [6 November 2025 13:22 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/2 "2025-11-06T13:22:38Z")

</div>

Tu peux surement voir les transferts en attente/pas entièrement validés dans la vue historique de Cesium. Elles ne sont pas affichées dans l’historique des transferts de Silkaj.  
Après, c’est visible pour pas longtemps.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [6 November 2025 13:24 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/3 "2025-11-06T13:24:46Z")

</div>

merci de ta réponse rapide @moul , mais non, rien dans l’historique de cesium

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [6 November 2025 17:06 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/4 "2025-11-06T17:06:44Z")

</div>

Plusieurs heures après, j’ai toujours cette transaction en pending et rien sur cesium

> **Logs de mon app**
>
> [LOGIN] ✅ PIN validé  
> [2025-11-06T16:59:05.404Z] [INFO] [API Statuts Documents] Statuts récupérés pour épicier 1 - 1 documents  
> [2025-11-06T16:59:11.369Z] [INFO] Récupération des informations du compte DE SYSTEME  
> [2025-11-06T16:59:11.464Z] [INFO] Compte système trouvé: Compte DE System (999999)  
> [2025-11-06T16:59:11.464Z] [INFO] Récupération solde pour clé: 8jWwwyRZMrRsidfVpsGd…  
> 🔍 [getSilkajBalance] Récupération du solde pour: 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL:9YE  
> 📝 Format legacy détecté (pubkey:checksum), extraction de la clé Base58: 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL  
> 📡 Solde non trouvé en cache, interrogation Silkaj…  
> 📡 Exécution de la commande Silkaj avec failover automatique  
> [2025-11-06T16:59:11.490Z] [INFO] ✅ Configuration des nœuds chargée depuis /app/config/silkaj/nodes.json  
> [2025-11-06T16:59:11.491Z] [INFO] 🔍 Recherche d’un nœud Ğ1 disponible…  
> [2025-11-06T16:59:11.705Z] [INFO] ✅ Nœud [g1.duniter.org](http://g1.duniter.org) disponible (HTTPS 200, 214ms)  
> [2025-11-06T16:59:11.705Z] [INFO] ✅ Nœud sélectionné: [g1.duniter.org](http://g1.duniter.org) (214ms)  
> [2025-11-06T16:59:11.706Z] [INFO] 📡 Tentative 1/3: [g1.duniter.org](http://g1.duniter.org)  
> [2025-11-06T16:59:13.653Z] [INFO] ✅ Commande Silkaj réussie sur [g1.duniter.org](http://g1.duniter.org)  
> ✅ Réponse Silkaj (stdout): │─────────────────────────────────────│────────────────────────────────────────│  
> │ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │  
> │ │ 2kDcrL:9YE │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Blockchain (unit|relative) │ 1207.6 Ğ1 | 105.19 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Pending transaction (unit|relative) │ 130.11 Ğ1 | 11.33 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Total balance (unit|relative) │ 1337.71 Ğ1 | 116.53 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Total relative to M/N │ 0.07 x M/N │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> 📋 [parseSilkajBalance] Lignes à parser (12): [  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │’,  
> ‘│ │ 2kDcrL:9YE │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Blockchain (unit|relative) │ 1207.6 Ğ1 | 105.19 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Pending transaction (unit|relative) │ 130.11 Ğ1 | 11.33 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Total balance (unit|relative) │ 1337.71 Ğ1 | 116.53 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Total relative to M/N │ 0.07 x M/N │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’  
> ]  
> 📊 [getSilkajBalance] Résultat parsing: {  
> amount: 1337.71,  
> udAmount: 116.53,  
> status: ‘found’,  
> outputPreview: ‘│─────────────────────────────────────│────────────────────────────────────────│\n’ +  
> ‘│ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │\n’ +  
> '│ ’  
> }  
> ✅ Solde sauvegardé dans le cache Redis (TTL: 5 min) - G1: 1337.71, DU: 116.53  
> [2025-11-06T16:59:13.659Z] [INFO] Solde récupéré: 1337.71 Ğ1, 116.53 DU  
> [2025-11-06T16:59:17.188Z] [INFO] Récupération des informations du compte DE SYSTEME (forceRefresh=true)  
> [2025-11-06T16:59:17.280Z] [INFO] Compte système trouvé: Compte DE System (999999)  
> [2025-11-06T16:59:17.280Z] [INFO] Récupération solde pour clé: 8jWwwyRZMrRsidfVpsGd…  
> [2025-11-06T16:59:17.281Z] [INFO] 🔄 Force refresh activé - Invalidation des caches…  
> [2025-11-06T16:59:17.284Z] [INFO] ✅ Caches invalidés, récupération depuis la blockchain…  
> 🔍 [getSilkajBalance] Récupération du solde pour: 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL:9YE  
> 📝 Format legacy détecté (pubkey:checksum), extraction de la clé Base58: 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL  
> 📡 Solde non trouvé en cache, interrogation Silkaj…  
> 📡 Exécution de la commande Silkaj avec failover automatique  
> [2025-11-06T16:59:17.288Z] [INFO] 📡 Tentative 1/3: [g1.duniter.org](http://g1.duniter.org)  
> [2025-11-06T16:59:19.113Z] [INFO] ✅ Commande Silkaj réussie sur [g1.duniter.org](http://g1.duniter.org)  
> ✅ Réponse Silkaj (stdout): │─────────────────────────────────────│────────────────────────────────────────│  
> │ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │  
> │ │ 2kDcrL:9YE │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Blockchain (unit|relative) │ 1207.6 Ğ1 | 105.19 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Pending transaction (unit|relative) │ 130.11 Ğ1 | 11.33 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Total balance (unit|relative) │ 1337.71 Ğ1 | 116.53 UD Ğ1 │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> │ Total relative to M/N │ 0.07 x M/N │  
> │─────────────────────────────────────│────────────────────────────────────────│  
> 📋 [parseSilkajBalance] Lignes à parser (12): [  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │’,  
> ‘│ │ 2kDcrL:9YE │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Blockchain (unit|relative) │ 1207.6 Ğ1 | 105.19 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Pending transaction (unit|relative) │ 130.11 Ğ1 | 11.33 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Total balance (unit|relative) │ 1337.71 Ğ1 | 116.53 UD Ğ1 │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’,  
> ‘│ Total relative to M/N │ 0.07 x M/N │’,  
> ‘│─────────────────────────────────────│────────────────────────────────────────│’  
> ]  
> 📊 [getSilkajBalance] Résultat parsing: {  
> amount: 1337.71,  
> udAmount: 116.53,  
> status: ‘found’,  
> outputPreview: ‘│─────────────────────────────────────│────────────────────────────────────────│\n’ +  
> ‘│ Balance of pubkey │ 8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf │\n’ +  
> '│ ’  
> }  
> ✅ Solde sauvegardé dans le cache Redis (TTL: 5 min) - G1: 1337.71, DU: 116.53  
> [2025-11-06T16:59:19.116Z] [INFO] Solde récupéré: 1337.71 Ğ1, 116.53 DU

**y a pas un moyen d’interroger la blockchain plus en détails ?**

---

<div class="post-metadata">

### Author: ![Moul](https://forum.duniter.org/user_avatar/forum.duniter.org/moul/32/9145_2.png) [@Moul](https://forum.duniter.org/u/Moul)
#### Post date: [6 November 2025 17:45 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/5 "2025-11-06T17:45:54Z")

</div>

Pour plus de détails, tu peux observer le résultat de :

```sh
curl https://g1.duniter.org/tx/history/8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL/pending

```

Sur mon nœud, il n’y a pas de transferts en attente.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [6 November 2025 18:16 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/6 "2025-11-06T18:16:38Z")

</div>

j’ai essayé plusieurs noeud et hop !!! Merci @Moul 🥰 pour la commande, je vais cherché pour trouver d"ou vient cet écart… PS: le Noeud de @kimamila est encore non synchro

```auto
curl https://g1.e-is.pro/tx/history/8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL/pending

```

> **Résumé**
>
> “pending”: [  
> {  
> “version”: 10,  
> “locktime”: 0,  
> “blockstamp”: “879595-00000019A75AD1F405EAD33E8ED2C1FB73CF4383910876C32FBA150B4621066E”,  
> “blockstampTime”: 1762370887,  
> “issuers”: [  
> “GxWEZrK4KmvLPYtjQpqQgXZ1N3wN3kVdFgakWs3oNW6q”  
> ],  
> “inputs”: [  
> “5455:0:T:A8B9E11322ABBE34BE38EEA601144D4388D424A995501C2D39DD087D1C15F681:1”  
> ],  
> “outputs”: [  
> “4941:0:SIG(8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL)”,  
> “514:0:SIG(GxWEZrK4KmvLPYtjQpqQgXZ1N3wN3kVdFgakWs3oNW6q)”  
> ],  
> “unlocks”: [  
> “0:SIG(0)”  
> ],  
> “signatures”: [  
> “h4hj8WH3eFIlXBx5foh6oJLblhLfiQAFpb7wb11RFCuoTBTvdMJdxPwSsLA1Jn9MwSqi+OT7Tf/oa7Y2OeuYCA==”  
> ],  
> “comment”: “”,  
> “hash”: “2DC94A9C1CDD194B7367C6E2831B9E11E24CE86E2AF2DE10D2AEC157CBBFA57D”,  
> “time”: 1762371429,  
> “block\_number”: 879597

---

<div class="post-metadata">

### Author: ![Frederic\_Renault](https://forum.duniter.org/user_avatar/forum.duniter.org/frederic_renault/32/3778_2.png) [@Frederic\_Renault](https://forum.duniter.org/u/Frederic_Renault)
#### Post date: [6 November 2025 19:13 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/7 "2025-11-06T19:13:50Z")

</div>

> [@Moul](#):
>
> `curl https://g1.duniter.org/tx/history/8jWwwyRZMrRsidfVpsGdQGC5zd8pbRDTHrYDwf2kDcrL/pending`

merci pour votre aide ! 🙏

Effectivement, grâce à ces observations avec `curl https://g1.duniter.org/tx/history/.../pending`, j’ai pu identifier un bug dans le calcul du solde “pending” de silkaj.

Le problème était que silkaj comptait incorrectement les transactions **“sending”** (en cours d’envoi) comme faisant partie du solde en attente. Dans mon cas, les transactions “Dividende Epicerie” avec leur change me revenaient gonflaient artificiellement le solde “pending” à 13011 Ğ1, alors qu’en réalité ces transactions sont des paiements sortants non encore confirmés dans la blockchain.

#### 🐛 Bug corrigé dans silkaj v0.20.1

J’ai corrigé le code pour ne compter que les transactions **entrantes** (“pending” et “receiving”) dans le solde en attente, ce qui est cohérent avec l’API Duniter.

#### ✨ Nouvelles fonctionnalités de silkaj v0.20

Cette version apporte également de nombreuses fonctionnalités supplémentaires :

**1. Support de l’authentification par clé privée en ligne de commande :**

```bash

silkaj --private-key "..." money balance

silkaj --seed "..." checksum

```

**2. Format de sortie JSON (`--json`) :**

```bash

silkaj --json money balance <pubkey>

# Retourne du JSON structuré pour usage programmatique

```

**3. Génération de rapports comptables :**

```bash

# Génère un rapport PDF + JSON pour l'année ou le mois

silkaj money history <pubkey> --compta 2024

silkaj money history <pubkey> --compta 03-2024 --compta-output /path/

```

**4. Vue “primal” pour accéder et parcourir les primo transactions :**

```bash

silkaj --json money primal <pubkey>

```

Le code source est disponible ici : **[Fred / silkaj · GitLab](https://git.duniter.org/zicmama/silkaj/)**

Encore merci @joss.rendall pour ton aide précieuse à mettre le doigt sur ce bug ! 🚀

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [6 November 2025 20:15 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/8 "2025-11-06T20:15:03Z")

</div>

> [@Frederic\_Renault](#):
>
> Le problème était que silkaj comptait incorrectement les transactions **“sending”** (en cours d’envoi) comme faisant partie du solde en attente. Dans mon cas, les transactions “Dividende Epicerie” avec leur change me revenaient gonflaient artificiellement le solde “pending” à 13011 Ğ1, alors qu’en réalité ces transactions sont des paiements sortants non encore confirmés dans la blockchain.

Je n’ai pas du tout interprété le résultat comme toi, ces transactions sont, pour moi, tout simplement en échecs, mais encore inscrite sur quelques noeuds…  
Elles ne seront JAMAIS confirmées, et mon CRON a pu relancer celles qui n’étaient pas réellement inscrite en blockchain. De plus, sur mon historique, il n’a eu que 5 ou 6 jours d’echecs de ces versements; donc, env. 5 x 4.86 Ğ1 = 24,30 Ğ1, donc on est loin des 130.11 Ğ1 que je cherche à identifier…  
J’ai juste besoin d’un solde conforme à l’API duniter.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [7 November 2025 06:20 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/9 "2025-11-07T06:20:42Z")

</div>

@Frederic_Renault , je reviens pour ajouter une remarque tout à fait personnelle :

- nous travaillons tous pour mettre à disposition des apps en opensource donc faire un fork n’est pas un souci
- améliorer et corriger ces apps est également vivement apprécié

Mais je ne comprends pas pourquoi :

- tu ne proposes pas une MR sur le repo existant de Silkja
- tu conserves le nom de SILKAJ pour ta nouvelle apps

Avoir deux versions d’une app, sur les repos, qui porte le même nom et donc qui s’exécute avec les même commandes de base, ce n’est pas du travail d’équipe.  
Libre à toi d’utiliser ton propre SILKAJ sur ton système, mais les autres utilisateurs vont devoir faire un choix : ta version ou celle du repo d’origine… trop compliqué pour l’user lambda de gérer les 2 en même temps.

Donc, pour mon cas personnel, je ne vais même pas essayer tes correctifs, et conserver le repo “officiel” de SILKAJ maintenu par @Moul

j’adapte donc, dans mon code, la pattern à rechercher dans la réponse de SILKAJ pour utiliser ‘Blockchain’ au lieu de ‘Total balance’ :

> **balance.ts**
>
> ```auto
> /**
> * Bibliothèque utilitaire - Récupération de soldes Silkaj
> * 
> * Fonction partagée pour récupérer le solde d'un compte Ğ1
> * Utilisable depuis les API routes sans faire de fetch HTTP interne
> */
> 
> import { exec } from 'child_process';
> import { promisify } from 'util';
> import { 
> getSilkajBalanceFromCache, 
> setSilkajBalanceInCache 
> } from '@/lib/redis/silkaj-cache';
> 
> const execAsync = promisify(exec);
> 
> interface BalanceResult {
> success: boolean;
> publicKey: string;
> balance?: {
> amount: number;
> udAmount: number;
> currency: string;
> status: string;
> lastUpdate: string;
> };
> fromCache?: boolean;
> cacheAge?: number;
> error?: string;
> timestamp: string;
> }
> 
> /**
> * Récupère le solde d'un compte Ğ1
> * @param publicKey - Clé publique Ğ1 (avec ou sans checksum)
> * @returns Résultat de la requête de solde
> */
> export async function getSilkajBalance(publicKey: string): Promise<BalanceResult> {
> try {
> console.log(`🔍 [getSilkajBalance] Récupération du solde pour: ${publicKey}`);
> 
> // Extraire la clé publique Base58 (avant les deux points si présent)
> // Silkaj 0.12.1 accepte uniquement la clé Base58 SANS le checksum
> let pubkeyBase58 = publicKey;
> if (publicKey.includes(':')) {
> const [pubkey] = publicKey.split(':');
> pubkeyBase58 = pubkey;
> console.log(`📝 Format legacy détecté (pubkey:checksum), extraction de la clé Base58: ${pubkeyBase58}`);
> }
> 
> if (!pubkeyBase58 || pubkeyBase58.length < 40) {
> return {
> success: false,
> publicKey,
> error: 'Format de clé publique invalide',
> timestamp: new Date().toISOString()
> };
> }
> 
> // 🚀 Vérifier le cache Redis d'abord
> const cachedBalance = await getSilkajBalanceFromCache(publicKey);
> if (cachedBalance) {
> console.log(`✅ Solde trouvé dans le cache Redis (age: ${Math.round((Date.now() - cachedBalance.lastUpdate) / 1000)}s)`);
>       
> return {
> success: true,
> publicKey: publicKey,
> balance: {
> amount: cachedBalance.balance,
> udAmount: cachedBalance.ud || 0,
> currency: 'Ğ1',
> status: 'found',
> lastUpdate: new Date(cachedBalance.lastUpdate).toISOString()
> },
> fromCache: true,
> cacheAge: Math.round((Date.now() - cachedBalance.lastUpdate) / 1000),
> timestamp: new Date().toISOString()
> };
> }
> 
> console.log(`📡 Solde non trouvé en cache, interrogation Silkaj...`);
> 
> // Exécuter la commande Silkaj avec failover automatique
> const baseCommand = `money balance "${pubkeyBase58}"`;
> console.log(`📡 Exécution de la commande Silkaj avec failover automatique`);
> 
> const { executeSilkajWithFailover } = await import('./node-manager');
> const { stdout, stderr } = await executeSilkajWithFailover(baseCommand);
> 
> if (stderr) {
> console.error('Erreur Silkaj (stderr):', stderr);
> }
> 
> if (stdout) {
> console.log('✅ Réponse Silkaj (stdout):', stdout);
> }
> 
> // Parser la réponse de Silkaj
> const balance = parseSilkajBalance(stdout);
>     
> // Log de débogage pour le parsing
> console.log(`📊 [getSilkajBalance] Résultat parsing:`, {
> amount: balance.amount,
> udAmount: balance.udAmount,
> status: balance.status,
> outputPreview: stdout?.substring(0, 200)
> });
> 
> // 🚀 Sauvegarder dans le cache Redis si le solde a été trouvé
> if (balance.status === 'found' || balance.status === 'zero_balance') {
> const cached = await setSilkajBalanceInCache(
> publicKey,
> balance.amount,
> balance.udAmount && balance.udAmount > 0 ? balance.udAmount : null
> );
> if (cached) {
> console.log(`✅ Solde sauvegardé dans le cache Redis (TTL: 5 min) - G1: ${balance.amount}, DU: ${balance.udAmount || 'null'}`);
> }
> }
> 
> return {
> success: true,
> publicKey: publicKey,
> balance: balance,
> fromCache: false,
> timestamp: new Date().toISOString()
> };
> 
> } catch (error: any) {
> console.error('❌ Erreur Silkaj:', error);
>     
> return {
> success: false,
> error: error.message || 'Erreur inconnue',
> publicKey: publicKey,
> timestamp: new Date().toISOString()
> };
> }
> }
> 
> /**
> * Parse la réponse de Silkaj pour extraire le solde
> */
> function parseSilkajBalance(output: string): any {
> try {
> // Analyser la sortie de Silkaj
> const lines = output.split('\n').filter(line => line.trim());
>     
> // Log de débogage pour voir la sortie brute
> console.log(`📋 [parseSilkajBalance] Lignes à parser (${lines.length}):`, lines);
>     
> // Chercher les informations de solde dans la sortie
> let balance = {
> amount: 0,
> udAmount: 0,
> currency: 'Ğ1',
> status: 'unknown',
> lastUpdate: new Date().toISOString()
> };
> 
> // Préférence : valeurs confirmées blockchain (sans pending) si présentes
> let blockchainAmount: number | null = null;
> let blockchainDetected = false;
> 
> // Patterns pour extraire les soldes depuis la sortie Silkaj formatée
> // IMPORTANT : Parcourir TOUTES les lignes pour trouver G1 et DU (peuvent être sur lignes séparées)
> for (const line of lines) {
> // Pattern pour la ligne Blockchain (soldes confirmés sans pending)
> const blockchainMatch = line.match(/Blockchain\s*\(unit\|relative\).*?│\s*(\d+\.?\d*)\s*Ğ1\s*\|\s*(\d+\.?\d*)\s*UD\s*Ğ1/i);
> if (blockchainMatch) {
> blockchainAmount = parseFloat(blockchainMatch[1]);
> const udValue = parseFloat(blockchainMatch[2]);
> balance.amount = blockchainAmount;
> balance.udAmount = udValue;
> balance.status = 'found';
> blockchainDetected = true;
> continue;
> }
> 
> const blockchainG1OnlyMatch = line.match(/Blockchain\s*\(unit\|relative\).*?│\s*(\d+\.?\d*)\s*Ğ1/i);
> if (blockchainG1OnlyMatch) {
> blockchainAmount = parseFloat(blockchainG1OnlyMatch[1]);
> balance.amount = blockchainAmount;
> balance.status = 'found';
> blockchainDetected = true;
> // Continuer pour trouver une éventuelle ligne UD
> continue;
> }
> 
> // Pattern pour "Total balance (unit|relative) │ 17115.21 Ğ1 | 1490.87 UD Ğ1" (format tabulaire)
> const balanceMatch = line.match(/Total balance.*?│\s*(\d+\.?\d*)\s*Ğ1\s*\|\s*(\d+\.?\d*)\s*UD\s*Ğ1/i);
> if (balanceMatch) {
> // Ne surcharger la valeur qu'en absence de ligne Blockchain
> const amountValue = parseFloat(balanceMatch[1]);
> const udValue = parseFloat(balanceMatch[2]);
> if (!blockchainDetected) {
> balance.amount = amountValue;
> balance.udAmount = udValue;
> balance.status = 'found';
> }
> continue; // Continuer pour vérifier s'il y a d'autres formats
> }
>       
> // Pattern pour "Total balance (unit|relative) │ 17115.21 Ğ1" (sans DU sur même ligne)
> const g1Match = line.match(/Total balance.*?│\s*(\d+\.?\d*)\s*Ğ1/i);
> if (g1Match && !balance.amount) {
> balance.amount = parseFloat(g1Match[1]);
> balance.status = 'found';
> // Ne pas break, continuer pour chercher le DU
> }
>       
> // Pattern pour DU sur une ligne séparée : "│ 1490.87 UD Ğ1" ou "1490.87 UD Ğ1"
> const udMatch = line.match(/│\s*(\d+\.?\d*)\s*UD\s*Ğ1|(\d+\.?\d*)\s*UD\s*Ğ1/i);
> if (udMatch) {
> const udValue = parseFloat(udMatch[1] || udMatch[2]);
> if (blockchainDetected) {
> balance.udAmount = udValue;
> } else if (!balance.udAmount) {
> balance.udAmount = udValue;
> }
> if (balance.amount > 0) {
> balance.status = 'found';
> }
> }
>       
> // Pattern alternatif pour DU : "X.XX UD" ou "(X.XX UD)"
> const udMatchAlt = line.match(/\((\d+\.?\d*)\s*UD\)|(\d+\.?\d*)\s*UD(?!\s*Ğ1)/i);
> if (udMatchAlt && !balance.udAmount) {
> const udValue = parseFloat(udMatchAlt[1] || udMatchAlt[2]);
> if (blockchainDetected || !balance.udAmount) {
> balance.udAmount = udValue;
> }
> }
>       
> // Pattern alternatif pour les montants (ex: "100.00 Ğ1" ou "Balance: 100.00")
> const amountMatch = line.match(/(\d+\.?\d*)\s*Ğ1(?!\s*\|)/i);
> if (amountMatch && !balance.amount) {
> balance.amount = parseFloat(amountMatch[1]);
> balance.status = 'found';
> }
> }
> 
> if (blockchainDetected) {
> console.log(`✅ [parseSilkajBalance] Utilisation du solde Blockchain confirmé (pending exclus)`);
> }
> 
> // Si aucun montant trouvé, vérifier si la clé existe
> if (balance.amount === 0 && balance.status === 'unknown') {
> if (output.toLowerCase().includes('no balance') || output.toLowerCase().includes('zero')) {
> balance.status = 'zero_balance';
> } else if (output.toLowerCase().includes('not found') || output.toLowerCase().includes('invalid')) {
> balance.status = 'key_not_found';
> } else {
> balance.status = 'parsing_error';
> }
> }
> 
> return balance;
> 
> } catch (error) {
> console.error('Erreur lors du parsing du solde:', error);
> return {
> amount: 0,
> udAmount: 0,
> currency: 'Ğ1',
> status: 'parsing_error',
> lastUpdate: new Date().toISOString()
> };
> }
> }
> 
> ```

---

<div class="post-metadata">

### Author: ![Frederic\_Renault](https://forum.duniter.org/user_avatar/forum.duniter.org/frederic_renault/32/3778_2.png) [@Frederic\_Renault](https://forum.duniter.org/u/Frederic_Renault)
#### Post date: [7 November 2025 14:49 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/10 "2025-11-07T14:49:36Z")

</div>

Bonjour,

Pour répondre à la question…  
C’est que l’architecture “UPlanet” repose sur une gestion massive de clés publiques,

Silkaj est pensé comme un client pour un humain, avec des interactions. Nos besoins requièrent un système capable de faire du traitement par lots (`batch processing`) pour des millions de clés.  
Des clefs administratives, celles des identité décentralisée pour chaque parcelle de “territoire” (0.01°), les clefs pour les jetons d’usages (MULTIPASS), d’autres pour les jetons de propriété (ZENCard), et de vote (UPassport)… Toutes convertibles en clef IPFS et NOSTR pour former une structure de données synchronisée selon la règle N² entre les stations P2P formant l’essaim ([Astroport.ONE](https://github.com/papiche/Astroport.ONE)). Tu trouveras des infos dans le code et des articles sur [https://www.copylaradio.com](https://www.copylaradio.com)

J’avais échangé avec @Moul sur les évolutions nécessaires pour que Silkaj puisse répondre à ce besoin. Malheureusement, ces demandes d’évolution, n’ont jamais reçu de réactions de sa part. La version [0.20.x](https://git.duniter.org/zicmama/silkaj/) apporte essentiellement l’usage “one line” avec fichier clef et export json

Sans cela comment le client Next.js dialogue avec silkaj ?

NB: (avec l’aide de @poka et de cursor 🤖) j’ai aussi pu soumettre des proto additifs (clef jumelle nostr & gestion de clef geographique) à [GCLI pour anticiper la compatibilité v2](https://git.duniter.org/clients/rust/gcli-v2s/-/merge_requests/49)…

---

<div class="post-metadata">

### Author: ![Moul](https://forum.duniter.org/user_avatar/forum.duniter.org/moul/32/9145_2.png) [@Moul](https://forum.duniter.org/u/Moul)
#### Post date: [24 November 2025 10:02 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/11 "2025-11-24T10:02:11Z")

</div>

En effet, il y a un problème avec ces transferts “pending” sortants qui sont ajoutés au solde total (temporaire et non sûr). Ils devraient être soustraits.  
C’est la partie la plus sensible de Silkaj, utilisée par les commandes `balance` et `transfer` (`history` n’est pas affectée). Tout changement doit être fait avec des pincettes. Le solde pris en compte pour l’émission d’un transfert prends en compte les transferts en attente. Les transferts bloqués en attente est rare dans le cas de l’interaction humaine sauf si un service régulier de transferts est mis en place. Le problème que je vois est que soit considéré que le solde soit plus élevé qu’en réalité. Si quelqu’un souhaite envoyer tout son solde ou presque, Duniter v1 ne va pas permettre un transfert qui consomme plus de sources que celles disponibles. Donc une erreur sera levée.  
C’est un cas assez rare, qui a lieu pour les services qui émettent beaucoup de transferts produisant des transferts en attente. Puis, un transfert consommant quasiment ou toutes les sources disponibles, ferait une erreur de la part de Duniter v1.  
Ça n’est pas un bug majeur. Du coup, je n’ai pas trop envie de le corriger, étant donné que la migration v2 ne devrait plus trop tarder.

> [@joss.rendall](#):
>
> j’adapte donc, dans mon code, la pattern à rechercher dans la réponse de SILKAJ pour utiliser ‘Blockchain’ au lieu de ‘Total balance’

Attention, le champ `Blockchain` est présent uniquement lorsqu’il y a des transferts temporaires.

* * *

Je suis d’accord que ça fait comme si tu publies au nom du projet Silkaj. Je considère que tu es de bonne foi et de bonne intention. Fred a proposé une MR sur le dépôt, donc pas de souci. Étant l’auteur et le mainteneur du projet Silkaj, je possède le droit d’auteur sur cette œuvre. Les licences libres sont fondées sur le droit d’auteur classique. Après, je suppose que dans les licences libres il est stipulé que s’il y a fork officiel, ce dernier doit changer de nom. Ou du moins ça va de soi. Bon après ça n’est pas le cas ici.

* * *

En réalité, je ne suis pas fan que le solde soit consommé via le parsing de ce qu’affiche Silkaj. L’interface est faite pour l’humain. Je fournis aucune garantie qu’elle ne change pas. Un petit changement peut casser votre parsing, comme a surement dû expérimenter Fred par le passé. Passer par une sortie `json` est une bien meilleure approche. Ça ça concerne la v1 où obtenir le solde nécessite de le calculer, ce qui complique la tâche. En v2, il est plus simple d’[obtenir le solde via l’API RPC](https://forum.duniter.org/t/proposition-pour-obtenir-facilement-le-solde-dun-compte-sans-indexeur/13154). Je vous conseille de la récupérer directement dans vos apps via cette API.

* * *

Durant le dev talk de novembre, tu te plaignais que je n’avais pas pris en compte tes souhaits d’avoir une sortie json sur Silkaj pour tes besoins. Pour ma part, la sortie `json` ça n’a pas été très haut dans mes priorités. Il y a déjà eu une [tentative d’ajouter la sortie `json`](https://git.duniter.org/clients/python/silkaj/-/issues/258), mais elle n’a pas abouti, car pas portée jusqu’au bout.  
On peut y travailler ensemble. Par contre, tu devras faire des efforts. Sinon, je veux bien m’en occuper en échange d’une rémunération.

Concernant ta MR, je ne peux pas l’accepter en l’état. Elle mélange trop de sujets : sortie json, fonctionnalité de `--compta` pour ton usage personnel, authentification, correctif transferts en attente, release. De plus, il y a beaucoup de contenu généré par LLM dont je ne souhaite pas me taper la lecture.

Je suis en train de bosser pour sortir la v0.20 qui est compatible avec l’écosystème v2.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [29 November 2025 07:15 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/12 "2025-11-29T07:15:08Z")

</div>

> [@Moul](#):
>
> Je suis en train de bosser pour sortir la v0.20 qui est compatible avec l’écosystème v2.

❤️‍🔥

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [11 February 2026 18:39 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/13 "2026-02-11T18:39:11Z")

</div>

> [@Moul](#):
>
> Je suis en train de bosser pour sortir la v0.20 qui est compatible avec l’écosystème v2.

Ola @Moul ! comme le 8 Mars se rapproche, je reviens vers toi pour savoir si la version de SILKAJ compatible avec la v2 est disponible pour des tests ?

Comme tu le sais, mon app utilise SILKAJ en Backend pour envoyer le ticket de paiement unique des PMA sur plusieurs comptes grâce à ta super fonction **“Multi-recipients transfers”**

Je dois préparer la migration de mon app. donc un petit topo de ton avancement serait grandement apprécié 🙏

---

<div class="post-metadata">

### Author: ![Moul](https://forum.duniter.org/user_avatar/forum.duniter.org/moul/32/9145_2.png) [@Moul](https://forum.duniter.org/u/Moul)
#### Post date: [12 February 2026 09:56 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/14 "2026-02-12T09:56:18Z")

</div>

Olà Joss ! Merci de venir aux nouvelles du développement de Silkaj !

J’ai décidé d’arrêter l’effort de porting pour pouvoir sortir au plus tôt une version de Silkaj compatible avec l’écosystème Duniter v2. Silkaj v0.20.0 sortira avec les fonctionnalités suivantes :

> **[0.20.0 · Milestones · clients / python / silkaj · GitLab](https://git.duniter.org/clients/python/silkaj/-/milestones/18#tab-issues)**
>
> Parallel maintenance topics

Il me reste l’écriture de tests pour la commande `network-check`, puis préparer la release.

* * *

> **[Migrate to Duniter v2 ecosystem: Polkadot-SDK, Subsquid/PostGraphile (#429) ·...](https://git.duniter.org/clients/python/silkaj/-/issues/429)**
>
> Duniter v2 Substrate support with polkadot-sdk: APIs: RPC, and GraphQL Hasura Resources

Ton usage semble être les transferts à plusieurs bénéficiaires. J’ai implémenté les transferts basiques que je ne publierais pas dans la v0.20. Il manque l’implémentation des références de transfert, la gestion du multi-bénéficiaires (batch), et l’écriture de tests. Je ferais une release v0.21.0 dédiée uniquement pour la commande `money transfer`, qui sera complète. C’est ma priorité, puis ce sera au tour de DeathReaper.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [12 February 2026 17:44 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/15 "2026-02-12T17:44:29Z")

</div>

Merci de ton retour @Moul 🙏  
Je te prie de m’excuser par avance, mais je n’ai pas bien compris ton calendrier entre les fonctions de la v0.20 et v0.21.0…

Désolé de revenir sur les fonctions que j’utilise principalement :

| - Version - | v0.20.0 | v0.21.0 |
| --- | --- | --- |
| Date prévisionnelle de sortie | ? | ? |
| `silkaj authentication` | | |
| `silkaj money balance` | | |
| `silkaj money history --csv` | | |
| `silkaj money transfer --file` | | |

---

<div class="post-metadata">

### Author: ![Moul](https://forum.duniter.org/user_avatar/forum.duniter.org/moul/32/9145_2.png) [@Moul](https://forum.duniter.org/u/Moul)
#### Post date: [12 February 2026 21:00 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/16 "2026-02-12T21:00:56Z")

</div>

J’ai rempli le tableau.

---

<div class="post-metadata">

### Author: ![joss.rendall](https://forum.duniter.org/user_avatar/forum.duniter.org/joss.rendall/32/8566_2.png) [@joss.rendall](https://forum.duniter.org/u/joss.rendall)
#### Post date: [13 February 2026 06:10 UTC](https://forum.duniter.org/t/nouveau-client-en-next-js-et-silkaj-v0-12-1/13437/17 "2026-02-13T06:10:06Z")

</div>

merci de ces précisions, je reste attentif à ton travail. 😎
