Site icon Blog ARC Optimizer

Création, déploiement de jetons TNS –


Dans partie 1 de cette série de didacticiels sur la construction de DApps avec Ethereum, nous avons démarré deux versions d'une blockchain locale pour le développement: une version Ganache et une version complète de PoA privée

. , nous allons y plonger et construire et déployer notre jeton TNS – les utilisateurs de jetons utiliseront pour voter sur les propositions dans le DAO Story.

Prérequis

Avoir une version Ganache opérationnelle, comme dans la partie précédente . Vous pouvez aussi avoir une version locale d'une blockchain en cours d'exécution si vous ne suivez pas la première partie, mais assurez-vous que vous pouvez vous connecter avec les outils dont nous aurons besoin . vous avez une blockchain privée fonctionnelle et la possibilité de taper des commandes dans sa console et le terminal du système d'exploitation via l'application Terminal ou, sur Windows, une application comme Git Bash, Console, Invite CMD, Powershell, etc.

Les dépendances de base

Pour développer notre application, nous pouvons utiliser l'un des nombreux frameworks et kits de démarrage dont nous disposons: Dapp eth-utils Populus ] Embarquez … et ainsi de suite. Mais nous irons avec le roi actuel de l'écosystème, Truffle .

Installez-le avec ce qui suit:

 npm install -g truffle

Cela rendra la commande truffle disponible partout. Maintenant, nous pouvons commencer le projet avec truffle init .

Commencer le Token

Mettons-nous en place et construisons notre jeton. Ce sera un jeton ERC20 à l'emporte-pièce assez standard. (Vous verrez quelle torsion plus bas dans ce post.) D'abord, nous allons tirer dans certaines dépendances. Les librairies OpenZeppelin sont des contrats de solidité de haute qualité, testés lors des combats et utilisables pour étendre et construire des contrats.

 npm install openzeppelin-solidity

Ensuite, créons un nouveau fichier de jetons:

 truffle create contract TNSToken

Le modèle par défaut que la truffe génère ici est un peu périmé, alors mettons-le à jour:

 pragma solidity ^ 0.4.24;

contrat TNStoken {
    constructeur () public {

    }
}

Jusqu'à présent, le constructeur du contrat de jeton était censé s'appeler le même que le contrat lui-même, mais par souci de clarté, il a été changé en constructeur . Il devrait également toujours avoir un modificateur indiquant au compilateur qui est autorisé à se déployer et à interagir avec ce contrat ( public signifiant tout le monde).

SafeMath

Le seul contrat Zeppelin que nous utiliserons dans ce contrat. affaire est leur contrat SafeMath . Dans Solidity, nous importons des contrats avec le mot-clé import alors que le compilateur n'exigera généralement pas de chemin complet, seulement un chemin relatif, comme ceci:

 pragma solidity ^ 0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";

contrat TNStoken {
    en utilisant SafeMath pour uint256;
    constructeur () public {

    }
}

Alors, qu'est-ce que SafeMath ? Il y a longtemps, il y avait un numéro de 184 milliards de bitcoins créés à cause d'un problème mathématique dans le code. Pour éviter les problèmes même à distance similaires à ceux-ci (pas que celui-ci en particulier est possible dans Ethereum), la bibliothèque SafeMath existe. Lorsque deux nombres ont la taille MAX_INT (c'est-à-dire le nombre maximum possible dans un système d'exploitation), leur additionnerait la valeur "wrap around" à zéro, comme si l'odomètre d'une voiture était remis à 0 999999 kilomètres. La bibliothèque SafeMath a donc des fonctions comme celles-ci:

 / **
* @dev Ajoute deux nombres, lance sur débordement.
* /
fonction add (uint256 a, uint256 b) retours internes purs (uint256 c) {
  c = a + b;
  affirmer (c> = a);
  retour c;
}

Cette fonction évite ce problème: elle vérifie si la somme de deux nombres est toujours plus grande que chacune des deux opérandes.

Bien qu'il ne soit pas trop facile de commettre de telles bêtises lors de l'écriture des contrats Solidité, il vaut mieux

En utilisant SafeMath for uint256 nous remplaçons les nombres uint256 standard dans Solidity (256 bits non signés – alias positifs seulement – nombres entiers) avec ces versions "safe". Au lieu de sommer des nombres comme ceci: sum = someBigNumber + someBiggerNumber nous les additionnerons comme ceci: sum = someBigNumber.add (someBiggerNumber) étant ainsi en sécurité dans nos calculs.

ERC20 from Scratch

Avec nos maths sécurisées, nous pouvons créer notre jeton.

ERC20 est un standard avec une interface bien définie, donc pour référence, ajoutons le dans le contrat. Lisez à propos de les standards de jetons ici .

Donc les fonctions qu'un jeton ERC20 devrait avoir sont:

 pragma solidity ^ 0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";

contrat ERC20 {
    function totalSupply () public view renvoie (uint256);
    function balanceOf (adresse qui) public view renvoie (uint256);
    transfert de fonction (adresse à, valeur uint256) retours publics (bool);
    transfert d'événement (adresse indexée à partir de, adresse indexée à, valeur uint256);
    allocation de fonction (propriétaire de l'adresse, dépense d'adresse) public view returns (uint256);
    function transferFrom (adresse de, adresse à, valeur uint256) public returns (bool);
    fonction approval (adresse spender, valeur uint256) public returns (bool);
    approbation d'événement (propriétaire indexé de l'adresse, adresse indexée, valeur uint256);
}

contrat TNStoken {

    en utilisant SafeMath pour uint256;

    constructeur () public {

    }
}

Cela peut sembler complexe, mais c'est en fait très simple. Ceci est un "répertoire" des fonctions que notre jeton doit avoir, et nous les construisons un par un, en expliquant ce que chacun d'eux veut dire. Considérons l'interface ci-dessus pour notre jeton. Nous verrons comment et pourquoi cela est utile lorsque nous créons l'application Story DAO.

Les balances de base

Commençons. Un jeton est en fait juste un "tableur" dans la blockchain Ethereum, comme ceci:

 | Nom | Montant |
|: - |: - |
| Bruno | 4000 |
| Joe | 5000 |
| Anne | 0 |
| Mike | 300 |

Alors créons un mappage qui est essentiellement identique à une feuille de calcul dans le contrat:

 mappage (adresse => uint256) soldes;

Selon l'interface ci-dessus, ceci doit être accompagné d'une fonction balanceOf qui peut lire cette table:

 function balanceOf (adresse _owner) public view returns (uint256) {
    soldes de retour [_owner];
}

La fonction balanceOf accepte un argument: _owner est public (peut être utilisé par n'importe qui), est une fonction view (ce qui signifie qu'il est libre d'utiliser – ne nécessite pas de transaction), et renvoie un numéro uint256 le solde du propriétaire de l'adresse envoyée. Le solde des jetons de tout le monde est publiquement lisible.

Quantité totale

Connaissant l'offre totale du jeton est important pour ses utilisateurs et pour les applications de suivi de pièces, définissons donc une propriété de contrat (variable) pour suivre cette fonction et une autre fonction gratuite à travers laquelle lire ceci:

 uint256 totalSupply_;
function totalSupply () public view renvoie (uint256) {
    return totalSupply_;
}

Envoi de jetons

Ensuite, faisons en sorte que le propriétaire d'un certain nombre de jetons puisse les transférer à quelqu'un d'autre. Nous voulons également savoir quand un transfert a eu lieu, nous allons donc définir un événement de transfert. Un événement de transfert nous permet d'écouter les transferts dans la blockchain via JavaScript, afin que nos applications puissent savoir quand ces événements sont émis au lieu de vérifier constamment manuellement si un transfert s'est produit. Les événements sont déclarés avec des variables dans un contrat et émis avec le mot-clé emit . Ajoutons ce qui suit dans notre contrat maintenant:

 Transfert d'événement (adresse indexée de, adresse indexée à, valeur uint256);

transfert de fonction (adresse _to, uint256 _value) public returns (bool) {
    require (_to! = adresse (0));
    require (_value <= soldes [msg.sender]);

    soldes [msg.sender] = soldes [msg.sender] .sub (_value);
    soldes [_to] = soldes [_to] .add (_value);
    Emit Transfer (msg.sender, _to, _value);
    retourner vrai;
}

Cette fonction accepte deux arguments: _to qui est l'adresse de destination qui recevra les jetons, et valeur qui est le nombre de jetons. Il est important de se rappeler que la valeur est le nombre des plus petites unités du jeton, pas des unités entières. Donc, si un jeton est déclaré avoir 10 décimales, alors pour envoyer un jeton, vous enverrez 10000000000. Ce niveau de granularité nous permet de traiter des montants minuscules.

La fonction est publique, ce qui signifie que tout le monde peut l'utiliser. et les utilisateurs – et il renvoie true si l'opération a réussi.

La fonction effectue ensuite des vérifications de cohérence. D'abord, il vérifie que l'adresse de destination n'est pas une adresse nulle. En d'autres termes, les jetons ne doivent pas être oubliés. Ensuite, il vérifie si l'expéditeur est autorisé à envoyer autant de jetons en comparant leur solde ( soldes [msg.sender]) avec la valeur transmise pour l'envoi. Si l'une de ces vérifications échoue, la fonction rejettera la transaction et échouera. Il restituera tous les jetons envoyés, mais le gaz dépensé pour l'exécution de la fonction jusqu'à ce point aura été dépensé.

Les deux lignes suivantes soustraient le montant des jetons du solde de l'expéditeur et ajoutent ce montant au solde de la destination. L'événement est ensuite émis avec emit et certaines valeurs sont transmises: l'expéditeur, le destinataire et le montant. Tout client abonné à des événements de transfert sur ce contrat sera maintenant averti de cet événement.

Bon, maintenant nos détenteurs de jetons peuvent envoyer des jetons. Croyez-le ou non, c'est tout ce dont vous avez besoin pour un jeton de base. Mais nous allons au-delà de cela et ajoutons plus de fonctionnalités

Allowance

Parfois, une tierce partie peut être autorisée à se retirer du solde d'un autre compte. Ceci est utile pour les jeux qui pourraient faciliter les achats en jeu, les échanges décentralisés, et plus encore. Nous faisons cela en construisant un mappage multi-dimensionnel appelé allocation qui stocke toutes ces permissions. Ajoutons ce qui suit:

 mapping (address => mapping (address => uint256)) interne autorisé;
approbation d'événement (propriétaire indexé de l'adresse, adresse indexée, valeur uint256);

L'événement est là pour que l'écoute des applications puisse savoir quand quelqu'un a pré-approuvé la dépense de son solde par quelqu'un d'autre – une fonctionnalité utile, et une partie de la norme . avec un autre mappage, qui combine des adresses avec des nombres. Il forme essentiellement une feuille de calcul comme celle-ci:

Ainsi, l'équilibre de Bob peut être dépensé par Marie jusqu'à 1000 jetons et Billy jusqu'à 50 jetons. L'équilibre de Mary peut être dépensé par Bob jusqu'à 750 jetons. Le solde de Billy peut être dépensé jusqu'à 300 jetons par Mary et 1500 par Joe.

Étant donné que cette cartographie est interne elle ne peut être utilisée que par des fonctions de ce contrat et des contrats qui utilisent ce contrat comme

Pour approuver quelqu'un d'autre dépensant de votre compte, vous appelez la fonction approuvez avec l'adresse de la personne autorisée à dépenser vos jetons, le montant qu'ils sont autorisés à dépenser, et dans la fonction que vous utilisez. émettre un événement Approbation :

 function approuver (adresse _spender, uint256 _value) public returns (bool) {
  autorisé [msg.sender][_spender]  = _value;
  Emettre l'approbation (msg.sender, _spender, _value);
  retourner vrai;
}

Nous avons aussi besoin d'un moyen de lire ce qu'un utilisateur peut dépenser du compte d'un autre utilisateur:

 function allow (adresse _owner, address _spender) public view returns (uint256) {
    retour autorisé [_owner][_spender];
}

Donc, c'est une autre lecture seule fonction ( vue ) ce qui signifie qu'elle est libre d'exécution. Il lit simplement le solde restant disponible

Alors, comment peut-on envoyer quelqu'un d'autre? Avec une nouvelle fonction transferFrom :

 function transferFrom (adresse _from, adresse _to, uint256 _value) public returns (bool) {
    require (_to! = adresse (0));
    require (_value <= soldes [_from]);
    require (_value <= autorisé [_from][msg.sender]);

    soldes [_from] = soldes [_from] .sub (_value);
    soldes [_to] = soldes [_to] .add (_value);
    autorisé [_from][msg.sender] = autorisé [_from][msg.sender] .sub (_value);
    émettre un transfert (_from, _to, _value);
    retourner vrai;
}

Comme précédemment, il y a des contrôles de sécurité: l'adresse de destination ne doit pas être une adresse nulle, donc pas d'envoi de jetons à un trou noir. La valeur transférée doit également être inférieure ou égale à non seulement le solde actuel du compte à partir duquel la valeur est transférée, mais également le solde que l'expéditeur du message (l'adresse initiant cette transaction) est toujours autorisé à dépenser pour eux.

Ensuite, le solde est mis à jour et le solde autorisé est synchronisé avec celui avant d'émettre l'événement sur le Transfert

Note: il est possible pour le détenteur de jetons de dépenser des jetons sans le permis cartographie en cours de mise à jour. Cela peut arriver si le détenteur de jeton envoie des jetons manuellement en utilisant transfert . Dans ce cas, il est possible que le détenteur ait moins de jetons que ce que la tierce partie peut lui demander.

Avec les approbations et les allocations en place, nous pouvons également créer des fonctions qui permettent à un détenteur de jetons d'augmenter. diminuer l'allocation de quelqu'un, plutôt que d'écraser la valeur entièrement. Essayez de le faire comme un exercice, puis reportez-vous au code source ci-dessous pour la solution.

 function increaseApproval (adresse _spender, uint _addedValue) public returns (bool) {
    autorisé [msg.sender][_spender]  = (
    autorisé [msg.sender][_spender] .add (_addedValue));
    emit Approval (msg.sender, _spender, allowed [msg.sender][_spender]);
    retourner vrai;
}

function increaseApproval (adresse _spender, uint _subtractedValue) public returns (bool) {
    uint oldValue = autorisé [msg.sender][_spender];
    if (_subtractedValue> oldValue) {
        autorisé [msg.sender][_spender]  = 0;
    } autre {
        autorisé [msg.sender][_spender]  = oldValue.sub (_subtractedValue);
    }
    emit Approval (msg.sender, _spender, allowed [msg.sender][_spender]);
    retourner vrai;
}

Constructeur

Jusqu'à présent, nous avons simplement construit un "contrat" ​​symbolique. Mais qu'est-ce que c'est que ce jeton? Comment ça s'appelle? Combien de décimales a-t-il? Comment l'utilisons-nous?

Au tout début, nous avons défini une fonction constructeur . Maintenant, finissons son corps et ajoutons les attributs nom symbole et décimales :

 nom public de la chaîne;
symbole public de chaîne;
uint8 décimales publiques;

constructeur (string _name, chaîne _symbol, uint8 _decimals, uint256 _totalSupply) public {
    nom = _name;
    symbole = _symbol;
    décimales = _decimales;
    totalSupply_ = _totalSupply;
}

Le faire comme ceci nous permet de réutiliser le contrat plus tard pour d'autres jetons du même type. Mais vu que nous savons exactement ce que nous construisons, codons en dur ces valeurs:

 string public name;
symbole public de chaîne;
uint8 décimales publiques;

constructeur () public {
    name = "Le jeton de l'histoire sans fin;
    symbole = "TNS";
    décimales = 18;
    totalSupply_ = 100 * 10 ** 6 * 10 ** 18;
}

Ces informations sont lues par les différents outils et plateformes Ethereum lors de l'affichage des informations du jeton. La fonction constructeur est appelée automatiquement lorsqu'un contrat est déployé sur un réseau Ethereum, de sorte que ces valeurs seront automatiquement configurées au moment du déploiement.

Un mot sur totalSupply_ = 100 * 10 ** 6 * 10 ** 18; : c'est juste un moyen de faciliter la lecture du nombre par les humains. Puisque tous les transferts dans Ethereum sont faits avec la plus petite unité d'éther ou de jeton (y compris les décimales), la plus petite unité a 18 décimales de profondeur dans la virgule décimale. C'est pourquoi un seul jeton TNS est 1 * 10 ** 18 * . En outre, nous voulons 100 millions d'entre eux, donc c'est 100 * 10 ** 6 ou 100 * 10 * 10 * 10 * 10 * 10 * 10 . Cela rend le nombre beaucoup plus lisible que 100000000000000000000000000 .

Flux de développement alternatif

Alternativement, nous pouvons simplement étendre le contrat Zeppelin, modifier certains attributs, et nous avons notre jeton. C'est ce que font la plupart des gens, mais quand j'ai affaire à un logiciel qui gère potentiellement des millions de dollars, j'ai personnellement envie de savoir exactement ce que j'ai mis dans le code, donc la réutilisation de code aveugle est minime

 solidité pragma ^ 0,4,24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../node_modules/openzeppelin-solidity/contracts/token/ERC827/ERC20Token.sol";

contrat TNStoken est ERC20Token {
    en utilisant SafeMath pour uint256;

    nom public de chaîne;
    symbole public de chaîne;
    uint8 décimales publiques;
    uint256 totalSupply_;

    constructeur () public {
        name = "Le jeton de l'histoire sans fin";
        symbole = "TNS";
        décimales = 18;
        totalSupply_ = 100 * 10 ** 6 * 10 ** 18;
    }
}

Dans ce cas, nous utilisons la notation est pour déclarer que notre jeton est ERC20Token . Cela fait que notre jeton étend le contrat ERC20, qui à son tour étend StandardToken, et ainsi de suite …

De toute façon, notre jeton est prêt maintenant. Mais qui obtient combien de jetons pour commencer et comment?

Solde initial

Donnons au fabricant du contrat tous les jetons. Sinon, les jetons ne seront envoyés à personne. Mettez à jour le constructeur en ajoutant la ligne suivante à la fin:

 soldes [msg.sender] = totalSupply_;

Token Locking

Voyant que nous avons l'intention d'utiliser les jetons comme pouvoir de vote (combien de jetons verrous pendant le vote représente la puissance de votre vote), nous devons empêcher les utilisateurs de les envoyer après avoir voté , sinon notre DAO serait vulnérable à une attaque Sybil – une seule personne avec un million de jetons pourrait enregistrer 100 adresses et obtenir le pouvoir de vote de 100 millions de jetons simplement en les envoyant à des adresses différentes et en re-votant avec une nouvelle adresse. Ainsi, nous empêcherons de transférer exactement autant de jetons qu'une personne a consacré au vote, cumulativement pour chaque vote sur chaque proposition. C'est la torsion que nous avons mentionnée au début de ce post. Ajoutons l'événement suivant dans notre contrat:

 événement verrouillé (adresse indexée propriétaire, montant indexé uint256);

Ajoutons ensuite les méthodes de verrouillage:

 function increaseLockedAmount (adresse _owner, uint256 _amount) onlyOwner public returns (uint256) {
    uint256 lockingAmount = verrouillé [_owner] .add (_amount);
    require (balanceOf (_owner)> = lockingAmount, "Le montant de verrouillage ne doit pas dépasser la balance");
    verrouillé [_owner] = lockingAmount;
    Emit Locked (_owner, lockingAmount);
    return lockingAmount;
}

function decreaseLockedAmount (adresse _owner, uint256 _amount) onlyOwner public returns (uint256) {
    uint256 amt = _amount;
    require (verrouillé [_owner]> 0, "Ne peut pas être négatif, déjà à 0 jetons verrouillés.");
    if (amt> locked [_owner]) {
        amt = verrouillé [_owner];
    }
    uint256 lockingAmount = verrouillé [_owner] .sub (amt);
    verrouillé [_owner] = lockingAmount;
    Emit Locked (_owner, lockingAmount);
    return lockingAmount;
}

Chaque méthode s'assure qu'aucune quantité illégale ne peut être verrouillée ou déverrouillée, puis émet un événement après avoir modifié le montant verrouillé pour une adresse donnée. Chaque fonction renvoie également le nouveau montant qui est maintenant verrouillé pour cet utilisateur. Cependant, cela n'empêche pas l'envoi. Modifions transfert et transferFrom :

 transfert de fonction (adresse _to, uint256 _value) public returns (bool) {
    require (_to! = adresse (0));
    require (_value <= soldes [msg.sender] - verrouillé [msg.sender]); // <- CETTE LIGNE EST DIFFERENTE
    // ...

function transferFrom (adresse _from, adresse _to, uint256 _value) public returns (bool) {
    require (_to! = adresse (0));
    require (_value <= soldes [_from] - verrouillé [_from]);
    require (_value <= autorisé [_from][msg.sender] - verrouillé [_from]); // <- CETTE LIGNE EST DIFFERENTE
    // ...

Enfin, nous devons savoir combien de jetons sont verrouillés ou déverrouillés pour un utilisateur:

 function getLockedAmount (adresse _owner) affiche les retours publics (uint256) {
    retour verrouillé [_owner];
}

function getUnlockedAmount (adresse _owner) affiche les retours publics (uint256) {
    soldes de retour [_owner] .sub (verrouillé [_owner]);
}

Voilà: notre jeton est maintenant verrouillable de l'extérieur, mais seulement par le propriétaire du contrat de jetons (qui sera le DAO de l'histoire que nous construirons dans les prochains tutoriels). Faisons en sorte que le contrat de jetons soit propriétaire, c'est-à-dire qu'il permette d'avoir un propriétaire. Importez le contrat Ownable avec l'importation "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";puis modifiez cette ligne:

 contrat StoryDao {

… pour être ceci:

 contrat StoryDao is Ownable {

Code complet

Le code complet du jeton avec des commentaires pour les fonctions personnalisées à ce stade ressemble à ceci:

 pragma solidity ^ 0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
importer "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";

contrat TNStoken est propriétaire

    en utilisant SafeMath pour uint256;

    mappage (adresse => uint256) soldes;
    mapping (adresse => uint256) verrouillé;
    mapping (adresse => mapping (adresse => uint256)) internal autorisé;
    uint256 totalSupply_;

    transfert d'événement (adresse indexée à partir de, adresse indexée à, valeur uint256);
    approbation d'événement (propriétaire indexé de l'adresse, adresse indexée, valeur uint256);
    événement verrouillé (adresse indexée propriétaire, montant indexé uint256);

    nom public de chaîne;
    symbole public de chaîne;
    uint8 décimales publiques;

    constructeur () public {
        name = "Le jeton de l'histoire sans fin";
        symbole = "TNS";
        décimales = 18;
        totalSupply_ = 100 * 10 ** 6 * 10 ** 18;
        soldes [msg.sender] = totalSupply_;
    }

    / **
    @dev _owner sera empêché d'envoyer _amount de jetons. N'importe quoi
au-delà de ce montant sera dépensable.
    * /
    function increaseLockedAmount (adresse _owner, uint256 _amount) public onlyOwner renvoie (uint256) {
        uint256 lockingAmount = verrouillé [_owner] .add (_amount);
        require (balanceOf (_owner)> = lockingAmount, "Le montant de verrouillage ne doit pas dépasser la balance");
        verrouillé [_owner] = lockingAmount;
        Emit Locked (_owner, lockingAmount);
        return lockingAmount;
    }

    / **
    @dev _owner sera autorisé à envoyer à nouveau _amount de jetons. N'importe quoi
restant verrouillé ne sera toujours pas dépensable. Si le _amount est plus grand
que le montant verrouillé, le montant verrouillé est remis à zéro. Ne peut pas être négatif.
    * /
    function decreaseLockedAmount (adresse _owner, uint256 _amount) public onlyOwner renvoie (uint256) {
        uint256 amt = _amount;
        require (verrouillé [_owner]> 0, "Ne peut pas être négatif, déjà à 0 jetons verrouillés.");
        if (amt> locked [_owner]) {
            amt = verrouillé [_owner];
        }
        uint256 lockingAmount = verrouillé [_owner] .sub (amt);
        verrouillé [_owner] = lockingAmount;
        Emit Locked (_owner, lockingAmount);
        return lockingAmount;
    }

    transfert de fonction (adresse _to, uint256 _value) public returns (bool) {
        require (_to! = adresse (0));
        require (_value <= soldes [msg.sender] - verrouillé [msg.sender]);

        soldes [msg.sender] = soldes [msg.sender] .sub (_value);
        soldes [_to] = soldes [_to] .add (_value);
        Emit Transfer (msg.sender, _to, _value);
        retourner vrai;
    }

    function approval (adresse _spender, uint256 _value) public returns (bool) {
        autorisé [msg.sender][_spender]  = _value;
        Emettre l'approbation (msg.sender, _spender, _value);
        retourner vrai;
    }

    function transferFrom (adresse _from, adresse _to, uint256 _value) public returns (bool) {
        require (_to! = adresse (0));
        require (_value <= soldes [_from] - verrouillé [_from]);
        require (_value  oldValue) {
            autorisé [msg.sender][_spender]  = 0;
        } autre {
            autorisé [msg.sender][_spender]  = oldValue.sub (_subtractedValue);
        }
        emit Approval (msg.sender, _spender, allowed [msg.sender][_spender]);
        retourner vrai;
    }

    / **
    @dev Renvoie le nombre de jetons que l'adresse ne peut toujours pas utiliser
    * /
    function getLockedAmount (adresse _owner) public view renvoie (uint256) {
        retour verrouillé [_owner];
    }

    / **
    @dev Renvoie le nombre de jetons que l'adresse est autorisée à envoyer
    * /
    function getUnlockedAmount (adresse _owner) public view renvoie (uint256) {
        soldes de retour [_owner] .sub (verrouillé [_owner]);
    }

    function balanceOf (adresse _owner) public view renvoie (uint256) {
        soldes de retour [_owner];
    }

    function totalSupply () public view renvoie (uint256) {
        return totalSupply_;
    }

    allocation de fonction (adresse _owner, adresse _spender) public view returns (uint256) {
        retour autorisé [_owner][_spender];
    }

}

Conclusion

Cette partie nous a aidé à construire un jeton de base que nous utiliserons comme jeton de participation / partage dans L'histoire sans fin. Alors que le jeton a une utilité c'est par sa définition d'être un actif qui contrôle les décisions d'un plus grand corps qu'un jeton de sécurité . Attention à la différence .

Dans la prochaine partie de cette série, nous apprendrons à compiler, déployer et tester ce jeton.






Source link
Quitter la version mobile