Beta Release: ethers.js v5
RicMoo — June 14, 2019
who?! me? updating... *beep boop*

It's been a while since a new (major) version of ethers.js has been released, but it is something that has been in the works for a while now and it's time to start getting some early adopter feedback.

A major version change allows for non-backwards-compatible changes to be made to the API, which enables migrating from some legacy conventions and expanding to include new techniques, functionality, and improve the overall flow of the library.

This is a quick article to highlight some of the changes, provide some justification and let developers know they can start trying it out.

A rose by any other name is... ReferenceError: anyOtherName is not defined

First, the pain-points, which will likely be the largest head-ache for most people already using ethers.

Unfortunately, in trying to make the library more concise and logically named, there are several slight changes in function names and API calls. Apologies in advance.

This should affect mostly people who have used the lower-level functionality for tools and frameworks, but will also impact even some higher-level usage.

Some examples:

  • The Arrayish type has now been split into Bytes (which must be array-like) and BytesLike (which may be an array-like or a hex string).
  • The ethers.utils.bigNumberify(v) is now BigNumber.from(v)
  • The class ethers.utils.BigNumber is now ethers.BigNumber, the class ethers.utils.Wordlist is now ethers.Wordlist and several other functions have been moved to the top level
  • The Interface class has been quite overhauled; if you were previously using it to parse ABI fragments, the ABI parsing is now handled in the ethers.utils.Fragment (and child EventFragment and FunctionFragmentclasses), but provides a lot more flexibility to move between JSON and Human-Readable representations
  • Also in the Interface class, previously only encoding requests and decoding responses were available; now both encoding and decoding of both requests and responses are available, so the API has changed a little and has added a few other useful coding methods

Modular Packages

As time goes on, there are an increasing number of components, many of which are useful, but not for everyone and not for every project.

By using Lerna to manage the project as a single repository of many packages, the project remains simple to develop and maintain, while enabling a more modular approach, which allows consumers to pick and choose the bits and pieces they need.

An umbrella project (still ethers on npm) will continue to contain all the functionality required for the vast majority of developers, especially early in the development cycle, but can be easily streamlined later on, and additional ancillary packages can be imported for less common functionality.

As a result, the extensive suite of test cases (and the tools for generating the test cases) are now easily maintained as separate packages, which reduces the number of development dependencies for most users, and also exposes a very thin package of isolated JSON test cases that any other library is free to run their own test harnesses against; for example the Firefly Hardware Wallet firmware written in C can easily run all the relevant test cases to verify mnemonic private key generation, RFC-6979 signing and transaction parsing.

Another library that will benefit largely from this modularization is Web3.jslibrary which currently pulls in the entire ethers.js library, but only requires the ethers.js ABI coding components; once v5 is ready for production, it can import just the"@ethersproject/abi" package.

Modular package also simplify creating extra ancillary packages, such as command-line interfaces, encryption and hardware wallet libraries, which are useful first-class libraries to provide, but which not everyone needs bundled into their application or tool.

The "@ethersproject/experimental" package is a new playground for random ideas and examples from GitHub issues, which can be tried out and moved to their own packages, or merged into existing packages once they have matured.

Caveats: While Lerna is used to help maintain this mono-repo, there are a few aspects of Lerna found to be problematic; for example, no one-time password support for NPM publishing, so the /admin/ scripts handle a great deal of the committing process, such as the changelog, GitHub release management, CDN uploads and NPM publishing.

Contracts

Contracts have had few new "buckets" added to them, which specifically aim to make historical event querying and offline/counterfactual operations simpler.

The contract.queryFilter( filter [ , fromBlock [ , toBlock ] ] ) method has been added to simplify querying historic and recent contract events.

The callStatic bucket simplifies calling non-constant functions as constantfunctions, which is useful for non-consistent queries forfunction results or for dry-runs. For example, contract.callStatic.transfer(addr, amount), will use call instead or creating a transaction against an ERC-20 token.

The populateTransaction bucket can be used to generate the transaction request that would be necessary for a function call or transaction, which is useful for offline-signing and on-chain contract wallets. For example, contract.populateTransaction.transfer(adds, amount) will return a transaction object that could be passed to a wallet to be signed for future broadcast.

Also, another small change that may affect some developers is that ambiguous methods are no longer exposed by their bare name in either Contract or Interface. For example, if an ABI specifies two conflicting functions, with the same name such as [ "function foo(uint256)", "function foo(address)", "function bar()"], the function foo must be accessed as contract["foo(uint256)"], while bar may still be referenced via contract.bar. In v4, the first method in the ABI would be available by its bare name.

Smarter Default Provider

The default provider uses the new FallbackProvider, which now supports a quorum with weighted backends. Each request to the provider, selects a random subset of the available backends, and only returns a result once the required quorum has agreed on the result.

For example, the default provider for mainnet (i.e. "homestead") has 4 backends (INFURA, Etherscan, Alchemy and Nodesmith), each with a weight of 1 and a quorum of 2. If a call is made, two of the backends are selected at random; if their responses are the same, the Promise resolves to that response, if they do not match, a third backend is randomly selected, and so on, until a consensus among unrelated backends has been reached.

This helps mitigate a backend being compromised, such as if a backend service has been hacked, is responding maliciously or has become out-of-sync and is working with stale data.

For sendTransaction, all nodes are sent the request, since there is no harm in "over-broadcasting", and this helps mitigate any backend service from censoring transactions.

When using the FallbackProvider, the weights for each provider may be specified. This allows an application to favour their own cluster of nodes and trust them more, while still gracefully failing over to other public nodes in the event their own nodes are down or out-of-sync.

Odds and Ends

There are also plenty of smaller changes, which make common operations easier.

  • Block tags may be given as negative numbers in filters and Provider API calls; for example, erc20Token.queryFilter("Transfer", -1234) will find all ERC-20 transfer events in the last 1,234 blocks.
  • New backends, courtesy of Alchemy and Nodesmith as well as new common super-class, which makes adding new URL-based API services a quick and simple task
  • Exposed a more expressive API for manipulating JSON-based ABI and Human-Readable ABI formats make it easier for other tools (such as the TypeScript CLI tool) to manipulate and convert between the two formats and parse Solidity signatures in general
  • The new FixedNumber class provides a simple object-oriented interface on top of the parseUnits and formatUnits and supports basic fixed-pointmathematics, which also enables the Fixed type in the ABI (especially for Vyper development)
  • The /admin scripts handle a lot more of the publishing tasks and related security (still requires four factors to authenticate), so the CDN will stay in-sync with the version published to NPM, GitHub releases will be tagged and a CHANGELOG is now kept up to date with each update

Aion

A lot the time and effort for this release was thanks to a generous grant from the Aion Foundation. This release includes abstracting several low-level aspects of the library, making it easier to extend and create supportng libraries for additional smart-contract blockchains, such as Aion.

Any extension libraries can easily sub-class many of the existing classes and in most cases only need to override a minimal subset of the methods to enable new and exciting functionality.

The Aion-flavored version of ethers.js (which supports both their Ethereum-based FVM and Java-based AVM) is also available for beta testing from npm and is available on GitHub.

To try it out, use npm install @ethersproject-aion/ethers.

The AVM (Aion Virtual Machine), which is based on a Java VM, enables developers who are already familiar with Java to use the large collection of existing tools to create and interact with smart contracts on their network, using the AvmContract object in the Aion-flavored fork of ethers.

Documentation

The documentation for v5 is still quite lacking and has not been updated from the v4 API, but is being updated little-by-little, including a migration guide, in a similar format to the v3 to v4 migration guide.

There will also be a documentation node to cover all the extras provided for the Aion extensions.

Feedback

Obviously this is a beta release, so there will be bugs.

Please open issues if you find any bugs, or to start a dialogue regarding new functionality that may make sense to add, since a major version change affords the opportunity to break backwards compatibility (to some extent).

And complain. If you don't like a new feature, API, documentation style, or whatever, open an issue to start a public discussion. When bug reports slow down and people stop complaining, that will be an indication it is nearly time to leave beta and enter production.

Thanks! Please feel free to follow me on Twitter and the ethers.js project on GitHub for the latest news and updates.

• • •

"The universe is probably littered with the one-planet graves of cultures which made the sensible economic decision that there's no good reason to go into space -- each discovered, studied, and remembered by the ones who made the irrational decision."