Fermer

juillet 18, 2018

Communication inter-contrat et vente de jetons –


Dans partie 4 de cette série de tutoriels sur la construction de DApps avec Ethereum, nous avons commencé à construire et tester notre contrat DAO. Maintenant, allons plus loin et ajoutons du contenu et des jetons à l'histoire, selon notre introduction .

Adding Tokens

Pour qu'un contrat puisse interagir avec un autre contrat, il doit être au courant de l'interface de cet autre contrat – les fonctions à sa disposition. Puisque notre jeton TNS a une interface assez simple, nous pouvons l'inclure comme tel dans le contrat de notre DAO, au-dessus de la déclaration contrat StoryDao et sous nos déclarations importation : contrat

 LockableToken est propriétaire
    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);
    function approveAndCall (adresse _spender, uint256 _value, octets _data) public payable returns (bool);
    function transferAndCall (adresse _to, uint256 _value, octets _data) public payable returns (bool);
    function transferFromAndCall (adresse _from, adresse _to, uint256 _value, octets _data) public payable returns (bool);

    function increaseLockedAmount (adresse _owner, uint256 _amount) public returns (uint256);
    function decreaseLockedAmount (adresse _owner, uint256 _amount) public returns (uint256);
    function getLockedAmount (adresse _owner) affiche les retours publics (uint256);
    function getUnlockedAmount (adresse _owner) affiche les retours publics (uint256);
}

Notez que nous n'avons pas besoin de coller dans la "viande" des fonctions, mais seulement leurs signatures (squelettes). C'est tout ce qui est nécessaire pour interagir entre les contrats.

Maintenant, nous pouvons utiliser ces fonctions dans le contrat DAO. Le plan est le suivant:

  • lancer le jeton (nous l'avons déjà fait)
  • lancer le DAO à partir de la même adresse
  • envoyer tous les jetons du lanceur de jetons au DAO, puis transférer la propriété sur le contrat à le DAO lui-même
  • à ce point le DAO possède tous les jetons et peut les vendre aux personnes utilisant la fonction de transfert, ou peut les réserver pour dépenser en utilisant la fonction d'approbation (utile pendant les votes), etc.

DAO sait à quelle adresse le jeton est déployé? Nous le disons.

Tout d'abord, nous ajoutons une nouvelle variable en haut du contrat DAO:

 JetonToken public;

Ensuite, nous ajoutons quelques fonctions: constructeur

 (adresse _token) public {
    require (_token! = address (0), "L'adresse du jeton ne peut pas être une adresse nulle");
    jeton = LockableToken (_token);
}

Le constructeur est la fonction qui est appelée automatiquement lorsqu'un contrat est déployé. C'est utile pour initialiser des valeurs comme des contrats liés, des valeurs par défaut, etc. Dans notre cas, nous allons l'utiliser pour consommer et enregistrer l'adresse du jeton TNS. Le require check est là pour s'assurer que l'adresse du token est valide.

Pendant que nous y sommes, ajoutons une fonction qui permet aux utilisateurs de vérifier combien de jetons restent à vendre dans le DAO, et le la capacité de changer de jeton en cas de problème, et un tel changement est requis. Ce changement mérite aussi un événement, alors ajoutons cela aussi.

 event TokenAddressChange (token d'adresse);

Fonction daoTokenBalance () public view renvoie (uint256) {
    return token.balanceOf (adresse (this));
}

function changeTokenAddress (adresse _token) onlyOwner public {
    require (_token! = address (0), "L'adresse du jeton ne peut pas être une adresse nulle");
    jeton = LockableToken (_token);
    émet TokenAddressChange (_token);
}

La première fonction est réglée sur view car elle ne change pas l'état de la blockchain; cela ne modifie aucune valeur. Cela signifie qu'il s'agit d'un appel de fonction gratuit en lecture seule pour la blockchain: il n'a pas besoin d'une transaction payée. Il renvoie également la balance des jetons sous la forme d'un nombre, donc cela doit être déclaré sur la signature de la fonction avec returns (uint256) . Le jeton a une fonction balanceOf (voir l'interface que nous avons collée ci-dessus) et accepte un paramètre – l'adresse dont l'équilibre doit être vérifié. Nous vérifions la balance de ce DAO, donc "ceci", et nous transformons "this" en adresse avec address () .

La fonction de changement d'adresse de jeton permet au propriétaire (admin) pour changer le contrat de jeton. Il est identique à la logique du constructeur

Voyons comment nous pouvons laisser les gens acheter les jetons maintenant.

Tokens d'achat

Selon la partie précédente de la série, les utilisateurs peuvent acheter des jetons par:

  • Utilisation de la fonction de repli si elle est déjà en liste blanche. En d'autres termes, il suffit d'envoyer l'éther au contrat DAO.
  • Utiliser la fonction whitelistAddress en envoyant plus que la taxe requise pour la liste blanche.
  • Appel de la fonction buyTokens directement.

mise en garde, cependant. Quand quelqu'un appelle la fonction buyTokens de l'extérieur, nous voulons qu'il échoue s'il n'y a pas assez de jetons dans le DAO pour vendre. Mais lorsque quelqu'un achète des jetons via la fonction de liste blanche en envoyant trop d'informations lors de la première tentative de mise en liste blanche, nous ne souhaitons pas qu'il échoue, car le processus d'ajout de liste blanche sera annulé car tout échouera en même temps. Les transactions dans Ethereum sont atomiques: soit tout doit réussir, soit rien. Nous allons donc faire deux buyTokens fonctions.

 // Cela va en haut du contrat avec d'autres propriétés
uint256 public tokenToWeiRatio = 10000;

function buyTokensThrow (adresse _buyer, uint256 _wei) externe {

    require (liste blanche [_buyer]"Le candidat doit figurer sur la liste blanche.");
    require (! blacklist [_buyer]"Le candidat ne doit pas être mis sur liste noire");

    uint256 jetons = _wei * tokenToWeiRatio;
    require (daoTokenBalance ()> = jetons, "DAO doit avoir suffisamment de jetons à vendre");
    token.transfer (acheteur, jetons);
}

function buyTokensInternal (adresse _buyer, uint256 _wei) interne {
    require (! blacklist [_buyer]"Le candidat ne doit pas être mis sur liste noire");
    uint256 jetons = _wei * tokenToWeiRatio;
    if (daoTokenBalance () <jetons) {
        msg.sender.transfer (_wei);
    } autre {
        token.transfer (acheteur, jetons);
    }
}

Donc, 100 millions de jetons TNS existent. Si nous fixons un prix de 10000 jetons par éther, cela revient à environ 4-5 centimes par jeton, ce qui est acceptable.

Les fonctions effectuent des calculs après avoir fait des vérifications de sécurité contre des utilisateurs bannis et d'autres facteurs, et envoient immédiatement les jetons à l'acheteur, qui peuvent commencer à les utiliser comme ils l'entendent – soit pour voter, soit pour vendre sur des bourses. S'il y a moins de jetons dans le DAO que l'acheteur essaye d'acheter, l'acheteur est remboursé.

La partie token.transfer (_buyer, jetons) utilise le contrat de jeton TNS pour initier un transfert. du emplacement actuel (le DAO) à la destination _buyer pour le montant jetons .

Maintenant que nous savons que les gens peuvent mettre la main sur les jetons, Voyons si nous pouvons implémenter des soumissions.

Structures et soumissions

Selon notre post d'intro, soumettre une entrée coûtera 0,0001 eth fois le nombre d'entrées dans l'histoire déjà. Nous avons seulement besoin de compter les soumissions non supprimées (parce que les soumissions peuvent être supprimées) donc ajoutons les propriétés requises pour cela et une méthode pour nous aider.

 uint256 public submissionZeroFee = 0.0001 ether;
uint256 public nonDeletedSubmissions = 0;

function calculateSubmissionFee () affiche les retours internes (uint256) {
    return submissionZeroFee * nonDeletedSubmissions;
}

Note: La solidité a construit dans le temps et les unités éthérées. En savoir plus sur eux ici .

Cette taxe ne peut être modifiée que par le propriétaire, mais seulement abaissée. Pour augmenter, il faut un vote. Écrivons la fonction de diminution:

 function lowerSubmissionFee (uint256 _fee) onlyOwner external {
    require (_fee <submissionZeroFee, "Les nouveaux frais doivent être inférieurs aux anciens frais.");
    submissionZeroFee = _fee;
    Emission SubmissionFeeChanged (_fee);
}

Nous émettons un événement pour informer tous les clients observant que les frais ont été modifiés, donc déclarons cet événement:

 event SubmissionFeeChanged (uint256 newFee);

Une soumission peut contenir jusqu'à 256 caractères et la même limite s'applique aux images. Seul leur type change. C'est un excellent cas d'utilisation pour une structure personnalisée. Définissons un nouveau type de données.

 struct Submission {
    le contenu des octets;
    image booléenne;
    indice uint256;
    adresse émetteur;
    bool existe;
}

C'est comme un "type d'objet" dans notre contrat intelligent. L'objet a des propriétés de différents types. Le contenu est une valeur de type




Source link