Digital Twins and Cultural Assets

Note: les fichiers créés pour cet exemple sont disponibles sur Github.

Le cas

Les jumeaux numériques sont en pleine croissance et permettent d’informatiser des modèles virtuels issue d’objets, oeuvres, ou quelconques objets physiques avec une grande précision. Dans l’art, les domaines d’applications sont multiples. Cela permet tout d’abord de pouvoir préserver les œuvres et de les garder accessibles quoi qu’il advienne aux originaux.

Combiné aux technologies blockchain et smart contract, il est aussi possible d’envisager la circulation des biens culturels de manière participative (curation collective). En particulier avec l’utilisation de smart contract.

La problématique

Comment écrire et développer un smart contract dans le domaine des biens culturels et quand est-ce qu’un smart contract est valable et contraignant juridiquement ?

Lexique des concepts clés

Biens culturels :

Un bien culturel est un bien faisant partie du patrimoine culturel d’un pays tel que des antiquités, des artéfacts archéologiques, manuscrits anciens, monuments ou vestiges de monuments historiques, œuvres d’art, etc... Il peut posséder une grande valeur historique, archéologique, esthétique, artistique, scientifique, ou bien même technique. Ces biens sont souvent protégés (importations illégales, trafics illicites, …) et préservés au vue de leurs grandes valeurs culturelles.

L’article premier de la Convention pour la protection des biens culturels en cas de conflit armé, signée en 1954 à la Haye, nous donne la définition suivante des biens culturels :

« sont considérés comme biens culturels quels que soient leur origine et leur propriétaire : a) les biens, meubles ou immeubles, qui présentent une grande importance pour le patrimoine culturel des peuples… », [monuments, sites archéologiques, œuvres d’art, manuscrits, livres, collections scientifiques, collections d’archives ou de reproductions des biens, etc.]

» b) les édifices dont la destination principale et effective est de conserver ou d’exposer les biens culturels meubles […], c) les centres comprenant un nombre considérable de biens culturels […] ».

Jumeaux Numériques :

Les jumeaux numériques sont des modèles virtuels qui reproduisent de manière détaillée et précise des objets ou des systèmes réels, en utilisant des technologies de modélisation 3D, d'imagerie et de simulation.

Dans le contexte des biens culturels, les jumeaux numériques sont utilisés pour préserver et documenter des artefacts historiques, artistiques et culturels. Les musées et les institutions patrimoniales peuvent créer des jumeaux numériques de pièces de collection et de sites archéologiques, afin de les rendre accessibles au public et de préserver leur état actuel.

Les jumeaux numériques peuvent également être utilisés pour la recherche et l'éducation, en permettant aux scientifiques, aux historiens et aux étudiants d'explorer virtuellement des artefacts et des sites sans avoir besoin de les manipuler physiquement.

En outre, les jumeaux numériques peuvent être utilisés pour la restauration et la conservation des biens culturels. En créant un modèle virtuel précis d'un objet endommagé, les restaurateurs peuvent expérimenter différentes techniques de réparation et de conservation pour déterminer la meilleure approche avant de procéder à des interventions physiques.

Enfin, les jumeaux numériques peuvent être utilisés pour la reproduction d'objets culturels. En créant un modèle 3D précis d'un objet, il est possible de le reproduire en utilisant des technologies telles que l'impression 3D, ce qui permet de créer des répliques exactes pour la recherche et l'exposition dans les musées et les galeries d'art.

Blockchain :

La blockchain est une technologie de stockage et de transmission d'informations, transparente, sécurisée et fonctionnant sans organe central de contrôle. Elle prend la forme d'une base de données distribuée et contient une liste d'opérations (ou transactions) groupées en blocs, qui sont liés les uns aux autres de manière sécurisée et ordonnée.

Ethereum est une plateforme décentralisée de blockchain, spécialement conçue pour permettre la création et l'exécution de "smart contracts" (contrats intelligents). Un smart contract est un programme informatique qui permet d'exécuter automatiquement des actions ou des transactions selon des conditions prédéfinies, sans avoir besoin d'une autorité centrale ou d'un intermédiaire.

Smart Contract :

Les smart contracts sont des protocoles informatiques qui facilitent, vérifient et exécutent automatiquement des contrats sur une blockchain, éliminant ainsi le besoin d'un intermédiaire. Ils sont auto-exécutables, transparents et décentralisés, ce qui signifie qu'aucune autorité centrale n'est requise pour les valider ou les superviser.

Dans le contexte des biens culturels, les smart contracts offrent une solution pour assurer la traçabilité, la provenance et la gestion des droits d'auteur des jumeaux numériques. De plus, ils peuvent permettre de mettre en place un système de curation collective, où les participants ont la possibilité de voter sur des décisions relatives à la gestion et à la distribution des œuvres d'art numérisées.

Cependant, l'utilisation de smart contracts pour les biens culturels soulève des questions juridiques, notamment en ce qui concerne leur validité et leur force contraignante. Pour être considéré comme légalement contraignant, un smart contract doit respecter les principes généraux du droit des contrats, tels que l'offre, l'acceptation, la contrepartie, la capacité des parties à contracter et l'objet du contrat.

Il est important de souligner que les lois relatives aux contrats varient d'une juridiction à l'autre, et les smart contracts pourraient ne pas être reconnus comme légalement contraignants dans certaines régions. Une attention particulière doit donc être accordée à la conformité avec les lois et réglementations locales pour garantir que les smart contracts soient valables et contraignants d'un point de vue juridique.

Le Livrable

En guise de livrable, nous avons décidé d’utiliser Github pages, qui nous permet de mettre en ligne une page web.

Elle contiendra une description du projet et des concepts clés, mais aussi une “guideline” permettant d’avoir toutes les étapes pour créer et déployer un smart contract sur une blockchain. Elle contiendra aussi un exemple ou template de smart contract basique ainsi que les articles de lois choisis et les explications concernant ce choix de législation.

Cette page nous permettra donc de tout regrouper sur une seule page de sorte à ce qu’un utilisateur qui souhaiterait utiliser notre travail, puisse le faire le plus facilement possible.

Contrat Juridique

Après avoir défini les 2 grands thèmes à aborder, qui sont la validité du contrat ainsi que les droits et obligations liés au token, nous avons établi une liste d'articles régissant différentes conditions pour l’application du smart contract.

Les articles sont présentés ci-dessous:

1. La validité du contrat

La validité du smart contract

Article 1 : “Les parties conviennent que le présent smart contract est valable et exécutoire dans la mesure où il est conforme aux dispositions légales applicables en matière de contrats, notamment en ce qui concerne la liberté contractuelle et la bonne foi. Les parties reconnaissent que le smart contract est un outil automatisé, qui ne peut être modifié après sa mise en place. Par conséquent, les parties s'engagent à fournir toutes les informations nécessaires à la conclusion du contrat de manière claire, précise et exhaustive, et à respecter les termes du contrat dans la mesure où ils sont conformes à la loi et à la bonne foi. Les parties reconnaissent également que toute modification ou résolution du contrat ne peut intervenir qu'avec le consentement mutuel et express des parties concernées, et dans le respect des dispositions légales applicables."

Que faire en cas d’offre entre absents ?

Article 2 : « En cas d’offre entre absents, l’acceptation sera considérée comme étant parvenue à l’offrant au moment où elle est enregistrée sur la blockchain. (art 5 CO) Si l’offrant fixe un délai pour l’acceptation, l’acceptation doit être enregistrée sur la blockchain avant l’expiration du délai pour être valable. Si l’acceptation est enregistrée dans les délais, mais qu’elle n’arrive pas à temps en raison de circonstances indépendantes de la volonté de l’acceptant, l’acceptation sera considérée comme valable si elle est enregistrée sur la blockchain sans délai après la fin des circonstances ». Pourquoi un droit contractuel et pas un droit valeur ?

Article 3 : “L'acquéreur reconnaît et accepte que l'acquisition d'un token ne sera pas considérée comme une transaction de transfert de propriété selon les dispositions du droit suisse. Étant donné que l'œuvre numérique est une chose non tangible, la propriété sur cette dernière ne peut être transférée qu'à travers un droit contractuel enregistré sur la blockchain, en accord avec les dispositions spécifiques prévues dans le contrat. L'acheteur comprend également que l'acquisition d'un token confère uniquement des droits limités sur l'œuvre numérique, qui sont spécifiés dans le contrat. En conséquence, l'acheteur reconnaît que la propriété de l'œuvre numérique reste la propriété de l'artiste ou de l'entité propriétaire, et que l'acheteur n'a aucun droit de propriété sur l'œuvre en dehors des droits limités conférés par l'achat de tokens, en accord avec les termes du contrat. Par exemple, un acheteur acquiert un token représentant une œuvre numérique via une plateforme de vente en ligne. Ce token lui confère des droits limités sur l'œuvre numérique, tels que le droit de le transférer à un tiers ou le droit de le détenir à des fins de spéculation. Cependant, l'acheteur comprend que l'acquisition de ce token ne lui confère pas le droit de propriété sur l'œuvre numérique elle-même, mais uniquement sur les droits spécifiques mentionnés dans le contrat. La propriété de l'œuvre numérique reste donc la propriété de l'artiste ou de l'entité propriétaire, et l'acheteur n'a aucun droit de propriété sur l'œuvre en dehors des droits limités conférés par l'achat de tokens, en accord avec les termes du contrat.”

2. Droits et obligations liés au token - Enjeux éthiques

Acquisition d’un token

Article 4 : “L'acquéreur reconnaît et accepte que l'acquisition du jumeau numérique ne peut se faire que par l'achat de token. Les tokens acquis représentent une partie de la propriété de l'œuvre numérique et confèrent à l'acquéreur des droits limités sur cette œuvre. L'acheteur comprend également que l'acquisition de tokens ne lui confère pas le droit de modifier ou de copier l'œuvre originale. Les tokens ne peuvent être utilisés qu'à des fins de détention de propriété. L'acheteur reconnaît et accepte que la propriété de l'œuvre numérique reste la propriété de l'artiste ou de l'entité propriétaire, et que l'acheteur n'a aucun droit de propriété sur l'œuvre en dehors des droits limités conférés par l'achat de tokens.”

Clause de responsabilité

Article 5 : “Les parties conviennent que le smart contract contenant le token est fourni tel quel, sans garantie d'aucune sorte, expresse ou implicite, y compris, mais sans s'y limiter, les garanties de qualité marchande, d'adéquation à un usage particulier et de non-contrefaçon. Les parties conviennent que le fournisseur du smart contrat ne sera pas responsable des dommages directs, indirects, accessoires, spéciaux, exemplaires ou consécutifs résultant de l'utilisation ou de l'impossibilité d'utiliser le smart contract contenant le token, même si le fournisseur du smart contract a été informé de la possibilité de tels dommages. Les parties conviennent que la responsabilité du fournisseur du smart contract pour toute réclamation découlant de ou liée à ce contrat ne dépassera pas le montant payé par l'utilisateur pour l'utilisation du smart contract contenant le token. Les parties conviennent que le fournisseur du smart contract ne sera pas responsable de toute violation des lois ou réglementations applicables par l'utilisateur. Les parties conviennent que le fournisseur du smart contract n'est pas responsable de la perte ou de la destruction du token résultant de l'utilisation du smart contract, y compris, mais sans s'y limiter, en cas d'erreur de manipulation ou de piratage. En acceptant d'utiliser le smart contract contenant le token, l'utilisateur reconnaît avoir lu, compris et accepté les termes de cette clause de responsabilité”.

Lorsqu’une œuvre est rénovée après numérisation ?

Article 6 : “En cas de rénovation de l'œuvre numérique, par exemple une mise à jour majeure de la plateforme d'hébergement de l'œuvre numérique ou l'ajout de nouvelles fonctionnalités interactives, le propriétaire d'un token se verra automatiquement attribuer une mise à jour de son token, sans coût supplémentaire, pour autant que cette rénovation soit de nature à affecter la valeur ou les caractéristiques essentielles de l'œuvre numérique. Les parties conviennent que la rénovation est considérée comme affectant la valeur ou les caractéristiques essentielles de l'œuvre numérique si elle entraîne une modification substantielle de l'œuvre numérique ou de ses fonctionnalités, ou si elle a un impact significatif sur la performance ou la qualité de l'œuvre numérique. En cas de rénovation, le propriétaire du token sera informé de la mise à jour et de ses caractéristiques. Par exemple, si un artiste numérique décide d'ajouter une nouvelle couche de complexité à l'œuvre numérique en y intégrant une dimension interactive, les propriétaires de tokens seront automatiquement mis à jour sans frais supplémentaires.”

Quid en cas de refus d’actualisation du token ? Article 7 : “Le propriétaire du token s'engage à accepter ces mises à jour ou rénovations, sans droit de veto ou de refus. Le propriétaire du token reconnaît que le refus de mettre à jour ou de rénover l'œuvre numérique peut avoir un impact sur la valeur et la pertinence de son token, et accepte de prendre tous les risques associés à ce refus. Ainsi, dans l'exemple précédent de l'artiste numérique qui ajoute une dimension interactive à l'œuvre numérique, le propriétaire du token ne peut pas refuser la mise à jour sans compromettre la valeur de son token. De même, si une rénovation majeure est effectuée sur la plateforme d'hébergement de l'œuvre numérique pour améliorer la sécurité ou la convivialité, le propriétaire du token ne peut pas s'opposer à la mise à jour sans prendre le risque de voir la pertinence de son token diminuer. En acceptant cette clause, le propriétaire du token reconnaît que les mises à jour et les rénovations sont nécessaires pour assurer la qualité et la pertinence à long terme de l'œuvre numérique, et qu'il doit accepter les conséquences de son refus de mettre à jour ou de rénover son token.”

Limitation du nombre de tokens

Article 8 : "Les parties reconnaissent et acceptent que chaque personne physique ne peut posséder qu'un seul token représentant l'œuvre numérique [X]. Ainsi, si un acheteur achète un token représentant l'œuvre numérique [X], il ne pourra pas acquérir un autre token représentant cette même œuvre. Si l’acheteur tente de posséder plusieurs tokens représentant l'œuvre numérique [X] en utilisant des identités différentes, cela sera considéré comme une violation du contrat. Dans ce cas, l’acheteur pourra être sujet à des mesures disciplinaires, telles que la résiliation du contrat et la révocation de tous les tokens détenus par cette personne. Les parties conviennent également de prendre toutes les mesures raisonnables pour empêcher toute tentative de fraude ou d'usurpation d'identité en lien avec la possession de tokens."

Registre des propriétaires/Identification des propriétaires

Article 9 : “Il est convenu que l'acquéreur consente et admette que toutes les transactions de jetons soient consignées sur la blockchain. Les informations consignées incluront l'identité de l'acheteur, la quantité de jetons acquis et la date de la transaction. Ces données seront disponibles publiquement, permettant de conserver un registre chronologique de tous les propriétaires du jeton depuis sa création. L'acquéreur est également conscient que ces informations ne peuvent être effacées ou modifiées de manière rétroactive sur la blockchain. Il doit reconnaître que ces informations sont accessibles à tous les utilisateurs de la blockchain, mais que son identité peut être protégée en utilisant un pseudonyme ou une adresse électronique non identifiable. En outre, il est accepté que le propriétaire initial de l'œuvre numérique soit responsable de la mise à jour du registre des propriétaires de jetons, et qu'il puisse engager un tiers pour accomplir cette tâche. Il convient d'ajouter que le registre sera tenu par le musée où l'œuvre numérique est exposée, conformément aux termes et conditions régissant l'exposition de l'œuvre. Le musée veillera à ce que le registre soit tenu à jour et que les informations relatives aux propriétaires de jetons soient accessibles au public conformément aux lois et réglementations applicables en la matière. L'acquéreur est invité à prendre connaissance de ces termes et conditions avant d'acheter le jeton.”

Clause en cas de litige

Article 10 : “Tout litige résultant de l'interprétation, de l'exécution ou de la résiliation du présent contrat relatif aux tokens dans le smart contrat sera régi par le présent contrat. Si le litige concerne deux acquéreurs, le musée qui a numérisé le jumeau numérique sera compétent pour résoudre le litige. Toutefois, si le musée est partie au litige, alors c'est le Centre d'arbitrage et de médiation de l'Organisation Mondiale de la Propriété Intellectuelle (OMPI) qui sera compétent pour résoudre le litige. Les parties conviennent également de s'efforcer de résoudre tout différend de manière amiable.”

Le droit de vote ?

Article 11 : “Le DAO permettra aux détenteurs de jetons de voter pour les décisions importantes concernant les activités du DAO. Les décisions importantes incluent, sans s’y limiter, l’adoption de propositions, la modification des statuts du DAO, la distribution de fonds et toutes autres décisions jugées importantes par [...]. Les détenteurs de jetons recevront un nombre de voix proportionnel à leur participation dans le DAO et seront effectifs après avoir atteint un seuil de participation minimum, qui sera déterminé par [...]. Les décisions seront adoptées si une majorité simple de tous les votes exprimés est atteinte”.

Le savoir-faire culturel

Article 12 : “Il est stipulé que tous les propriétaires de jetons liés à l'œuvre culturelle reconnaissent et acceptent que le savoir-faire culturel associé à l'œuvre doit être reconnu et respecté en tout temps. Bien que les propriétaires de jetons aient des droits de vote sur l'œuvre, ceux-ci ne peuvent pas remettre en cause ou altérer le savoir-faire culturel qui lui est associé. Les propriétaires de jetons sont conscients que le savoir-faire culturel est immuable et qu'il doit être préservé pour les générations futures. Les propriétaires de jetons doivent comprendre que leur possession de jetons ne leur confère aucun droit de propriété sur le savoir-faire culturel associé à l'œuvre. Ils doivent respecter et reconnaître que le savoir-faire culturel appartient aux peuples et aux communautés qui l'ont créé, et qu'il est protégé par les lois et réglementations applicables en la matière. En conséquence, les propriétaires de jetons sont tenus de s'engager à préserver et à promouvoir le savoir-faire culturel associé à l'œuvre, dans le respect de la culture, des traditions et des coutumes des peuples et des communautés concernés. Ils doivent également s'engager à ne pas utiliser l'œuvre de manière inappropriée ou offensante pour les peuples et les communautés concernés.”

Reversement des revenus/dividendes

Article 13 : “Les parties au contrat reconnaissent et acceptent que tout revenu ou dividende généré par l'exploitation ou la vente des tokens représentant des biens culturels numériques ne sera pas distribué directement aux détenteurs de tokens. Au lieu de cela, les revenus ou dividendes seront reversés à un organisme de numérisation désigné par les parties au contrat. Cet organisme aura pour mission de financer et de soutenir des projets de numérisation d'œuvres culturelles, ainsi que de promouvoir l'accès à la culture numérique pour tous. Les parties conviennent que les modalités exactes du reversement des revenus ou dividendes à l'organisme de numérisation seront définies dans un accord séparé, qui sera intégré au présent contrat par référence. Les détenteurs de tokens acceptent que leur acquisition de tokens ne leur donne aucun droit sur les revenus ou dividendes générés par l'exploitation ou la vente des tokens. Ils reconnaissent que leur achat de tokens est un soutien à la numérisation et à l'accès à la culture numérique pour tous, et non une transaction d'investissement ou de spéculation financière.”

Clause pénale

Article 14 : “En cas de violation du contrat, [...] pourra demander à [...] un montant de [...] au titre de clause pénale, tout autre moyen de droit étant réservé.”


Smart Contract

Architecture choisie

L’architecture que nous avons choisie est illustrée dans l’image suivante :

  1. Remix est une plateforme qui permet de facilement implémenter, tester et déployer des smart contracts.
  1. Tally et Defender: Tally est une plateforme de gouvernance blockchain pour les DAOs, tandis que Defender fournit des outils pour la gestion des smart contracts. Tally permet aux membres de voter sur les propositions à l'aide de leurs tokens, tandis que Defender fournit des outils pour les développeurs pour construire, déployer et gérer des smart contracts. Il permet aussi aux utilisateurs d’interagir avec les smart contracts.
  1. Solidity est le langage de programmation dédié aux smart contract.
  1. OpenZeppelin est une librairie informatique, permettant d’assister ses utilisateurs à la création de leurs smart contracts. Il est plutôt très commun en informatique de ne pas réinventer la roue. C’est ce que nous permet de faire cette librairie : ne pas ré-implémenter tout le code mais de réutiliser le sien. De plus, cela permet d’éviter de créer des failles de sécurité ou bien même de produire un code moins efficace et rapide. OpenZeppelin nous octroie une sécurité accrue pour nos smart contracts.
  1. Dans le coin inférieur droit, nous avons d’abord IPFS qui propose un service de stockage et de partage de fichier décentralisé, et donc nul besoin de passer par un serveur central comme le fait HTTP. Mais nous pouvons aller plus loin! Pour garantir la sécurité et une persistance des fichiers sur IPFS, Piñata est un autre service de backup qui nous permet aussi d’utiliser plus facilement IPFS

Création du Token smart contract

Pour coder un smart contract, nous allons utiliser le langage de programmation Solidity.

Pour implémenter les fonctionnalités requises il est important de se renseigner sur ce qui existe déjà et d’utiliser des librairies existantes. Les librairies sont généralement à la fois plus sûres et efficientes que de partir de zéro. C’est d’autant plus important pour les smarts contracts pour lesquels la sécurité et le coût d’opération sont primordiaux.

Une librairie populaire est OpenZeppelin, qui possède notamment des modules pour gérer le système de votation (voir Governance). Leurs contrats suivent des standards reconnus qui peuvent être intégrés facilement à d’autres applications.

Ils possèdent également un générateur intégré le Contracts Wizard permettant de créer des templates simples.

En utilisant le wizard fourni par OpenZeppelin, nous pouvons facilement créer un smart contract. Nous allons donc d’abord nous rendre sur ce site Wizard. Vérifiez que c’est bien le protocole ERC721 qui est sélectionné.

Nous avons privilégié le choix du protocole ERC721 (qui sont des NFT) au protocole ERC20 car celui-ci nous fournit plus de libertés sur les tokens créés (comme par exemple récupérer un token sans le consentement). Dans les explications qui suivent le mot token sera employé mais notez bien que ces tokens sont en réalité des NFT.

Dans la colonne de gauche plusieurs choix s’offrent à nous. Cependant, dans notre cas nous allons simplement utiliser:

Voici le code du smart contract pour le token. Il y a plusieurs champs à modifier selon les besoins.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/draft-ERC721Votes.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyToken is ERC721, Pausable, Ownable, EIP712, ERC721Votes {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    // Name of the token and its symbol
    string private _name = "MyFinalToken";
    string private _symbol = "MFT";

    // number of pre-minted tokens
    uint256 public constant MAX_TOKEN_CIRCULATION = 2;

    // Value of each token to be sold in eth
    uint256 public VALUE_PER_TOKEN = 0.001 ether;

    // Fee amount to be paid to the owner on each sale (10% of the sale price)
    uint256 public FEE_AMOUNT = 10;

    // Keep track of the token balances of each address
    mapping(address => uint256) private _tokenBalances;

    // Mapping to store prices of tokens put on sale
    mapping(uint256 => uint256) public tokenPrices;

    // Mapping to store approved buyers for each token
    mapping(uint256 => address) public tokenBuyers;

    // Contract web page or IPFS file
    string private _lawContract;

    // Digital Twin file url or IPFS file
    string private _digitalTwin;


    constructor(string memory lawContract, string memory digitalTwin) ERC721(_name, _symbol) EIP712(_name, "1") {
        // set the base URI
        _lawContract = lawContract;
        _digitalTwin = digitalTwin;
    }

    // Getter function to return the _lawContract
    function getlawContract() public view returns (string memory) {
        return _lawContract;
    }

    // Setter function to change the _lawContract. Only callable by the owner.
    function setlawContract(string memory newLawContract) public onlyOwner {
        _lawContract = newLawContract;
    }

    // Getter function to return the contractWebPage
    function getDigitalTwin() public view returns (string memory) {
        return _digitalTwin;
    }

    // Setter function to change the contractWebPage. Only callable by the owner.
    function setDigitalTwin(string memory newDigitalTwin) public onlyOwner {
        _digitalTwin = newDigitalTwin;
    }

   

    function buyToken() external payable {
        // check if the payment is sufficient
        require(msg.value >= VALUE_PER_TOKEN, "Insufficient payment");
        
        // check if the token supply has reached the maximum
        require(_tokenIdCounter.current() <= MAX_TOKEN_CIRCULATION, "Token supply has reached the maximum");

        // check if the buyer has already bought a token
        require(_tokenBalances[msg.sender] == 0, "You have already bought a token");


        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(msg.sender, tokenId);

        // Transfer the payment to the owner
        payable(owner()).transfer(msg.value);

        // Update the token balance of the buyer
        _tokenBalances[msg.sender] += 1;
    }




    function sellToken(uint256 tokenId, uint256 salePrice, address buyer) external {
        // check that the caller of the function is the owner of the token
        require(msg.sender == ownerOf(tokenId), "Caller is not the owner of the token");

        // check that the salePrice is at least the VALUE_PER_TOKEN
        require(salePrice >= VALUE_PER_TOKEN, "Sale price is less than the minimum value per token");

        // check if the token is already on sale
        require(tokenPrices[tokenId] == 0, "Token is already on sale");

        // check if the buyer has already bought a token
        require(_tokenBalances[buyer] == 0, "Buyer has already bought a token");

        // put the token on sale and define the buyer
        tokenPrices[tokenId] = salePrice;
        tokenBuyers[tokenId] = buyer;
    }

    function buyToken_byID(uint256 tokenId) external payable {
        // check that the token is for sale
        require(tokenPrices[tokenId] != 0, "Token is not for sale");

        // check that the msg.sender is allowed to buy the token
        require(msg.sender == tokenBuyers[tokenId], "You are not allowed to buy this token");

        // calculate the fee to be paid to the owner (10% of the sale price)
        uint256 fee = (tokenPrices[tokenId] * FEE_AMOUNT) / 100;

        // check that the value sent with the function is at least the token price
        require(msg.value >= tokenPrices[tokenId] + fee, string(abi.encodePacked("Payment is not enough. Don't forget the fee which is of 10% of the sale price")));
        

        // get seller before transfer
        address seller = ownerOf(tokenId);

        // transfer the token from the seller to the buyer
        _safeTransfer(seller, msg.sender, tokenId, "");

        // remove the token from sale and buyer's approval
        tokenPrices[tokenId] = 0;
        tokenBuyers[tokenId] = address(0);

        // send the remaining funds to the seller
        payable(seller).transfer(msg.value - fee);

        // send the fee to the owner
        payable(owner()).transfer(fee);

        // Update the token balance of the buyer
        _tokenBalances[msg.sender] += 1;
        _tokenBalances[seller] -= 1;
    }

    
    function retrieveToken(address from, uint256 tokenId) public onlyOwner {
        // Ensure the token exists
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");

        // Transfer token back to owner
        _transfer(from, owner(), tokenId);

        // Update the token balance of the owner
        _tokenBalances[from] -= 1;
    }




    // The following functions are overrides required by Solidity.

    function _afterTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
        internal
        override(ERC721, ERC721Votes)
    {
        super._afterTokenTransfer(from, to, tokenId, batchSize);
    }

        function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
        internal
        whenNotPaused
        override
    {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }

}

Création du governor smart contract

Dans OpenZeppelin, un contrat de gouvernance fait référence à un smart contract conçu pour faciliter la prise de décision décentralisée et les processus de gouvernance au sein d'un système ou d'un protocole. Il fournit un cadre permettant aux parties prenantes de participer au vote, de proposer et d'approuver des changements, et de gérer les paramètres ou les règles du système.

OpenZeppelin propose une bibliothèque appelée "Governor" qui offre un ensemble de contrats intelligents pour construire des mécanismes de gouvernance. Ces contrats permettent aux détenteurs de tokens ou aux membres d'un réseau de prendre collectivement des décisions.

Voici les principaux composants et fonctionnalités que l'on retrouve généralement dans les contrats de gouvernance d'OpenZeppelin :

Les contrats de gouvernance d'OpenZeppelin fournissent une base pour créer des processus de prise de décision transparents, décentralisés et dirigés par la communauté. Ils peuvent être personnalisés et étendus pour répondre aux exigences spécifiques de différents projets, plateformes ou protocoles visant à mettre en œuvre des mécanismes de gouvernance.

Voici le code du gouverneur. Il y a deux champs à modifier selon vos choix:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";



contract MyGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, Ownable {
    
    // Voting delay in blocks. 1 block ~= 12 seconds
    uint256 public constant VOTING_DELAY = 1;

    // Voting period in blocks. 1 block ~= 12 seconds
    uint256 public constant VOTING_PERIOD = 25;

    // Quorum percentage
    uint256 public constant QUORUM_PERCENTAGE = 50;
    
    constructor(IVotes _token)
        Governor("MyGovernor")
        GovernorSettings(VOTING_DELAY, VOTING_PERIOD, 0)
        GovernorVotes(_token)
        GovernorVotesQuorumFraction(QUORUM_PERCENTAGE)
    {}


    // The following functions are overrides required by Solidity.

    function votingDelay()
        public
        view
        override(IGovernor, GovernorSettings)
        returns (uint256)
    {
        return super.votingDelay();
    }

    function votingPeriod()
        public
        view
        override(IGovernor, GovernorSettings)
        returns (uint256)
    {
        return super.votingPeriod();
    }

    function quorum(uint256 blockNumber)
        public
        view
        override(IGovernor, GovernorVotesQuorumFraction)
        returns (uint256)
    {
        return super.quorum(blockNumber);
    }

    function proposalThreshold()
        public 
        view 
        override(Governor, GovernorSettings)
        onlyOwner
        returns (uint256)
    {
        return super.proposalThreshold();
    }
}

Déploiement avec Remix du token smart contract

Allez sur le site de Remix.

Créez un nouveau workspace “blank”. Uploadez les fichiers tokenContract et governorContract. Ouvrez le fichier tokenContract et toutes les dépendances nécessaires vont être ajoutées au workspace automatiquement.

Dans la colonne de gauche cliquez sur Solidity Compiler, allez dans les advanced configurations et cochez la case Enable optimization (laisser 200). Puis cliquez sur Compile tokenContract.sol. Vous aurez ensuite une encoche verte qui apparaîtra.

Puis allez dans l’onglet Deploy & run Transactions (situé sur la gauche). Choisissez l’environnement (on choisira Injected Provider afin de le lier directement à notre Metamask et au bon réseau).

Cliquez ensuite sur le bouton de la flèche qui descend. Il faudra ensuite compléter les deux champs avec les liens vers les fichiers IPFS. Décochez la case Publish to IPFS. Puis cliquez sur le bouton orange transact. Vous verrez dans la case Deployed Contracts l’adresse du contrat publié.

Notez que vous pouvez laisser ces champs vides et les mettre jour par la suite avec les fonctions setDigitalTwin() et setlawContract() . Si vous voulez mettre directement les liens vers les fichiers. Faites ce qui est expliqué dans la section IPFS & Pinata.

Copiez l’adresse du contrat.

Déploiement avec Remix du governor smart contract

Ouvrez le fichier governorContract.sol puis allez dans Solidity Compiler puis cliquez sur le bouton bleu pour compiler. Vous aurez à nouveau une encoche verte.

Ensuite allez dans Deploy & Run Transactions et cliquez sur la flèche qui descend à côté du deploy et collez l’adresse du contrat du token précédemment copié (vous pouvez toujours le copier car il se trouve dans la case en dessous dans Deployed contracts). Puis cliquez sur Transact.

Ajouter le token contract sur Defender

Il faudra ensuite vous rendre sur Defender et cliquer sur le bouton Add Contract puis Import Contract.

Choisissez le nom du contrat, sa blockchain (eth) et ensuite copiez l’adresse du contrat. Enfin cliquez sur Add.

Normalement Defender ne parviendra pas à obtenir le ABI (Application Binary Interface) qui permet la communication entre smart contracts et entités extérieures comme des interfaces ou d’autres smart contracts. Dans ce cas, il faudra aller dans le Solidity Compiler sur Remix et copier le ABI code.

Interagir avec le smart Contract avec Defender

Tout d’abord vérifiez que votre wallet est connecté (en haut à droite).

Pour pouvoir acheter un token, il faudra aller sur Defender, clique sur le token que vous avez ajouté et cliquez sur New Proposal puis sur Admin action.

Choisissez la fonction buyToken, rentrez dans le champ value le prix du token (ici on a choisi 0.001 eth). Cochez la case EOA et choisissez votre portefeuille. Donnez un titre à l’action et cliquez sur Create admin action.

Ensuite cliquez sur Approve and Execute.

Vous aurez donc obtenu votre token (qui est comme dit plus haut, en réalité un NFT). Pour l’ajouter à votre metamask il suffit de copier l’adresse du contrat du token ainsi que de rentrer l'id (qui est 0 vu que c’est le premier NFT minter).

Fonctions disponibles avec notre smart contract:

  1. getlawContract(): Cette fonction est un "getter" qui renvoie le contrat de loi actuel. Il est déclaré comme public view, ce qui signifie qu'il ne modifie pas l'état du contrat.
  1. setlawContract(string memory newLawContract): Cette fonction est un "setter" qui permet de modifier le contrat de loi actuel. Il est uniquement accessible par le propriétaire du contrat (comme indiqué par le modificateur onlyOwner).
  1. getDigitalTwin(): Cette fonction est un "getter" qui renvoie le jumeau numérique actuel. Tout comme getlawContract, cette fonction ne modifie pas l'état du contrat.
  1. setDigitalTwin(string memory newDigitalTwin): Cette fonction est un "setter" qui permet de modifier le jumeau numérique actuel. Elle est uniquement accessible par le propriétaire du contrat.
  1. buyToken(): Cette fonction permet à un utilisateur d'acheter un token. Elle vérifie d'abord si le paiement est suffisant, si la fourniture de tokens n'a pas atteint son maximum, et si l'acheteur n'a pas déjà acheté un token. Si ces conditions sont remplies, elle crée un nouveau token, le transfère à l'acheteur, et met à jour le solde des tokens de l'acheteur.
  1. sellToken(uint256 tokenId, uint256 salePrice, address buyer): Cette fonction permet à un utilisateur de vendre un token. Elle vérifie d'abord que l'appelant de la fonction est le propriétaire du token, que le prix de vente est au moins égal à la valeur par token, que le token n'est pas déjà en vente, et que l'acheteur n'a pas déjà acheté un token. Si ces conditions sont remplies, elle met le token en vente et définit l'acheteur.
  1. buyToken_byID(uint256 tokenId): Cette fonction permet à un utilisateur d'acheter un token spécifique. Elle vérifie d'abord que le token est à vendre et que l'appelant est autorisé à acheter le token. Elle calcule ensuite les frais à payer au propriétaire, et vérifie que le montant envoyé avec la fonction est au moins égal au prix du token plus les frais. Si ces conditions sont remplies, elle transfère le token de l'acheteur au vendeur, retire le token de la vente, envoie les fonds restants au vendeur et les frais au propriétaire, et met à jour le solde des tokens de l'acheteur et du vendeur.
  1. retrieveToken(address from, uint256 tokenId): Cette fonction permet au propriétaire du contrat de récupérer un token spécifique d'un utilisateur. Elle vérifie d'abord que le token existe, puis transfère le token de l'utilisateur au propriétaire du contrat, et met à jour le solde des tokens de l'utilisateur.

Utilisation du governor contract avec Tally

Maintenant il faut aller sur Tally. Cliquez sur Add a DAO.

Puis sur Get Started.

Suivez les étapes pour connecter votre wallet.

Donnez un nom à votre DAO ainsi qu’une description.

Mettez l’adresse du contrat du Governor (que vous pouvez obtenir sur Remix) et choisissez la blockchain et cliquez sur Find Governor Contract.

Il est possible que vous ayez une erreur:

Dans ce cas, il faudra remplir les différents champs. Pour récupérer les Start Block il faut aller sur etherscan. Sur votre wallet et prendre le numéro du block associé à la création des différents contrats (token + gouverneur). Puis cliquer sur Add Governor.

Création d’un vote

Cliquez sur Create new proposal.

Suivez les différentes étapes et remplissez les différents champs (titres, descriptions etc). Pour la partie 3 Add actions. Vous pouvez ne pas choisir d’action et faire un simple vote classique.

À la fin, cliquez sur Submit on-chain.

Une fois la période de délai passée, les votes peuvent commencer. Pour voter il suffit de cliquer sur la proposition de vote sur tally.

Puis de cliquer sur Vote on-chain.

Après le délai de vote passé. On pourra voir le résultat sur la plateforme.

IPFS & Pinata

IPFS, ou InterPlanetary File System, est un protocole et un réseau conçus pour créer une méthode de stockage et de partage de fichiers décentralisé. IPFS permet aux utilisateurs de partager des fichiers directement entre eux, au lieu de passer par un serveur central comme le fait le protocole HTTP traditionnel.

En ajoutant un fichier a IPFS, cela signifie que vous le téléchargez sur ce réseau décentralisé. Lorsque vous ajoutez un fichier à IPFS, vous recevez un hash unique qui sert d'adresse pour le fichier sur le réseau IPFS. Si vous avez un smart contract sur une blockchain, vous pouvez stocker le hash de votre fichier sur la blockchain pour garder l'accès aux données décentralisé et sécurisé, tout en restant accessible et vérifiable.

Voici comment ajouter un fichier à IPFS :

  1. Installez IPFS à partir du site officiel (https://ipfs.io/) et suivez les instructions d'installation.
  1. Ouvrez l'interface desktop de IPFS et vous n’avez plus qu’à ajouter votre fichier en cliquant import
  1. IPFS vous fournira un hash unique pour votre fichier que vous pouvez utiliser pour le référencer sur le réseau IPFS.


Pinata est un service qui facilite l'utilisation d'IPFS. Pinata assure que les fichiers restent disponibles sur le réseau IPFS, fournissant une sorte de 'sauvegarde' pour vos fichiers.

Pour utiliser Pinata avec votre contrat intelligent :

  1. Créez un compte sur Pinata.
  1. Une fois connecté, vous pouvez télécharger votre fichier de contrat intelligent sur Pinata. Ils l'ajouteront au réseau IPFS pour vous et vous fourniront un CID (Content Identifier, similaire à un hash).
  1. Vous pouvez stocker ce CID sur la blockchain, ce qui garantit que votre fichier reste disponible sur le réseau IPFS.
  1. Quiconque possède le CID peut maintenant récupérer le fichier à partir d'IPFS, tout comme avec le hash. La différence est qu'avec Pinata, le fichier est garanti de rester disponible, même si aucun autre nœud sur le réseau ne le stocke.

En résumé, Pinata fournit un service qui rend IPFS plus fiable et plus facile à utiliser car vous pouvez rajoutez tout un dossier d’un trait également. Il est particulièrement utile si vous stockez des données importantes sur IPFS, comme un fichier de contrat intelligent, que vous devez vous assurer de rester disponible.

Accéder au fichier

C’est très simple, vous n’avez qu’à accéder au fichier grâce au CID et le lien donnée par le service IPFS ou Piñata suivant:

💡
https://ipfs.io/ipfs/<CID> https://gateway.pinata.cloud/ipfs/<CID>