Rejoignez-Nous sur

Voici comment j'ai développé le portefeuille Ethereum le plus rapide. Déjà.

News

Voici comment j'ai développé le portefeuille Ethereum le plus rapide. Déjà.

Je l'appelle le pistolet Ethereum Gatling

Dans cet article, je décris une méthode pour envoyer des quantités arbitraires de transactions arbitraires en vrac au sein d'une transaction unique sur la chaîne Ethereum. Je démontre également la souplesse de la méthode dans la pratique en effectuant les quatre transactions suivantes en une seule:

1. Envoyez 1 Ether à un autre portefeuille
2. Envoyez 1 jeton ERC223 à un autre portefeuille
3. Appeler un contrat sans argument de fonction
4. Appelez un contrat avec des arguments de fonction et une valeur Ether.

Pour une version TLDR, vous pouvez passer directement à la Démonstration d'utilisation section. Pour une version détaillée, vous pouvez continuer à lire ici et je vous guiderai dans le terrier du lapin.

introduction

L'un des principaux problèmes auxquels les blockchains sont confrontées aujourd'hui est leur manque de rapidité. Les transactions sur Ethereum Mainnet peuvent prendre plusieurs minutes, ce qui est inacceptable pour toute application qui aspire à être une véritable DApp (application décentralisée) dans la chaîne.

Dans un monde idéal, nous aurions une blockchain capable d'exploiter toutes les transactions en une fraction de seconde, sans être encombrée par le trafic et suffisamment étendue pour couvrir toute la demande d'espace. Dans un monde aussi idéal, toute interaction avec l'interface utilisateur DApp pourrait être une transaction, tout comme aujourd'hui, il peut s'agir d'un appel d'API.

Malheureusement, nous ne vivons pas dans un monde idéal. Dans quelle mesure pouvons-nous nous approcher de ce scénario idéal avec l’état actuel du Mainnet Ethereum? Je me suis posé la question il y a un moment et j'ai décidé de mener une enquête. Il s'avère que la réponse est la suivante: Plus près que vous ne le pensez.

Bien que nous ne soyons pas en mesure de résoudre le problème de l’espace, nous pouvons le résoudre dans une certaine mesure. Cet article raconte l’écriture du portefeuille Ethereum le plus rapide, celui qui rapproche l’idée de transaction par clic de souris du domaine des possibilités.

Mon but était d’écrire un contrat intelligent, qui aurait une fonction spéciale avec un seul but: recevoir plusieurs transactions encodées dans sa charge utile et virez-les tous en succession rapide au sein d'une même transaction. (Vous pouvez maintenant deviner d'où vient le nom «Ethereum Gatling Gun».) Un tel contrat permettrait de composer les actions d'un utilisateur dans l'interface utilisateur DApp en une séquence d'opérations en chaîne, qui pourraient toutes être déclenchées ensemble lorsque l'utilisateur est connecté. fait des changements.

Ce contrat serait également entièrement extensible pour permettre à toutes les contreparties dans la chaîne de bénéficier de toute fonctionnalité d’interface utilisateur supplémentaire pouvant être ajoutée à l’avenir de la vie d’un DApp, dans la mesure où il constitue lui-même un portefeuille.

Avant de continuer, je dois avertir les lecteurs que le travail et le code présentés dans cet article sont expérimentaux et potentiellement très dangereux si mal utilisé! Tout contrat capable de déclencher rapidement des centaines de transactions doit être exécuté avec une extrême prudence, pour des raisons évidentes. En outre, cet article est un demi-article, un demi-discours technique.

Contrat de procuration simple

Nous pouvons commencer simplement et rédiger un contrat intelligent qui peut transférer une transaction sur le réseau en son propre nom. le

address

tapez Solidity a un

call

fonction, qui peut être utilisée pour appeler un contrat d’un autre contrat.

Transférer des transactions en utilisant le

call

fonction est exactement comment les portefeuilles Multi-Signature (MultiSigs) atteignent leur objectif. Au lieu de transférer instantanément les transactions entrantes, ils enregistrent la transaction dans le stockage et ne la transfèrent que lorsque tous les propriétaires de MultiSig l'ont approuvée.

Nous pouvons utiliser le

call

créer un contrat de proxy très simple, qui transfère instantanément toutes les transactions:

pragma solidity ^0.4.25;
contract Proxy {
    function execute(
        address target, uint256 weiValue, bytes payload
    ) public {
        target.call.value(weiValue)(payload);
    }
}
J'ai déployé ce contrat (avec quelques fonctions de secours, pourquoi pas) ici pour quiconque de jouer avec. Il n'a pas de concept de propriété, il transfèrera donc les transactions de quiconque.

Pour démontrer le contrat en action, j'ai transféré un seul jeton Bether ERC223 au contrat dans cette transaction. J'ai ensuite appelé le proxy dans une autre transaction, ordonnant au proxy de renvoyer le jeton, ce qu’il a fait.

Notre prochaine étape consiste à étendre ce contrat pour exécuter plusieurs transactions, et non une seule.

Limitations de la solidité et des codeurs expérimentaux

Le moyen évident d’étendre le contrat de procuration pour transférer plusieurs transactions ensemble serait de renverser les arguments du

execute

fonction dans les tableaux et boucle simplement sur eux dans la fonction comme ceci:

pragma solidity ^0.4.25;
contract ArrayProxy {
    function execute(
        address() targets, uint256() weiValues, bytes() payloads
    ) public {
        for(uint256 i = 0; i < targets.length; i++){
            targets(i).call.value(weiValues(i))(payloads(i));
        }
    }
}
Il y a cependant un problème. Le compilateur Solidity 0.4.25 ne prend pas en charge les arguments de fonction qui sont des tableaux de types de longueur variable. En dehors

bytes()

l'argument est exactement tel:

bytes

est un type de longueur variable et nous voulons un tableau d'entre eux. Le compilateur Solidity s'en plaindra avec le message suivant:

Error: This type is only supported in the new experimental ABI encoder.
    Use "pragma experimental ABIEncoderV2;" to enable the feature.
        address() targets, uint256() weiValues, bytes() payloads
                                                 ^--------------^

Le codeur ABI expérimental mentionné dans le message d'erreur ci-dessus est lentement étendu dans les dernières versions du Solidity Compiler 0.5.

Il existe plusieurs problèmes avec ce codeur. Tout d’abord, il s’agit d’expérimentation et l’activation de tout ce qui est qualifié d’expérimental dans le contexte des blockchains est un drapeau rouge en soi.

Le message d’avertissement délicieusement inquiétant que vous recevez lorsque vous faire essayez d'activer l'encodeur expérimental, "N'utilisez pas de fonctionnalités expérimentales sur des déploiements en direct", n'aide pas.

En outre, l'encodeur est un travail en cours, ce qui signifie que des fonctionnalités y sont ajoutées en permanence, et que je n'allais pas attendre et lancer des fonctionnalités expérimentales avec des portefeuilles à tir rapide sur réseau principal en direct…

Alors, comment pouvons-nous construire un portefeuille à tir rapide dans Solidity 0.4.25 sans fonctionnalités expérimentales? Nous devons aller au-delà de la solidité. Nous devons nous aventurer dans le bytecode redouté EVM lui-même.

Solidity! = EVM, optimisation de l'assemblage en ligne

Même s’il s’agit peut-être d’un sentiment subjectif, il me semble que la ligne de démarcation entre Solidity et l’EVM (Ethereum Virtual Machine) est extrêmement floue dans la communauté des Blockchain, au point que «contrats intelligents» est presque synonyme de «Solidité». Permet clairement de les lever de l'ambiguïté.

Tous les contrats intelligents sur le réseau Ethereum sont écrits en bytecode (la chaîne hexadécimale que vous voyez sur Etherscan lorsque vous ouvrez le code d'un contrat). L'EVM est responsable de l'exécution du bytecode.

Solidity est un langage de programmation qui compile du code lisible par l’homme en bytecode. La raison pour laquelle nous utilisons Solidity, c’est que la rédaction manuelle de contrats entiers en bytecode est extrêmement gênante et Solidity est le compilateur le plus populaire qui génère du bytecode EVM. Mais, fondamentalement, rien ne vous empêche d'écrire manuellement un contrat intelligent en hexadécimal sans l'aide de Solidity.

Cette distinction est cruciale, car les contrats intelligents écrits dans Solidity ne peuvent tirer parti de la fonctionnalité EVM que le compilateur Solidity actuel prend en charge. Si vous souhaitez que votre contrat smart fasse quelque chose que Solidity ne prend pas en charge, vous pouvez toujours le mettre en œuvre en écrivant vous-même le bytecode.

De manière pratique, Solidity donne aux programmeurs l’option de spécifier le code temporel dans le fichier.

assembly

blocs dans le code Solidity, au cas où nous aurions besoin d'aller au-delà de ce que Solidity peut faire. Cela revient à dire à Solidity: «Vous savez quoi, n’essayez même pas de générer du bytecode pour ce bit particulier, utilisez simplement ce bytecode que j’ai écrit moi-même».

Il se trouve que pendant que

bytes()

le type n’est pas supporté par la solidité, nous pourrions l’émuler (sous une autre forme) nous-mêmes en utilisant du bytecode. Alors laissez-nous faire juste cela.

Dissection de l'opcode CALL

La première chose à réaliser est que nous n’avons pas nécessairement besoin de la

bytes()

tapez du tout. Ce dont nous avons réellement besoin, c’est d’appeler un autre contrat avec certaines données, et

bytes()

le type est juste la manière dont Solidity l'aurait fait (s'il le pouvait). Mais Solidity! = EVM, voyons comment l’EVM effectue les transactions internes et sous quelle forme il a besoin des données utiles.

L'EVM a un

CALL

l'opcode, qui déclenche des transactions internes. Voici comment nous l'appelons manuellement à partir de Solidity:

assembly{
 call(
  ,
  ,
  ,
  ,
  ,
  ,
  
 )
}
Si nous voulons utiliser le

CALL

opération, nous devons fournir / calculer les sept arguments qu’elle consomme. Les deux arguments qui nous concernent sont les

et

, car ce sont eux qui définissent la charge utile que nous envoyons. Le reste est trivial.

Fondamentalement, quand on invoque le

CALL

opération, il appellera le contrat cible avec une charge utile composée du premier

octets situés à l'emplacement de la mémoire

. Cela signifie que si nous pouvons en quelque sorte obtenir toutes les données utiles de toutes les transactions dans une zone contiguë en mémoire, nous pouvons boucler des paires limite / offset et invoquer

CALL

directement sur les emplacements de mémoire calculés, déclenchant toutes les transactions.

Heureusement, Solidity contient un type de données très pratique pour stocker des octets contigus. C'est, sans surprise,

bytes

. Nous pouvons remplacer notre désir de

bytes()

tapez avec un simple

bytes

argument, et nous pouvons ajouter un nouveau

uint256()

argument des longueurs de charge utile pour naviguer dans les charges utiles contiguës dans l'unique

bytes

argument.

Écrire le portefeuille Gatling Gun

Alors, cousons tout ensemble. Nous allons créer une fonction incendie, qui aura la signature suivante:

function fire(
    bytes, address() targets, uint256() lengths, uint256() values
)
La première

bytes

L'argument est constitué de toutes les données utiles de toutes les transactions simplement ajoutées les unes aux autres. le

targets

argument est un tableau d'adresses des contrats que vous souhaitez appeler. le

lengths

tableau est un tableau de longueurs des charges utiles individuelles dans le

bytes

argument, et enfin, le

values

argument sont les valeurs en wei qui doivent être attachées à chaque transaction.

La première chose à faire est de s’assurer que tous les tableaux ont la même longueur:

require(targets.length == lengths.length);
require(targets.length == values.length);
Ensuite, nous passons le

bytes

argument en mémoire. C'est un peu plus compliqué, car nous devons savoir où se trouve le tableau d'octets de calldata, combien de temps il est, le déplacer en mémoire et décaler le pointeur de mémoire libre (0x40) à la fin des données. Nous faisons tout cela en assemblée:

uint256 payloadMemoryLocation;
assembly {
payloadMemoryLocation := mload(0x40)
let payloadLengthLocation := add(4, calldataload(4))
let payloadLength := calldataload(payloadLengthLocation)
let payloadLocation := add(32, payloadLengthLocation)
calldatacopy(payloadMemoryLocation, payloadLocation, payloadLength)
mstore(0x40, add(payloadMemoryLocation, payloadLength))
}

Une fois que les données utiles sont en mémoire, nous pouvons configurer la boucle sur toutes les transactions, ce qui calcule les limites et les décalages. Nous exigeons également que toutes les transactions internes aboutissent pour que toutes les transactions soient terminées ou aucune. Tout cela se fait dans la solidité:

uint256 offset = 0;
bool success;
for(uint256 i = 0; i < targets.length; i++){
  address target = targets(i);
  uint256 limit = lengths(i);
  uint256 value = values(i);
  
  # ASSEMBLY MAGIC HERE
  require(success);
  offset += limit;
}
Il ne nous reste plus qu'à remplacer le «ASSEMBLE MAGIC"Partie avec assemblage réel, qui invoque la

CALL

opération sur la charge utile correcte:

assembly {
  success := call(
    gas,
    target,
    value,
    add(payloadMemoryLocation, offset),
    limit,
    0,
    0
  )
}

C'est ça! Combiné, ce code vous permet d'exécuter des centaines de transactions arbitraires au sein d'une même transaction.

Mais assez de bavardages, démontrons-le dans la pratique.

Démonstration d'utilisation

La première chose à faire est de placer le portefeuille Gatling Gun sur la blockchain. Pour faciliter ce processus, j'ai écrit et déployé un contrat de déploiement de pistolet Gatling. ici (un contrat équivalent est également ici sur Ropsten si vous voulez jouer avec testnet Ether) Ce contrat permet à quiconque de se procurer son propre portefeuille Gatling Gun en appelant simplement le

deployGatlingGun(address owner)

fonction de ce contrat, qui en crée un nouveau et définit l'adresse du propriétaire sur celle fournie.

En fait, utiliser le portefeuille Gatling Gun est encore assez compliqué. J'ai donc mis en place une interface utilisateur minimaliste très simple, qui permet de déployer et d'utiliser ces portefeuilles avec une relative facilité via Metamask.

L'interface utilisateur est à eth-gatling-gun.com, ainsi qu’une vidéo de démonstration de son utilisation. Cette interface utilisateur est uniquement conçue comme un terrain d’essai pour l’utilisation du portefeuille. Si vous décidez d'utiliser les portefeuilles Gatling Gun dans n'importe quel projet / DApp, je vous recommanderais de vous y connecter directement à partir de votre propre code client via Metamask.
Si vous vous dirigez vers le Opération onglet, il vous sera présenté avec cet écran:
odDaJEW22WNo4J1j9b7I4gpjJpr1 qm433h3

Si vous n’en avez pas encore, vous pouvez cliquer sur le bouton «Déployer un nouveau pistolet Gatling», ce qui vous mènera automatiquement à l’écran suivant une fois la transaction minée:

odDaJEW22WNo4J1j9b7I4gpjJpr1 u41t433au

L’adresse en haut correspond à l’adresse de votre nouveau portefeuille Gatling Gun. Le champ de texte avec le bouton "Ajouter une transaction (s)" est l'endroit où nous plaçons toutes les transactions que nous voulons envoyer. N'oubliez pas que chaque transaction a une adresse cible, une quantité d'Ether et une charge utile.

Nous devons spécifier ces trois valeurs (séparées par un signe moins) pour chaque transaction. Nous pouvons spécifier plus de ces triplets (transactions multiples) en les séparant par le point-virgule.

Nous sommes maintenant prêts à exécuter les transactions mentionnées dans la préface de l’article. Pour cette démo, j’ai décidé que les opérations suivantes, toutes les opérations déclenchées en même temps devaient mettre suffisamment en valeur la flexibilité du portefeuille:

1. Envoyez 1 Ether à un autre portefeuille
2. Envoyez 1 jeton ERC223 à un autre portefeuille
3. Appeler un contrat sans argument de fonction
4. Appelez un contrat avec des arguments de fonction et une valeur Ether.

Bien sûr, vous pouvez en tirer beaucoup plus que quatre si vous le souhaitez.

Pour envoyer de l’éther du portefeuille de Gatling Gun, nous devons d’abord lui envoyer de l’éther. Rappelez-vous que le pistolet Gatling possède son propre Ether, qu’il utilise pour lui dire d’envoyer Ether ailleurs. Alors j'ai chargé le portefeuille avec 2 Ether dans cette transaction. J'ai aussi envoyé un jeton ERC223 dans cette transaction.

Pour la troisième transaction, nous avons besoin d’un contrat à appeler. J'ai créé un contrat très simple qui incrémente une valeur unique à chaque appel et émet un événement. Le contrat avec le code source vérifié est ici.

Pour la quatrième transaction, nous avons besoin d’un contrat pouvant recevoir des paiements Ether dans l’une de ses fonctions. Pour cela j’ai créé un simple contrat qui émet des événements chaque fois qu’il reçoit un Ether, avec le

uint256

valeur qui lui est envoyée dans la charge utile. Le contrat est ici.

Créons donc les triplets cible-valeur-charge utile pour chacune de ces transactions.

Pour transaction 1, nous allons envoyer le 1 Ether à l’un de mes portefeuilles de test:

0x785b8612b225b06764499f61e098725864ecd26b

. Ce sera la cible. La valeur sera 1 Ether (1000000000000000000 Wei) et la charge sera vide (

0x

). Ensemble, cela s’ajoute à:

0x785b8612b225b06764499f61e098725864ecd26b - 
1000000000000000000 - 
0x
Pour transaction 2 nous visons en réalité le contrat de jeton lui-même, qui est

0x14c926f2290044b647e1bf2072e67b495eff1905

. La valeur dans Wei sera zéro, car les transferts de jetons sont gratuits. La charge utile est plus compliquée. La signature de la fonction de transfert est

0xa9059cbb

, qui sera le début de notre charge utile. Ceci est suivi par une représentation de 256 bits de l'adresse du destinataire, que nous allons définir à mon adresse:

000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a

Ceci est ensuite suivi du montant que nous souhaitons transférer, qui est dans notre cas 1 jeton (1000000000000000000 en wei):

0000000000000000000000000000000000000000000000000de0b6b3a7640000

Ensemble, cette transaction ajoute à:

0x14c926f2290044b647e1bf2072e67b495eff1905 - 
0 - 
0xa9059cbb000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a0000000000000000000000000000000000000000000000000de0b6b3a7640000
Transaction 3 est très simple. Nous appelons le contrat à l'adresse

0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5

, la valeur sera zéro et la charge utile sera la signature du

increment()

fonction, qui est

0xd09de08a

. Ensemble c'est:

0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5 - 
0 - 
0xd09de08a
Transaction 4 utilise toutes les entrées possibles. Nous appelons le contrat à l'adresse

0x06741096ef84fd751b0805a96583123b5cb11540

avec une valeur de 1 Ether et appelez le

payment(uint256 number)

une fonction. Sa signature est

0x8b3c99e3

et la charge utile que nous lui envoyons peut être n’importe quel nombre, disons 123. Encodé en hex et assemblé la transaction ressemble à ceci:

0x06741096ef84fd751b0805a96583123b5cb11540 - 
1000000000000000000 - 
0x8b3c99e3000000000000000000000000000000000000000000000000000000000000007b

Nous pouvons annexer toutes ces transactions les unes aux autres avec un séparateur de point-virgule pour aboutir à ce qui suit:

0x785b8612b225b06764499f61e098725864ecd26b - 1000000000000000000 - 0x ; 0x14c926f2290044b647e1bf2072e67b495eff1905 - 0 - 0xa9059cbb000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a0000000000000000000000000000000000000000000000000de0b6b3a7640000 ; 0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5 - 0 - 0xd09de08a ; 0x06741096ef84fd751b0805a96583123b5cb11540 - 1000000000000000000 - 0x8b3c99e3000000000000000000000000000000000000000000000000000000000000007b

Il n'y a rien d'autre à faire avec ce texte, nous pouvons simplement le copier et le coller dans le champ de texte des transactions de la page Opération de cette manière et cliquer sur «Ajouter une transaction (s)»:

odDaJEW22WNo4J1j9b7I4gpjJpr1

Une fois que vous avez ajouté les transactions, vous les verrez toutes dans la liste des transactions, que vous pouvez modifier à l’aide des boutons (supprimer).

odDaJEW22WNo4J1j9b7I4gpjJpr1 4y2bj33md
Il ne reste plus qu’à Fire! J'ai tiré cette séquence exacte de transactions dans cette transaction sur le Mainnet.

Comme vous pouvez le voir dans l'aperçu des transactions ci-dessous, les deux transferts Ether ainsi que le transfert de jeton ont été enregistrés.

odDaJEW22WNo4J1j9b7I4gpjJpr1 1c2db33b5
le Onglet Journal des événements C’est le meilleur endroit pour enquêter sur ce qui s’est passé pendant l’exécution de la transaction, car tous nos contrats d’essais émettent des événements. Les deux premiers sont les événements de jeton ERC223 standard. Les deux derniers sont les événements de nos deux contrats de test:
odDaJEW22WNo4J1j9b7I4gpjJpr1 ta2eq330o

Évaluation

À ce stade, vous voudrez peut-être naturellement demander «Qu'est-ce qui le rend préférable à l'envoi des transactions une par une?». Il y a trois raisons principales.

Tout d'abord, l'envoi de transactions en masse est moins cher. J'ai simulé chacune de nos quatre transactions individuellement pour démontrer la différence de quantité de gaz utilisée.

En comparant cela au coût de la transaction en vrac ce qui a coûté 78 378, nous pouvons voir que la transaction en vrac était 38% moins chère.

En effet, chaque transaction Ethereum a un coût de base de 21 000 gaz. Ce coût de base n'est appliqué qu'une seule fois à la transaction en bloc, quel que soit le nombre de transactions déclenchées en interne. En fait, plus vous effectuez de transactions en même temps, plus vous économisez de gaz par transaction.

Deuxièmement, l'envoi de transactions en masse nous donne la sécurité transactionnelle. Supposons que vous ayez envoyé les quatre transactions en tant que transactions individuelles. Vous devez compter sur le fait que certaines d’entre elles peuvent échouer tandis que le reste réussit.

C'est chaotique.

La transaction en bloc applique une stratégie du tout ou rien, ce qui signifie que vous pouvez vous assurer que toutes vos modifications sont appliquées ou qu'aucune d'entre elles ne s'applique (et vous pouvez même les modifier avant un deuxième essai si vous le souhaitez).

Finalement, l'envoi de transactions en masse est plus convivial. Lors de la conception d'une blockchain DApp, on se heurte inévitablement au problème de déranger l'utilisateur avec des popups Metamask ou tout autre moyen utilisé pour la communication par blockchain.

Cela est inévitable pour un vrai DApp qui souhaite donner à l'utilisateur le contrôle total. Les transactions en masse permettent à l’interface d’un DApp de «composer» et de modifier la transaction en bloc de manière dynamique lorsque l’utilisateur interagit avec le DApp et ne demandent qu’à l’utilisateur de confirmer. tout leurs modifications immédiatement après que l’utilisateur ait fini de les faire.

Avant de terminer, certains d'entre vous se demandent peut-être si le portefeuille Gatling Gun peut être utilisé pour déployer des contrats. Après tout, les portefeuilles ordinaires peuvent l'être, alors pourquoi pas le portefeuille Gatling Gun? Eh bien, vous avez de la chance, car il le peut! Il y a un

deploy(bytes initCode, uint256 value)

fonctionner dans chaque portefeuille Gatling Gun, ce qui correspond exactement à ce à quoi vous vous attendiez, mais je ne voulais vraiment pas en parler dans cet article.

Conclusion

Ce projet et cet article sont le résultat de ma curiosité et de mon désir de partager des choses intéressantes avec des personnes partageant les mêmes idées. J'espère que vous avez apprécié la lecture. Si votre projet peut en tirer parti et que vous souhaitez en savoir plus, regardez la vidéo de démonstration sur le site ETH Gatling Gun page d'accueil.
Si vous souhaitez contribuer de quelque manière que ce soit à ce projet, j’ai placé le contrat ainsi que le code du site ici sur Bitbucket. Le code du site Web est très minimaliste et autonome (n’importe aucun script externe ni CSS). Cela signifie que n'importe qui peut l'exécuter localement simplement en clonant le référentiel.



Traduction de l’article de Simon Rovder : Article Original

BlockBlog

Le Meilleur de l'Actualité Blockchain Francophone & Internationale | News, Guides, Avis & Tutoriels pour s'informer et démarrer facilement avec Bitcoin, les Crypto-Monnaies et le Blockchain. En Savoir Plus sur L'Équipe BlockBlog

Commenter cet Article

Commenter cet Article

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Plus dans News

Les Plus Populaires

Acheter des Bitcoin

Acheter des Alt-Coins

Sécuriser vos Cryptos

Vêtements et Produits Dérivés

Top