Checklist d’audit de contrat intelligent

Lorsque vous déployez un contrat intelligent sur la blockchain, il devient une composante permanente. Contrairement aux logiciels traditionnels, vous ne pouvez pas publier une mise à jour rapide ou un correctif pour résoudre un problème. Le code est immuable, ce qui signifie qu’il est inchangé une fois qu’il est en ligne. Cette permanence est une arme à double tranchant : elle garantit la transparence et l’absence de confiance, mais ne laisse aucune place à l’erreur.

Les contrats intelligents gèrent souvent des actifs de valeur, ce qui signifie que toute faille dans le code peut avoir de graves répercussions financières. Un minuscule bug n’est pas seulement un problème technique, il peut entraîner des pertes importantes pour vous et vos utilisateurs. Dans certains cas, des vulnérabilités ont été exploitées pour voler des millions de dollars de cryptomonnaie. C’est pourquoi il est essentiel de bien faire les choses dès la première fois.

Assurez la sécurité de vos applications grâce à notre liste de contrôle complète pour l’audit de contrats intelligents. En tant que fournisseur de confiance de services de développement blockchain avec des années d’expérience pratique, nous vous guiderons tout au long du processus de développement et d’audit de contrats intelligents. Dans cet article, nous aborderons :

Considérations de sécurité

Étant donné que les contrats intelligents gèrent souvent des actifs financiers importants et exécutent des fonctions critiques sans intervention humaine, toute vulnérabilité peut avoir des conséquences désastreuses. Contrairement aux logiciels traditionnels, vous ne pouvez pas facilement mettre à jour un contrat intelligent une fois qu’il est déployé, ce qui signifie que les vulnérabilités sont permanentes et exploitables par quiconque. Une seule faille de sécurité peut entraîner des pertes financières massives, éroder la confiance des utilisateurs et endommager irréparablement la réputation de votre projet.

Vulnérabilités de réentrance :

  • Utilisez le modèle « checks-effects-interactions » pour prévenir les attaques par réentrance
  • Évitez de modifier l’état après des interactions externes, car cela ouvre la possibilité d’attaques par réentrance
  • Utilisez des modificateurs comme nonReentrant de ReentrancyGuard d’OpenZeppelin pour empêcher les entrées multiples dans une fonction
  • Utilisez des fonctions comme transfer ou send au lieu de call de bas niveau pour limiter le gas envoyé aux contrats externes, réduisant ainsi les risques de réentrance
  • Minimisez et examinez attentivement tous les appels externes à des contrats non fiables, en vous assurant qu’ils n’introduisent pas de vulnérabilités
pragma solidity ^0.8.0;

contract ChecksEffectsInteractions {

    mapping(address => uint) balances;

    function deposit() public payable {
        balances[msg.sender] = msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount);

        balances[msg.sender] -= amount;

        msg.sender.transfer(amount);
    }
}
Le modèle « checks-effects-interactions » en action : D’abord, la fonction de retrait effectue toutes les vérifications requises (montant disponible), puis applique les effets (changement de solde après le retrait). Ce n’est qu’après qu’elle effectue le transfert réel, empêchant ainsi l’attaque par réentrance

Débordements et sous-débordements d’entiers :

  • Implémentez des bibliothèques SafeMath pour gérer les opérations arithmétiques en toute sécurité
  • Examinez toutes les opérations arithmétiques pour vous assurer qu’elles ne provoqueront pas involontairement de débordements ou de sous-débordements
  • Choisissez des types de variables qui correspondent à la plage de valeurs attendues, réduisant ainsi le risque de débordements
  • Écrivez des tests qui couvrent les cas limites de vos opérations arithmétiques pour détecter rapidement tout problème potentiel
pragma solidity ^0.8.0;

contract SafeMathExample {
    function safeAdd(uint256 a, uint256 b) public pure returns (uint256) {
        uint256 result = a + b; // Overflow check is automatic in Solidity 0.8+
        return result;
    }
}
Utilisation des vérifications de dépassement intégrées à Solidity 0.8+ pour effectuer des additions en toute sécurité sans avoir besoin de bibliothèques externes
pragma solidity >=0.7.0 <0.9.0;

contract SafeMathExample {
    function safeAdd(uint256 a, uint256 b) public pure returns (uint256) {
        uint256 result = a + b;
 assert(c >= a);
        return result;
    }
}
Si le code est censé s’exécuter sur des versions antérieures à 0.8, il faut ajouter une vérification de dépassement

Contrôle d’accès et autorisation :

  • Utilisez des modificateurs d’accès comme onlyOwner pour restreindre l’accès aux fonctions
  • Implémentez un modèle de propriété à l’aide du contrat Ownable d’OpenZeppelin
  • Examinez attentivement toute la logique de contrôle d’accès pour en assurer l’exactitude
  • Utilisez le contrôle d’accès basé sur les rôles (RBAC) si plusieurs niveaux d’autorisation sont nécessaires
  • Initialisez correctement la propriété pour empêcher l’accès non autorisé après le déploiement
  • Évitez de coder en dur les adresses ; utilisez des variables qui peuvent être mises à jour si nécessaire
  • Émettez des événements pour les actions critiques telles que les transferts de propriété pour plus de transparence
  • Protégez les fonctions constructeur et d’initialisation pour empêcher les attaques de réinitialisation
  • Implémentez des exigences de signature multiple pour les fonctions très sensibles
  • Documentez clairement les politiques de contrôle d’accès dans la base de code
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract AccessControlledContract is Ownable {
    uint256 private value;

    // Only the owner can set the value
    function setValue(uint256 _value) public onlyOwner {
        value = _value;
    }

    // Anyone can read the value
    function getValue() public view returns (uint256) {
        return value;
    }
}
Utilisation du contrat Ownable d’OpenZeppelin pour restreindre l’accès à la fonction setValue avec le modificateur onlyOwner

Aléatoire et prévisibilité :

  • Évitez d’utiliser des variables de bloc (par exemple, block.timestamp, blockhash, block.number) pour l’aléatoire
  • Comprenez les limites des sources d’aléatoire sur la chaîne et leur susceptibilité à la manipulation
  • Envisagez d’utiliser des oracles ou des solutions hors chaîne comme Chainlink VRF pour un aléatoire sécurisé et vérifiable
  • Implémentez des schémas commit-reveal pour ajouter une couche de sécurité lorsque l’aléatoire est requis
  • Utilisez des fonctions aléatoires vérifiables (VRF) pour une génération de nombres aléatoires prouvablement juste et inviolable
  • Soyez prudent avec les variables influencées par les mineurs, car ceux-ci peuvent les manipuler dans certaines limites
  • Testez minutieusement l’implémentation de l’aléatoire pour vous assurer qu’elle répond aux exigences de sécurité
  • Restez informé des meilleures pratiques et des vulnérabilités connues liées à l’aléatoire dans les contrats intelligents
  • Documentez l’approche d’aléatoire utilisée dans le contrat pour plus de transparence et d’audits futurs
  • Considérez les compromis entre coût et complexité des solutions d’aléatoire hors chaîne par rapport aux solutions sur chaîne
pragma solidity ^0.8.0;

contract InsecureRandom {
    function getRandomNumber() public view returns (uint256) {
        // Vulnerable to manipulation by miners
        return uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty, msg.sender)));
    }
}
Un aléatoire non sécurisé utilisant des variables de bloc peut être manipulé par les mineurs
pragma solidity ^0.8.0;

contract OddEvenGame {
    uint public yourAnswer;

    function oddOrEven(bool yourGuess) external payable returns (bool) {
        yourAnswer = block.timestamp % 2;

        if (yourGuess == (yourAnswer > 0)) {
            uint fee = msg.value / 10;
            payable(msg.sender).transfer(msg.value * 2 - fee);
            return true;
        } else {
            return false;
        }
    }
}
La variable de timestamp du bloc pourrait être manipulée par les mineurs afin de gagner le jeu à chaque fois

Appels et interactions externes :

  • Examinez tous les appels externes : assurez-vous qu’ils sont nécessaires et correctement sécurisés
  • Implémentez des tests complets : incluez des tests unitaires et d’intégration pour les interactions externes

Attaques par déni de service (DDoS) :

  • Optimisez l’utilisation du gas dans les fonctions pour éviter que les transactions n’échouent en raison du dépassement des limites de gas
  • Évitez les boucles illimitées ou les boucles qui dépendent des entrées utilisateur, ce qui pourrait entraîner des vulnérabilités DDoS
  • Limitez la quantité de données traitées par transaction à des tailles gérables
  • Utilisez le modèle « pull over push » lors de l’envoi d’Ether ou de tokens à plusieurs adresses
  • Implémentez des mécanismes d’arrêt d’urgence (disjoncteurs) pour arrêter les opérations en cas d’attaque
  • Définissez des limites de gas raisonnables et surveillez régulièrement la consommation de gas
  • Méfiez-vous des appels externes qui peuvent consommer un gas excessif ou échouer de manière inattendue
  • Validez et nettoyez toutes les entrées pour éviter une mauvaise utilisation pouvant entraîner un DDoS
  • Utilisez des structures de mappage plutôt que des tableaux lorsque c’est possible pour éviter l’itération sur de grands ensembles de données
  • Testez les cas limites où les fonctions peuvent consommer un gas excessif ou se comporter de manière inattendue
pragma solidity ^0.8.0;

contract InefficientContract {
    uint256[] public data;

    function addData(uint256[] memory _data) public {
        for (uint256 i = 0; i < _data.length; i++) {
            data.push(_data[i]);
        }
    }
}
Une fonction inefficace qui peut consommer un gas excessif lors du traitement de grands tableaux
pragma solidity ^0.8.0;

contract EfficientContract {
    mapping(uint256 => uint256) public data;
    uint256 public dataCount;

    function addData(uint256[] memory _data) public {
        uint256 count = dataCount;
        for (uint256 i = 0; i < _data.length; i++) {
            data[count] = _data[i];
            count++;
        }
        dataCount = count;
    }
}
Une fonction optimisée utilisant des mappages pour gérer les données plus efficacement, réduisant la consommation de gaz

Dépendance aux horodatages :

  • Évitez d’utiliser les horodatages de bloc (block.timestamp) pour une logique critique telle que la génération de nombres aléatoires, les mécanismes de verrouillage ou les conditions basées sur le temps
  • Comprenez les limitations et la manipulation potentielle des horodatages de bloc par les mineurs
  • Utilisez des approches alternatives comme les numéros de bloc pour la logique sensible au temps lorsque c’est possible
  • Implémentez des tampons temporels ou des périodes de grâce pour atténuer les manipulations mineures d’horodatage
  • Soyez prudent lorsque vous utilisez now, car il s’agit d’un alias pour block.timestamp et présente les mêmes risques
  • Documentez toute utilisation d’horodatages de manière approfondie, en expliquant pourquoi elle est sûre ou nécessaire
  • Envisagez d’utiliser des oracles de temps fiables si un chronométrage précis est essentiel pour la fonctionnalité du contrat
  • Testez le comportement du contrat dans différents scénarios d’horodatage pour identifier les vulnérabilités potentielles
  • Restez informé des meilleures pratiques relatives à la dépendance aux horodatages dans les contrats intelligents
  • Examinez les avertissements du compilateur Solidity concernant les vulnérabilités potentielles d’horodatage
pragma solidity ^0.8.0;

contract Auction {
    uint256 public auctionEndTime;
    uint256 constant TIME_BUFFER = 15 minutes;

    constructor(uint256 _biddingTime) {
        auctionEndTime = block.timestamp + _biddingTime;
    }

    function bid() public payable {
        require(block.timestamp <= auctionEndTime + TIME_BUFFER, "Auction already ended");
        // Bidding logic
    }
}
Inclure un tampon temporel pour tenir compte de la manipulation possible des horodatages par les mineurs

Limite de gaz et limite de gaz de bloc :

  • Tirez parti des modèles de codage économes en gaz et des fonctionnalités Solidity
  • Assurez-vous que les fonctions ne dépassent pas la limite de gaz du bloc, ce qui provoquerait l’échec des transactions
  • Évitez les opérations coûteuses en gaz, telles que les boucles illimitées ou les écritures de stockage étendues
  • Optimisez les structures de données et les algorithmes pour réduire la consommation de gaz
  • Utilisez des événements plutôt que le stockage lorsque c’est possible pour économiser sur les coûts de gaz
  • Divisez les grandes fonctions en unités plus petites et gérables si nécessaire
  • Surveillez les coûts de gaz des fonctions pendant le développement et les tests
  • Restez informé des limites de gaz du réseau, car elles peuvent changer avec le temps
  • Tenez compte de l’expérience utilisateur, car des coûts de gaz élevés peuvent dissuader les utilisateurs d’interagir avec votre contrat
function processArray(uint256[] memory data) public {
    for (uint256 i = 0; i < data.length; i++) {
        // Perform operations
    }
}
Une boucle gourmande en gaz : le traitement de grands tableaux dans une seule transaction peut dépasser la limite de gaz du bloc

Qualité du code et bonnes pratiques

En priorisant la lisibilité et la maintenabilité du code, vous améliorerez la qualité de vos contrats intelligents tout en contribuant à un écosystème blockchain plus sécurisé et plus robuste. Un code clair et bien organisé facilitera l’audit, la collaboration et les améliorations futures. Cela garantira en fin de compte que vos contrats restent fiables et efficaces au fil du temps.

Lisibilité et maintenabilité du code :

  • Utilisez des conventions de nommage claires et descriptives pour les variables, les fonctions, les contrats et les événements
  • Appliquez un formatage de code cohérent dans tout le contrat
  • Suivez les guides de style établis, tels que le guide de style officiel de Solidity
  • Utilisez des commentaires et de la documentation pour expliquer la logique et les intentions complexes
  • Organisez le code logiquement, en regroupant les fonctions et les définitions connexes
  • Évitez les structures de code profondément imbriquées pour améliorer la lisibilité
  • Utilisez des modificateurs et des fonctions d’assistance pour réduire la duplication de code
  • Gardez les fonctions concises, en vous concentrant sur une seule responsabilité
  • Tirez parti de l’héritage et des interfaces pour une architecture plus propre
  • Refactorisez régulièrement le code pour améliorer la clarté et supprimer les redondances
// Nested if statements (less readable)
if (condition1) {
    if (condition2) {
        if (condition3) {
            // Do something
        } else {
            // Handle else
        }
    }
}

// Refactored using require statements (more readable)
require(condition1, "Condition1 failed");
require(condition2, "Condition2 failed");
require(condition3, "Condition3 failed");
// Do something
Refactoriser les instructions if imbriquées en une série d’instructions require

Utilisation appropriée des bibliothèques et de l’héritage :

  • Incorporez des bibliothèques fiables telles qu’OpenZeppelin pour les fonctionnalités courantes afin d’améliorer la sécurité et l’efficacité
  • Utilisez l’héritage pour promouvoir la réutilisation et l’organisation du code, rendant les contrats plus modulaires et maintenables
  • Tirez parti des bibliothèques bien testées pour éviter de réinventer la roue et réduire les bugs potentiels
  • Comprenez la fonctionnalité et les limites des bibliothèques que vous intégrez
  • Maintenez les bibliothèques à jour pour bénéficier des correctifs de sécurité et des améliorations
  • Utilisez des interfaces et des contrats abstraits pour définir des comportements standards
  • Évitez les hiérarchies d’héritage profondes qui peuvent compliquer la compréhension du code
  • Soyez prudent avec l’héritage multiple et le problème du diamant en Solidity ; utilisez des modèles tels que le système d’override virtuel de Solidity 0.6+ pour le gérer
  • Importez correctement les bibliothèques et gérez les dépendances en toute sécurité
  • Documentez toute modification personnalisée des bibliothèques externes pour plus de clarté
  • Commentaires et documentation :

    • Documentez toutes les fonctions et la logique complexe de manière approfondie pour améliorer la compréhension et la maintenabilité
    • Incluez des commentaires expliquant le but et la fonctionnalité des segments de code, en particulier pour la logique complexe ou non évidente
    • Utilisez le format Solidity NatSpec (Natural Specification) pour une documentation standardisée
    • Fournissez des descriptions claires des paramètres de fonction et des valeurs de retour
    • Expliquez toute hypothèse, limitation ou effet secondaire associé aux fonctions ou aux blocs de code
    • Maintenez les commentaires à jour avec les modifications du code pour éviter les divergences
    • Évitez les commentaires redondants qui ne font que répéter le code ; concentrez-vous sur le « pourquoi » plutôt que sur le « quoi »
    • Documentez les événements et les modificateurs pour clarifier leur but et leur utilisation
    • Incluez des exemples d’utilisation le cas échéant pour montrer comment appeler les fonctions
    • Maintenez la documentation externe (par exemple, README, Wikis) pour un contexte plus large et des informations au niveau du projet
    /**
     * @dev Emitted when the ownership is transferred.
     * @param previousOwner The address of the previous owner.
     * @param newOwner The address of the new owner.
     */
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner, "Caller is not the owner");
        _;
    }
    
    Documentation des événements et des modificateurs pour clarifier leur objectif et leur utilisation

    Modularité et réutilisation du code :

    • Divisez le code en composants plus petits et réutilisables pour améliorer la lisibilité et la maintenabilité
    • Assurez-vous que les modules sont autonomes et testables, favorisant ainsi un débogage et une vérification plus faciles
    • Adoptez le principe de responsabilité unique (SRP), où chaque module ou fonction a un seul objectif
    • Utilisez des bibliothèques et des contrats pour encapsuler les fonctionnalités courantes
    • Utilisez des interfaces et des contrats abstraits pour définir des contrats clairs entre les composants
    • Implémentez des stratégies de test modulaires, en testant chaque composant indépendamment
    • Encapsulez la fonctionnalité et restreignez l’accès aux composants internes de manière appropriée
    • Favorisez le découplage entre les modules, en réduisant les interdépendances
    • Tirez parti des modèles de conception, tels que les modèles de fabrique ou de proxy, pour organiser le code efficacement
    • Refactorisez régulièrement le code pour améliorer la modularité et éliminer les redondances
    pragma solidity ^0.8.0;
    
    library MathUtils {
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
    
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a <= b ? a : b;
        }
    
        // Additional utility functions...
    }
    
    contract ExampleContract {
        using MathUtils for uint256;
    
        function getMaxValue(uint256 x, uint256 y) public pure returns (uint256) {
            return x.max(y);
        }
    }
    
    Utilisation d’une bibliothèque MathUtils pour fournir des fonctions utilitaires réutilisables entre les contrats

    Tests et validation :

    • Écrivez des tests unitaires complets couvrant divers scénarios d’entrée pour chaque fonction
    • Écrivez des tests d’intégration pour vérifier les interactions entre différents contrats et systèmes externes
    • Visez une couverture de test élevée ; efforcez-vous d’atteindre 100 % lorsque c’est possible

    Conformité et normes :

    • Suivez méticuleusement les normes pertinentes (par exemple, ERC-20, ERC-721) pour assurer la compatibilité
    • Respectez le guide de style officiel de Solidity pour la cohérence
    • Restez informé des dernières meilleures pratiques
    • Incorporez les améliorations validées par la communauté dans votre code

    Pièges courants à éviter :

    • Excès de confiance : faites toujours examiner votre code par d’autres pour déceler les problèmes négligés
    • Sauter la phase de test : allouez suffisamment de temps et de ressources pour des tests approfondis ; ne sautez pas cette phase critique
    • Ignorer les avertissements du compilateur : traitez tous les avertissements du compilateur avant de considérer le code prêt à être déployé
    • Ne pas se tenir au courant des mises à jour : restez informé des derniers développements de la technologie blockchain et des normes de contrats intelligents

    Outils et ressources pour la revue de code

    L’intégration d’outils et de ressources efficaces dans votre flux de développement est essentielle pour créer des contrats intelligents sécurisés et fiables. En utilisant des outils de sécurité automatisés tels que MythX et Slither, en tirant parti des frameworks de développement comme Truffle et Hardhat, et en vous engageant dans une formation continue, vous garantissez que vos compétences et vos projets restent robustes et à jour dans le paysage en évolution rapide de la blockchain.

    Outils d’analyse de sécurité automatisée :

    • Intégrez des outils comme MythX et Slither dans votre flux de développement pour détecter les problèmes potentiels
    • Utilisez des outils d’analyse statique pour détecter les vulnérabilités et les problèmes de qualité du code dès le début
    • Automatisez l’analyse de sécurité dans le cadre de votre pipeline d’intégration continue (CI)
    • Comprenez les limites des outils automatisés et complétez-les par des revues manuelles
    • Restez informé des nouvelles fonctionnalités et mises à jour de ces outils pour exploiter pleinement leur potentiel
    • Utilisez des outils complémentaires tels que Mythril, Echidna et Manticore pour une analyse complète
    • Configurez les outils de manière appropriée pour répondre aux besoins de votre projet et réduire les faux positifs
    • Exécutez régulièrement ces outils tout au long du développement, pas seulement à la fin

    Frameworks de développement et de test :

    • Utilisez des frameworks comme Truffle et Hardhat pour un développement, des tests et un déploiement efficaces
    • Exploitez les environnements de test intégrés pour écrire et exécuter des tests automatisés
    • Utilisez des scripts de déploiement pour gérer le déploiement de contrats sur différents réseaux
    • Tirez parti des outils de débogage fournis par ces frameworks
    • Intégrez des plugins et des extensions pour améliorer les fonctionnalités
    • Gérez efficacement les dépendances à l’aide de gestionnaires de paquets et de fichiers de configuration
    • Utilisez des fonctionnalités de “fork” de réseau pour tester des contrats par rapport aux états de blockchain en direct
    • Implémentez le rapport de gaz pour optimiser les fonctions de contrat
    • Maintenez des structures de projet organisées pour une meilleure gestion du code
    • Restez informé des dernières versions pour bénéficier des nouvelles fonctionnalités et des correctifs de sécurité

    Développer et sécuriser des contrats intelligents

    En tant qu’agence de développement blockchain, nous fournissons des solutions de contrats intelligents de bout en bout, de la conception initiale au déploiement final. Notre expérience pratique dans la création de solutions blockchain sécurisées et efficaces nous donne une perspective unique sur la sécurité des contrats intelligents.

    Développement de Protocoles Innovants : BAMM

    Nous avons développé BAMM, un protocole de transport de niveau 3 optimisé pour transférer de petites quantités de données entre des pairs anonymes. Conçu comme une base pour les applications décentralisées web3, BAMM s’appuie sur le mécanisme de consensus Proof-of-Stake pour valider les transactions. Voici comment BAMM améliore la confidentialité des utilisateurs :

    • Chiffrement de bout en bout : les messages sont chiffrés sur l’appareil de l’expéditeur et restent illisibles par toute personne autre que le destinataire prévu, même s’ils sont interceptés.
    • Intégrité des données : les messages sont garantis d’arriver intacts. Toute falsification les rend illisibles et ils sont rejetés.
    • Routage en oignon : les messages sont enveloppés dans plusieurs couches de chiffrement et acheminés via un réseau de nœuds intermédiaires. Chaque nœud retire une couche, révélant la destination suivante sans exposer le destinataire final. Cela masque les métadonnées de l’utilisateur telles que les adresses IP et les identifiants d’appareils, améliorant la confidentialité et l’anonymat.
    • Proof-of-Stake : chaque nœud doit mettre en jeu une devise avant de rejoindre le réseau, ce qui incite à un comportement honnête et décourage les acteurs malveillants de tenter de compromettre la confidentialité des utilisateurs.

    BAMM facilite la communication directe et sécurisée entre les utilisateurs sans dépendre d’un serveur central. Cette décentralisation élimine un point de défaillance unique et garantit une confidentialité robuste des utilisateurs.

    Création de Dapps Sécurisées : Tingl

    Nous avons également développé Tingl, un messager web3 anonyme qui démontre notre engagement envers la sécurité et la confidentialité. Dans Tingl, les données utilisateur et les métadonnées des messages résident sur la blockchain pour une sécurité maximale, tandis que les fichiers multimédias volumineux sont stockés hors chaîne pour une efficacité accrue. Pour une solution de stockage entièrement décentralisée, l’intégration d’IPFS est également possible.

    Voici un aperçu de ce que nous avons mis en œuvre pour Tingl :

    • Messages payants à la consultation : les fichiers ne deviennent disponibles qu’après le paiement de l’expéditeur. Les utilisateurs peuvent partager en toute sécurité des images et des fichiers jusqu’à 2 Mo.
    • Chat invité : facilitant la communication unique, les utilisateurs de Tingl peuvent héberger un chat invité pour une personne sans compte.
    • Lien web de vente : permettant des transactions sécurisées, les utilisateurs de Tingl peuvent créer un lien web avec des aperçus du contenu et des options pour que les acheteurs puissent acheter le contenu.
    • Superlike : les utilisateurs peuvent envoyer un « superlike » via une transaction payante sur la chaîne, récompensant ainsi l’auteur du message.

    Pourquoi cela compte pour vous

    Notre expérience avec BAMM et Tingl signifie que nous avons navigué directement dans les complexités du développement et de la sécurité des contrats intelligents. Nous comprenons les pièges et les défis auxquels vous pourriez être confrontés, car nous les avons déjà rencontrés nous-mêmes. Cette expertise approfondie nous permet de fournir non seulement des conseils théoriques, mais aussi des solutions pratiques adaptées à vos besoins spécifiques.

    En nous choisissant pour la revue de code de vos contrats intelligents, vous bénéficiez d’une richesse de connaissances et d’expérience. Nous savons ce qu’il faut pour créer des applications blockchain sécurisées, efficaces et conviviales. Laissez-nous vous aider à garantir que vos contrats intelligents sont solides comme le roc, afin que vous puissiez vous concentrer sur ce que vous faites de mieux : innover et développer votre entreprise. Contactez-nous dès aujourd’hui pour une consultation gratuite et sans engagement.

    Découvrez comment nous avons développé un messager web3 anonyme avec une confidentialité de chat exclusive

    Veuillez saisir votre adresse courriel professionnelle n'est pas un courriel professionnel