Πώς να δημιουργήσετε μια εφαρμογή ιστού Πορτοφόλι Ethereum

Μια ανασκόπηση των πιο όμορφων μερών του eth-hot-wallet

Αυτό το άρθρο είναι μια τεχνική ανασκόπηση των ενδιαφέρων τμημάτων του eth-hot-wallet , μιας εφαρμογής web πορτοφόλι Ethereum με εγγενή υποστήριξη erc20 token. Ο πηγαίος κώδικας βρίσκεται στο GitHub (άδεια MIT).

Πίνακας περιεχομένων:

  • Το πορτοφόλι Ethereum ως εφαρμογή ιστού
  • Η στοίβα
  • Τα δοχεία eth-hot-wallet
  • Ενιαίο σχέδιο για το πορτοφόλι Ethereum
  • Redux και Redux-Saga
  • Ασφαλής γεννήτρια κωδικών πρόσβασης
  • eth-lightwallet και SignerProvider
  • Κρυπτογραφημένος χώρος αποθήκευσης εκτός σύνδεσης
  • Αποστολή Ethereum χρησιμοποιώντας Web3.js
  • Αποστολή διακριτικών erc20 χρησιμοποιώντας το Web3.js
  • Εγγραφή στον κύκλο ζωής συναλλαγών Ethereum χρησιμοποιώντας κανάλια Web3.js V1 και Redux-Saga
  • Ψηφοφορία Ethereum blockchain και δεδομένα τιμών χρησιμοποιώντας το Redux-Saga
  • Παρακολουθώντας το μέγεθος της δέσμης
  • συμπέρασμα

Το πορτοφόλι Ethereum ως εφαρμογή ιστού

Όταν το λογισμικό αναπτύσσεται ως εφαρμογή ιστού, η ευρεία προσβασιμότητα είναι το πρώτο πράγμα που έρχεται στο μυαλό. Σε τελική ανάλυση, ο ιστός είναι η πιο ευρέως προσβάσιμη πλατφόρμα μεταξύ συσκευών. Το Eth-hot-wallet είναι ένα PWA (προοδευτική εφαρμογή ιστού) που μπορεί να χρησιμοποιηθεί από οποιοδήποτε σύγχρονο πρόγραμμα περιήγησης ιστού.

Επιπλέον, οι πρόσφατες βελτιώσεις στην υποστήριξη PWA βελτιώνουν σημαντικά την εμπειρία χρήστη σε κινητά.

Πλεονεκτήματα:

  • Δεν απαιτείται πρόσθετο λογισμικό
  • Δεν απαιτείται εγκατάσταση οποιουδήποτε είδους
  • Δυνατότητα χρήσης σύγχρονων εργαλείων ανάπτυξης ιστού.
  • Εύκολη ανάπτυξη και αναβάθμιση

Μειονεκτήματα:

  • Πιο επιρρεπείς σε επιθέσεις ηλεκτρονικού ψαρέματος.
  • Οι προσθήκες προγράμματος περιήγησης ενδέχεται να εισάγουν κακόβουλο κώδικα στη σελίδα.
  • Υψηλός χρόνος φόρτωσης σε αργές συνδέσεις στο Διαδίκτυο
  • Περιορισμένη πρόσβαση στον χώρο αποθήκευσης συσκευών

Το γεγονός ότι κακόβουλες επεκτάσεις προγράμματος περιήγησης ενδέχεται να εισάγουν κώδικα JavaScript σε μια προσπάθεια εξαγωγής των κλειδιών είναι σημαντικό. Για τη μετεγκατάσταση αυτού του κινδύνου, ο χρήστης θα πρέπει να ενθαρρύνεται να απενεργοποιεί τις επεκτάσεις (δηλ. Χρησιμοποιώντας σε κατάσταση ανώνυμης περιήγησης) ή να ενσωματώνει τον Ιστό με έναν εξωτερικό πάροχο web3 όπως το πρόγραμμα περιήγησης MetaMask ή Trust. Η μετατροπή της εφαρμογής ιστού σε εφαρμογή για επιτραπέζιους υπολογιστές είναι επίσης μια βιώσιμη επιλογή.

Όσον αφορά το ηλεκτρονικό ψάρεμα (phishing), ο χρήστης θα πρέπει να ενθαρρύνεται να προσθέτει σελιδοδείκτη στη σελίδα και να έχει πρόσβαση σε αυτήν μέσω της αναζήτησης Google. Είναι πολύ απίθανο ένας ιστότοπος ηλεκτρονικού ψαρέματος να κατατάσσεται πάνω από τον πραγματικό ιστότοπο στα αποτελέσματα αναζήτησης.

Κατώτατη γραμμή: μια εφαρμογή ιστού θα σας επιτρέψει να προσεγγίσετε το ευρύτερο κοινό με ελάχιστη τριβή . Κατά τη γνώμη μου, ο ιστός είναι η καλύτερη πλατφόρμα στόχος για νέες εφαρμογές.

Η στοίβα

Το μεγαλύτερο μέρος του κώδικα είναι αφιερωμένο στο front-end:

Το τελικό πακέτο αποτελείται από πολλά πακέτα, όπως φαίνεται στο package.json.

Τα συστατικά ανώτερου επιπέδου περιλαμβάνουν:

  • Eth-lightwallet - Ελαφρύ πορτοφόλι JS για κόμβο και το πρόγραμμα περιήγησης για διαχείριση κλειδιών
  • React, Redux, saga.js, immutableJS και επανεπιλέξτε τυλιγμένο από την πρώτη αντίδραση boiler
  • Σχεδιασμός μυρμηγκιών - εξαιρετικό σύνολο συστατικών UI για αντίδραση
  • Webpack - Ένα πακέτο για JavaScript και φίλους.

Και για το back-end:

Το τελικό πακέτο αναπτύσσεται απευθείας σε σελίδες GitHub από έναν ειδικό κλάδο στο αποθετήριο. Δεν υπάρχει ανάγκη για back-end στην παραδοσιακή σκηνή.

Για να δημιουργήσουμε τη βρύση Testnet Ethereum, θα χρησιμοποιήσουμε ένα πλαίσιο χωρίς διακομιστή. Βελτιώνει σημαντικά την εμπειρία προγραμματιστή κατά τη χρήση του AWS Lambda. Είναι μια πολύ αποδοτική λύση που εξαλείφει την ανάγκη συντήρησης της υποδομής, ειδικά σε εφαρμογές χαμηλού όγκου.

Τα δοχεία eth-hot-wallet

Όταν χρησιμοποιείτε τον συνδυασμό React, Redux, Saga.js και Reselect, κάθε δοχείο (μπορεί) αποτελείται από τα ακόλουθα συστατικά:

  • index.js - για την απόδοση του GUI
  • δράσεις.js
  • reducer.js
  • saga.js
  • selectors.js
  • constants.js

Όπως δήλωσε ο Dan Abramov, υπάρχουν περισσότερες από μία προσεγγίσεις για το αν θα χρησιμοποιήσετε ένα εξάρτημα ή ένα κοντέινερ. Από την εμπειρία μου, εάν ένα στοιχείο έχει περισσότερα από ~ 8 χαρακτηριστικά εντός της κατάστασης εφαρμογής, θα πρέπει να διαχωριστεί σε νέο κοντέινερ. Αυτός είναι απλώς ένας κανόνας. Ο αριθμός των χαρακτηριστικών μπορεί να φουσκώνει με την πάροδο του χρόνου. Με σύνθετα στοιχεία, είναι καλύτερο να έχετε ένα μοναδικό κοντέινερ παρά να ομαδοποιήσετε την κατάσταση του κύριου κοντέινερ.

Not every container needs to have all the ingredients. In eth-hot-wallet, the sendTokencontainer does not use its own Saga.js. We separated it so as not to burden the state of the homepage component.

The Homepage container

The primary container, where most of the action takes place, is the homepage container. In the homepage container, Saga.js is responsible for dealing with side effects. Besides GUI, its main responsibility will be dealing with keystore operations.

The ETH-Lightwallet package provides the keystore. All related operations including keys, seeds, encryption, importing, exporting are done in this section.

The Header container

The header demonstrates the fact that a container is much more than just a GUI component:

This container might look simple at first with only a logo and a network selector. Does it even need to be in its own container? The answer is that in eth-hot-wallet every network communication-related action and state management is done inside the header container. More than enough for any container.

The SendToken container

SendToken is a modal that appears while the user selects to send Ether/ tokens.

The modal includes some basic input verification, like amount and Ethereum address check. It does not use Saga.js to initiate side effects, but instead uses actions provided by the homepage and header containers.

We separated it into a new container to reduce clustering the state of the homepage container.

TokenChooser container

Token Chooser appears when the user wants to select what token the wallet will manage.

The TokenChooser name was selected in order not to be confused with the term “selector” which appears many times through the wallet code in a different context (reduxjs/reselect: Selector library for Redux).

Same as with the SendToken container, TokenChooser does not use its own Saga.js file but calls actions from the homepage container when needed.

A Unified design for Ethereum Wallet

Since the appearance of the ERC20 standard (EIP20), it was obvious that tokens were going to be an important part of the Ethereum ecosystem. The Ethereum wallet was designed with a unified design approach in mind. Ether and token should be treated equally from the user’s perspective.

Under the hood, the API for sending Ether and sending tokens is quite different. So is checking the balance, but they will appear the same on the GUI.

To send Ether, we need to use native functions provided by the web3.js library, while sending tokens and checking balances involves interaction with a smart contract. More on this issue later.

Redux and Redux-Saga

Using Redux store as a single source of truth greatly benefits the wallet. GUI actions and user-triggered flows can be relatively easily managed by actions and reducers provided by Redux.

Aside from keeping the UI state, the Redux store also holds the key-store object (a partially encrypted JavaScript object supplied by eth-lightwallet). This makes the keystore accessible throughout the app by using a selector.

Redux-Saga is what makes the entire setup shine.

redux-saga είναι μια βιβλιοθήκη που στοχεύει να κάνει παρενέργειες της εφαρμογής (δηλαδή, ασύγχρονα πράγματα όπως ανάκτηση δεδομένων και ακάθαρτα πράγματα, όπως η πρόσβαση στην κρυφή μνήμη του προγράμματος περιήγησης) ευκολότερη στη διαχείριση, πιο αποτελεσματική στην εκτέλεση, εύκολο στη δοκιμή και καλύτερα στο χειρισμό αστοχιών.

Το Saga.js χρησιμοποιεί Generators για να κάνει τις ασύγχρονες ροές να διαβάζονται και να γράφονται εύκολα . Με αυτόν τον τρόπο, αυτές οι ασύγχρονες ροές μοιάζουν με τον τυπικό σας σύγχρονο κώδικα JavaScript (είδος like async/ awaitαλλά με περισσότερες επιλογές προσαρμογής).

Στην περίπτωση του πορτοφολιού Ethereum, χρησιμοποιώντας το Saga έχουμε έναν άνετο τρόπο χειρισμού ασύγχρονων ενεργειών όπως κλήσεις API ανάπαυσης, ενέργειες βασικών καταστημάτων, κλήσεις blockchain Ethereum μέσω web3.js και πολλά άλλα. Όλα τα αιτήματα διαχειρίζονται καθαρά σε ένα μέρος, χωρίς επανάκληση και πολύ διαισθητικό API.

Παράδειγμα χρήσης για redux-saga:

Secure password generator

To adequately secure the user’s keystore, we need to encrypt it with a strong password. When using eth-lightwallet, the password needs to be provided during the initiation of the hd-wallet.

Let’s assume that we have a function called generateString, which can provide genuinely random strings at any length.

If the user wants to generate a new wallet, we will produce the following parameters:

We can ask the user to confirm the password and the seed or generate a new set on its behalf. Alternatively, we can ask the user for their own existing seed and password.

generateString implementation: We will use the relatively new window.crypto API to get random values (currently supported by all major browsers).

Eth-hot-wallet implementation is based on the following code to generate random but human-readable strings:

After the user has accepted the password and the seed, we can use the values and generate the new wallet.

eth-lightwallet and SignerProvider

  1. LightWallet is intended to be a signing provider for the Hooked Web3 provider.
  2. Hooked Web3 provider has been deprecated, and currently the author recommends the package ethjs-provider-signer as an alternative.
  3. At the moment of writing, there is a bug in ethjs-provider-signer that prevents the display of error messages. The bug was fixed but didn’t merge back into the main branch. Those error messages are critical for this setup to function correctly.

Bottom line: Use eth-lightwallet with this version of ethjs-provider-signer: //github.com/ethjs/ethjs-provider-signer/pull/3 to save time on trial and error.

Encrypted offline storage

The lightwallet keystore vault JSON object is encrypted, and it requires from us an external passwordProvider to safely keep the encryption key. The keystrore object is always encrypted. The app is responsible for safekeeping the password and provides it with any action.

eth-hot-wallet uses Store.js — Cross-browser storage for all use cases, used across the web. Store.js allows us to store the encrypted keystore easily and extract it back from storage when the webpage is accessed.

When the wallet is loaded for the first time, it will check if there is a keystore in local storage and will auto load it to Redux state if so.

At this point, we can read the public data of the keystore but not the keys. To display public data before the user enters the encryption password, we need an additional operation mode: loaded and locked. In this mode, the wallet will display the addresses and fetch the balances but will not be able to perform operations such as sending transactions or even generating new addresses. Triggering any of those actions will prompt for the user’s password.

Sending Ethereum using Web3.js

When using [email protected], the function sendTransaction is provided in the following form:

web3.eth.sendTransaction(transactionObject [, callback])

The callback will return the TX as a result in case of success.

However, to properly integrate it into our saga.js flow, we need to wrap sendTransaction function with a promise:

This way we continue regular Saga.js execution after sendTransaction is called.

Sending erc20 tokens using Web3.js

The Ethereum blockchain does not provide primitives that encapsulate token functionality, nor should it. Every token deployed on Ethereum is, in fact, a program that corresponds to the eip20 specification. Since the Ethereum virtual machine (EVM) is Turing complete (with some restrictions), every token might have a different implementation (even for the same functionality). What unifies all those programs under the term “token” is that they provide the same API as defined by the specification.

When we are sending a token on Ethereum, we are interacting with a smart contract. To communicate with a smart contract we need to know its API, the format for sharing contract’s API called Ethereum Contract ABI.

We will store the erc20 ABI as part of our JavaScript bundle and instantiate a contract during the program run-time:

const erc20Contract = web3.eth.contract(erc20Abi);

After contract setup, we can easily interact with it programmatically using the Web3.js contract API.

For each token we will need a dedicated contract instance:

const tokenContract = erc20Contract.at(tokenContractAddress);

After the creation of contract instance, we can access the contract functions by calling the desired function straight from JavaScript:

See Web3.js contract API for the full details.

We will promisify the tokenContract.transfer.sendTransaction to use it with our redux-saga flow:

It is possible to use es6-promisify or similar instead of writing the promise directly, but I prefer the direct approach in this case.

Subscribing to Ethereum transaction life-cycle using Web3.js V1 and redux-saga channels

eth-hot-wallet uses web3.js v0.2.x and does not support this feature at the moment. The example is provided by another project. It is an important feature and should be used extensively.

The new version of Web3.js (V1.0.0) is shipped with a new contract API that can inform us about transaction life-cycle changes.

Enter the PromiEvent: A promise combined event emitter.

web3.eth.sendTransaction({...}).once('transactionHash', function(hash){ ... }).once('receipt', function(receipt){ ... }).on('confirmation', function(number, receipt){ ... }).on('error', function(error){ ... }).then(function(receipt){ //fired once the receipt is mined});

web3.eth.sendTransaction() will return an object (a promise) that will resolve once the transaction is mined. The same object will allow us to subscribe to ‘transactionHash’, ‘receipt’, ‘confirmation’ and ‘error’ events.

This API is far more informative and elegant than the one provided with 0.2.x version of Web3.js. We will see how we can integrate it into our web app with the help of Saga.js channels. The motivation is to update the application state (Redux store) once a change to the transaction state is detected.

In the following example, we will create a ‘commit’ transaction to an arbitrary smart contract and update app state when we get ‘transactionHash’, ‘receipt’ and ‘error’ events.

We need to initialize the new channel and fork a handler:

The handler will catch all channel events and will call the appropriate Redux action creator.

Once the channel and the handler are both ready and the user initiates the transaction, we need to register to the generated events:

In fact, we don't need a new channel for each transaction and can use the same channel for all types of transactions.

The full source code of this example can be found here.

Polling Ethereum blockchain and price data using redux-saga

There are several ways to watch for blockchain changes. It is possible to use Web3.js to subscribe to events or we can poll the blockchain by ourselves and have more control over some aspects of polling.

In eth-hot-wallet, the wallet is polling the blockchain periodically for balance changes and Coinmarketcap API for price changes.

This redux-saga pattern will allow us to poll any data source or API:

After the CHECK_BALANCES action is seen by the default saga, the checkAllBalances function is called. It can end with one of two possible outcomes: CHECK_BALANCES_SUCCESS or CHECK_BALANCES_ERROR . Each one of them will be caught by watchPollData() to wait X seconds and call checkAllBalance again. This routine will continue until STOP_POLL_BALANCES is caught by watchPollData . After that, it is possible to resume the polling by submitting CHECK_BALANCES action again.

Keeping an eye on the bundle size

When building web apps using JavaScript and npm, it might be tempting to add new packages without analyzing the footprint increase. Eth-hot-wallet uses webpack-monitor to display a chart of all the dependencies and the differences between each build. It allows the developer to see the bundle size change clearly after each new package is added.

Webpack monitor also can help in finding the most demanding dependencies and might even surprise the developer by highlighting the dependencies that do little for the app but contribute a lot to the bundle size.

Webpack-monitor is easy to integrate and is definitely worth including in any webpack based web app.

Conclusion

The issues presented in this article are only part of the challenges that need to be solved when building an Ethereum wallet. However, overcoming those issues will create a solid foundation and will allow us to continue and create a successful wallet.

Building a wallet can also be a great introduction to the world of Ethereum since most distributed applications (DApps) require a similar set of capabilities both from the front-end and blockchain perspective.

ETH Hot Wallet - Ethereum Wallet with ERC20 support

Το ETH Hot πορτοφόλι είναι ένα πορτοφόλι Ethereum με υποστήριξη ERC20. Τα κλειδιά δημιουργούνται μέσα στο πρόγραμμα περιήγησης και δεν αποστέλλονται ποτέ… eth-hot-wallet.com

Σε περίπτωση που έχετε οποιεσδήποτε ερωτήσεις σχετικά με το eth-hot-wallet ή οποιοδήποτε σχετικό θέμα, μη διστάσετε να επικοινωνήσετε μαζί μου μέσω Twitter ή να ανοίξετε ένα ζήτημα στο GitHub.