Highlights ethers.js: December 2021
RicMoo — December 6, 2021

The current version of ethers as of this article is 5.5.2.

Season's greetings from my home to yours...

The release of a beta for v6 is just around the corner, and I thought I'd spend most of this highlights article outlining some of the upcoming changes and features.

A Picture is worth 1,000 words (ENS Avatars in v5)

But there is one new feature in v5 I'd like to highlight though, which has become quite useful.

The recent release of the ENS token has created a large influx of people fully configuring their ENS names, so the new provider.getAvatar("ricmoo.eth")returns a URL which can be used to display an avatar in your User Interfaces next to an address or ENS name.

This is an important step towards a pleasant and unified web3 experience, including the new "login with Ethereum" standard the ENS team has been working on. It will be wonderful to log into websites with an account I own and control and not some random backend database of usernames and passwords the website will invariably leak. Keep an eye on it. :)

And now onto v6...

It's a Modern JavaScript World

Previous versions of ethers provided backwards compatibility for a large variety of browsers, node.js environments and tools, which are now quite outdated.

In v6, all code assumes an ES2016 or newer environment, which means all generated code is provided as ES modules, simplifying the ethers build process and making it more compatible with modern tools such as bundlers and linters.

It also means more concise and easy-to-understand code, a smaller libraryand better encapsulation through features like private fields.

TypeScript

A lot of TypeScript changes are coming.

First of all, the library is now strictNullCheck enabled, so all methods are very explicit about whether null is a valid return type or not, which improves type-safety both within ethers and for libraries which depend on ethers which also have strict null checking.

There is a also a much stronger emphasis on using generics to create tighter interfaces, which allows many objects and types to be safer while also more flexible when necessary.

For example, in many situations, such as using a TransactionRequest or contract method, a Signer objet should be allowed for the to field or as an address type parameter.

Contracts are also designed to be much more TypeScript friendly, so that a typed interface is all that is needed to get full type-safety.

New Crypto Libs on the Block

Along with using modern JavaScript, comes the ability to use newer libraries which did not work in ES3 environments.

The secp256k1 and hashes library by Paul Miller are awesome, and help shrink the size of ethers, improve the ESM compatibility and provide a fully typed dependency for all the signing, hashing and password derivation functions needed by ethers.

This replaces ten dependencies by five different authors with two by the same author, who has made a significant effort to make a compact, audited set of libraries. Honestly, they are just so spot on.

Dynamic and Typed Contract Methods

The modern JavaScript feature I'm most excited about using is the Proxy object, which makes the Contract object much more straight forward.

In previous versions of ethers, a load-time warning was logged when a method was ambiguous and the method names for lookup had to be statically determined when the ABI was first used.

With the modern JavaScript Proxy object, this can be evaluated at run-time, reducing superfluous warnings for ambiguous methods that are never used, and method names can also be normalized on lookup, so contract["foo(uint256)"] and contract["foo(uint bar)"] both access the same methods (in v5 the latter would fail).

Along the same lines, methods with an ambiguous set of parameters also get a better way to be specified. The new Typed object, allows an otherwise ambiguous input (e.g. "0x1234"; should it be a uint16, bytes2, bytes, or string?) to be explicitly cast to its type using Typed.uint256("0x1234").

const ABI = [
"function foo(bytes bar)",
"function foo(uint bar)",
"function foo(address bar)",
]
const contract = new Contract(address, ABI, signer)
const value = "0xb1966861d47d2f2a1279fa46d9da0fecaf3071a4"
// Throws an error, as the method to call is ambiguous
//contract.foo(value)
// Calls foo(bytes); same as contract["foo(bytes)"](value)
contract.foo(Typed.bytes(value))
// Calls foo(uint256); same as contract["foo(uint)"](value)
contract.foo(Typed.uint(value))
// Calls foo(address); same as contract["foo(address)"](value)
contract.foo(Typed.address(value))
Network Flexing

The Network object is now a proper object, and can override a lot of network-specific functionality within the Provider. In most cases, non-Ethereum networks should be able to do away with their custom providers, and just roll all the necessary changes into their respective Network object.

The Network object also provides a plug-in system, which makes it much easier for provider-specific changes, and makes L2 providers much easier and flexible to create and use.

By swapping out methods on the Formatter object in a Network, low-level changes to the input and output values can be made.

// Network for Tendermint-based chains, which are mostly compatible
// with Ethereum, but differ in transaction hash calculation
class TendermintFormatter extends Formatter {
transaction(value: any): any {
const tx = super.transaction(value)
tx.hash = tendermintHashFunction(tx)
return tx
}
}
class TendermintNetwork extends Network {
constructor(name: string, chainId: string) {
super(name, chainId, new CustomFormatter())
}
}
// We register a plugin, to ensure a new copy each time
const createExchain = () => { new TendermintNetwork("enchain", 65)
Network.register(\{"enchain"}, \{createExchain})
// This could be added into its own ancillary package, but
// can now be used like any other network
const provider = new JsonRpcProvider("exchain")

Or by adding plug-ins to a Provider, consumers of a provider (including sub-classes) can be extended seemlessly.

// Network for BSC which is compatible with Ethereum, but has
// alternate URLs for Etherscan
const bscscanUrl = "https:/\/api.bscscan.com"
class BscNetwork extends Network {
constructor() {
super("bsc-mainnet", 56)
this.addPlugin(new EtherscanPlugin(bscscanUrl))
}
}
Network.register("bsc-mainnet", () => (new BscNetwork()))
// This can now be used directly by the EtherscanProvider
const provider = new EtherscanProvider("bsc-mainnet")
Conclusion

Thanks for reading! This was just a quick article to whet your appetites, but the beta should be out soon for you to try. Follow me on Twitter for my random thoughts and my imminent release announcements.

Thanks! And a Very Happy Holidays to all!

• • •

"I'm from outer space! It's not clear to me why you have to cook bread twice." ~Korvo