En février 2024, nous avons pris part au Hackathon organisé par Alephium. Durant cet événement, nous avons développé.. TipALPH 🥳
Comme on peut le déduire de son nom, TipALPH a un rapport avec les tips (ou pourboires comme dirait Molière) et la blockchain Alephium. Notre but était de créer un bot très simple et intuitif pour permettre aux utilisateurs de s'envoyer des pourboires de $ALPH, la crypto-monnaie principale sur la Blockchain Alephium, s'inspirant de LightningTipBot pour Lightning, également disponible sur Telegram. Une fois le bot fonctionel, nous avons ajouté la gestion des autre tokens de la blockchain Alephium, tel que le $ALF our le $AYIN.
Dans cet article, nous allons détailler le processus par lequel nous sommes passé, comment le code est structuré ainsi que présenter les prochaines étapes pour le développement de TipALPH.
Attention: le code présenté et lié dans cet article est celui de la première version de TipALPH. D'autres fonctionnalités ont été ajoutées depuis, rendant le code plus compliqué à comprendre. C'est pourquoi nous utilisons le code initial, plus simple, ici.
Si vous êtes intéressé à en savoir plus sur le développement des nouvelles fonctionnalités de TipALPH, écrivez-nous pour nous en informer!
Design et conséquences
Comme mentionné, nous voulions que le bot soit très simple et intuitif. Les utilisateurs doivent pouvoir facilement interagir avec le bot pour pouvoir recevoir et envoyer des $ALPH.
Ce désir pousse à choisir une solution « custodial », pour laquelle le bot conserve la clé privée des comptes des utilisateurs. L'alternative, impliquant un porte-feuille « non-custodial », nécessiterait que l'utilisateur y connecte un porte-feuille externe existant et valide chaque pourboire versé. Cette seconde solution complique beaucoup l'utilisation et restreint l'utilisation du bot aux utilisateurs initiés.
Choisir la voie « custodial » implique que les utilisateurs doivent pouvoir déposer et retirer des fonds sur les comptes qui leur sont attribués. Étant donné qu'ils ne possèdent pas la clef privée liée à leurs compte, nous recommandons fortement de ne pas laisser trop d'argent sur ces comptes. En principe, les utilisateurs devraient utiliser le bot pour ce qu'il est: un moyen d'envoyer des pourboires et non des salaires.
Le bot aurait donc un porte-feuille avec une mnémonique associés que nous conserverons très secrètement. Depuis ce porte-feuille, nous dériverions une adresse pour chaque utilisateurs lors de l'enregistrement de celui-ci, en utilisant un chemin de dérivation. Cette adresse contiendrait les fonds pour cet utilisateur.
Les commandes que le bot devraient a minima prendre en charge sont les suivantes:
/start
pour initialiser un compte auprès du bot/tip
pour envoyer des pourboires aux autres utilisateurs. Cette commande doit pouvoir être utilisée avec un montant et un pseudo Telegram, dans un chat mais également uniquement avec un nombre, en réponse à un message d'un utilisateur (qui sera le destinataire du tip)/address
pour afficher l'adresse de l'utilisateur afin qu'il puisse déposer des fonds sur son compte et voir les transactions liées à leur compte sur l'explorer (pour plus de transparence)/balance
pour consulter son solde/withdraw
pour envoyer ses fonds de son compte géré sur une adresse externe. Nécessitera le montant et une adresse de destination
Une commande /help
pour afficher des informations sur les commandes disponibles serait bienvenue.
Maintenant que nous avons défini ce que nous voulons, voyons comment cela peut être implémenté!
Choix technologiques
Notre bot devra communiquer avec un nœud (« fullnode ») Alephium pour accéder au status de la blockchain et y envoyer des transactions. Nous utiliserons notre propre nœud, pour être responsable de sa disponibilité, que nous lancerons depuis un docker compose (les instructions pour cela se trouvent ici). De plus, nous souhaiterions utiliser une librairie pour faciliter la création du bot Telegram.
Voilà donc les deux contraintes majeurs qui guideront le choix du langage que nous utiliserons.
Bien que nous adorions Golang, nous avons opté pour Typescript afin d'utiliser le SDK Web3d'Alephium, qui facilite grandement la communication avec un nœud Alephium. En bonus, il y a plein d'exemples de code pour se connecter à la blockchain, interagir avec les smart contracts dans leur documentation et sur leur dépôt Github (y compris un point de départ pour un tipping bot).
Pour la partie bot Telegram, nous sommes parti avec Telegraf.js, qui est une librairie Javascript populaire. Nous l'avons complété avec le module @telegraf/types pour bénéficier des annotations pour Typescript.
Nous avons fixé la version de Node.js que nous utilisons à la lts/hydrogen (v18.19.0), pour simplifier la collaboration.
Une image Docker sera utilisée pour faciliter le déploiement et les mises à jour.
Finalement, nous aurons besoin d'une base de donnée pour se souvenir des utilisateurs enregistrés ainsi que leur chemin de dérivation. Comme nous sommes de très grands fans de bases de données SQLite car elles sont faciles à déployer et maintenir, nous allons en utiliser pour ce projet.
Gestion des données et schéma de la base de donnée
Chez No Trust Verify, nous sommes profondément concernés par les questions liées au respect de la vie privée. C'est pourquoi nous appliquons le principe de minimisation des données et ne collectons seulement les informations nécessaires pour que l'application puisse faire ce que l'on attend d'elle.
Nous stockons donc, lors de l'enregistrement de l'utilisateur, son pseudo Telegram afin que les autres utilisateurs puissent lui envoyer des pourboires, ainsi que l'identifiant Telegram unique. Nous y associons un identifiant unique, qui est utilisé comme chemin de dérivation pour dériver une adresse depuis le porte-feuille du bot. Comme chaque utilisateur a un identifiant unique, il n'y a aucun risque qu'une adresse d'un utilisateur puisse être utilisé par quelqu'un d'autre.
Pour résumer, nous stockons l'objet User(id, telegramId, telegramUsername)
pour chaque utilisateur. Cet objet est disponible et utilisé dans le code, comme on peut le voir dans les extraits de code ci-dessous.
Structure du code
Le code est séparé en deux parties principales 1 : un client Alephium qui utilise le SDK Web3 pour répercuter les envois de pourboire sur la blockchain et un bot Telegram qui instancie le bot, défini les commandes disponibles et appelle le client Alephium quand nécessaire. Nous les expliquons plus en détail ci-après.
Bien sûr, il y a également un point d'entrée, des fichiers de configuration et des utilitaires mais ceux-ci sont moins intéressants à détailler dans un article de blog. 😉
Bot Telegram
Au lancement du programme, le main
crée une instance d'un bot Telegram et lui fourni les éléments nécessaire à son fonctionnement (token pour le bot Telegram, connexion à la base de donnée,...). Les commandes que nous avons listées plus haut y sont définies pour effectuer les actions que l'on attend en utilisant la base de donnée pour se rappeler des utilisateurs et de leur adresses ainsi que le client alephium pour l'interaction avec la blockchain.
Par exemple, la commande /tip
peut être simplifiée comme suit:
bot.on("/tip", async (ctx: Context<Typegram.Update.MessageUpdate>) => {
// Récupère l'émetteur du tip
const sender = await db.getUserFromTgId(ctx.message.from.id);
// On extrait le montant et destinataire du pourboire envoyé
// ...en utilisant des regex ;)
const args = tipAmountUserRegex.exec(ctx.message.text);
{ amount, receiverUsername } = args.groups;
const receiver = await db.getUserFromTgUsername(receiverUsername);
// On utilise le client alephium pour faire la transaction sur la chaîne.
const txId = alphClient.transferFromUserToUser(sender, receiver, amount);
ctx.sendMessage(`Tip successfull! TxId is {txId}`);
});
La code original complet se trouve ici.
Naturellement, nous faisons bien plus que ce qui affiché ici! Cela ne fonctionne que pour les pourboires mentionnés (par ex. /tip 1 @jane_doe
). Nous avons également omis toutes les vérifications nécessaires (les arguments fournis sont-ils valides? l'émetteur est-il enregistré? est-ce que le destinataire existe? est-il également enregistré? si non, doit-on l'enregistrer avant? ...).
Client Alephium
Dans ce fichier, nous établissons le lien avec le fullnode alephium et définissons des fonctions qui effectuent certaines actions sur la blockchain (transfert de fonds, consolidation des UTXOs,...).
Par exemple, la fonction transferFromUserToUser
utilisée plus haut pourrait être simplifiée ainsi:
async transferFromUserToUser(sender: User, receiver: User, amount: string): Promise<string> {
const senderWallet = this.getUserWallet(sender);
const newTxId = await senderWallet.signAndSubmitTransferTx({
signerAddress: (await senderWallet.getSelectedAccount()).address,
destinations: [{
address: receiver.address,
attoAlphAmount: convertAlphAmountWithDecimals(amount), // Du SDK
}]
})
.then(tx => tx.txId)
.catch((err) => Promise.reject(this.handleError(err)));
await waitTxConfirmed(this.nodeProvider, newTxId, NB_CONFIRMATIONS, 1000);
return newTxId;
}
La code original complet se trouve ici.
Conclusion
Nous avons présenté le processus par lequel nous sommes passé pour le développement de TipALPH, ainsi que du code qui compose la commande /tip
(toutes les autres commandes sont plus simples ou similaires).
Depuis la sortie initiale de TipALPH, nous avons ajouté la gestion des autres tokens de l'écosystème Alephium, tel que le $ALF, le $AYIN ou le $JURA et grandement amélioré les messages affichés par TipALPH.
Comme futures améliorations, nous souhaitons permettre aux utilisateurs de TipALPH d'interagir directement avec des dApps (comme Ayin) et également étendre le bot sur Discord.
Si vous connaissez Typescript et voulez nous aider, vous pourriez nous être utile pour certains problèmes que nous rencontrons. N'hésitez pas à nous contacter!
- Actuellement, chaque partie se trouve dans un fichier dédié, ce qui n'est pas très modulaire et est mal-pratique lors de l'ajout de nouvelles fonctionnalités. Nous travaillons sur une re-structuration du code qui permettra de séparer chaque partie en plusieurs fichiers, facilitant leur utilisation. ↩