Highlights ethers.js: March 2022

RicMoo
RicMoo
Published in
6 min readMar 10, 2022

--

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

Graffiti Alley, Toronto, Canada

Ahoy! Welcome to the latest quarterly update for Ethers.

The latest minor version has just been released with some exciting new features along with some bug fixes and quality of life improvements.

Also, a quick reminder that the latest GitCoin GR13 grant is now in full swing, and I wanted to make sure to thank everyone who has contributed to Ethers in the past and also encourage everyone to poke around a bit; there are a lot of other worthwhile projects too, and even a dollar goes a long way against the CLR. Each contribution is also a little warm-fuzzy to the author, letting them know they are appreciated.

Thanks! You are all rock stars!

Off-chain Lookups for the L2 in you (via CCIP)

A major emphasis that both Ethers and the entire blockchain space is focused on is L2 support, which improves the entire experience, from reducing gas fees, increasing transaction capacity and allowing much faster interactions.

With the newly finalized CCIP Read (formerly Durin) standard, view and pure contract methods can defer their response to off-chain services, where storage and data processing is cheap, and then have the response verified by the contract by whatever security mechanism the author has decided is acceptable.

All high-level ENS-related calls now support CCIP Read, which makes it available to resolve addresses (including multicoin, like BTC), avatars, content-hashes and any other feature that the built-in ENS resolver handles.

To use CCIP Read for your own dapps and contracts, CCIP Read must be explicitly enabled in any call by setting the ccipReadEnabled property, otherwise any CCIP Off-chain lookup request will be throw like any normal CALL_EXCEPTION.

// To make a CCIP Read call, you must explicitly enable it, but
// then the rest of the magic just happens :)
await provider.call(
{
to: contractAddress,
data: "0x6352211e"
ccipReadEnabled: true
})

The following is a short example contract (off the top of my head, it likely won’t compile) to help illustrate the overall idea. Most people can skip over this, but I know some people will crave an example.

// Example Solidity Contract// All ownership is kept off-chain, with a single merkle-root
// within the contract encoding ownership on-chain.
// How this merkle-root is updated is left as an exercise
// to the reader; batched updates, signed messages or
// zk-snarks are all splendid ideas. :)
contract MyCcipTest { bytes32 _merkleRoot; error OffchainLookup(address, string[], bytes, bytes4, bytes); function ownerOf(uint token) public view returns (address) { // This URL will provide the merkle proof; we allow
// fallback urls in case one is down

string[] memory urls = new string[](2);
urls[0] = "https://api.sprinkles.gallery/{sender}/{data}";
urls[1] = "https://api-2.sprinkles.gallery/{sender}/{data}";
// Remember the token ID for later in verifyOwner
bytes memory extraData = abi.encode(token);
// Defer to the interwebs
revert OffchainLookup(
address(this), urls, callData,
this.verifyOwner.selector, extraData);
}
function verifyOwner(bytes calldata result,
bytes calldata extraData) public view returns (address) {
// Get the original contract.ownerOf details
uint token = abi.decode(extraData, (uint));
// Parse the server response
(address owner, bytes32 memory proof[]) =
abi.decode(result, (address, bytes[]));
// Compute this entry in the merkle tree
bytes32 leaf = keccak256(abi.encodePacked(owner, token));
// Throws if the merkle proof fails; otherwise owner is correct
verifyMerkleProof(_merkleRoot, leaf, proof);
// Fetched off-chain, verified on-chain! Huzzah!
return owner;
}
}

Security

There are possible security issues with the introduction of CCIP Read, which is why it is required to be explicitly enabled per call.

Since an external web resource is fetched as directed by a contract, which is potentially controlled by an untrusted actor, automatic calls (without user interaction) to untrusted contracts should be carefully thought through as it can leak the IP address of users or used to coordinate a DDoS attack.

For these reasons, the Provider also exposes a new method, which a sub-class can override, for example to proxy requests through an anonymizing service or to reject untrusted URLs or contracts.

// Example Customer Provider which rejects urls based on
// some method `isSafeUrl`. It could extend any BaseProvider.

class MyCustomProvider
extends JsonRpcProvider {
ccipReadFetch(tx, calldata, urls) {
urls = urls.filter(u => isSafeUrl(u));
return super.ccipReadFetch(tx, calldata, urls);
}
}

For those curious about further consequences about allowing contracts to trigger unsolicited web fetches, here is a worthwhile read about an exploit that affected Wallets and Websites which automatically fetched NFT images.

As for those who wish to fully and explicitly disable any and all CCIP Read functionality for now and kick any issues down the road, there is a disableCcipRead property on Providers.

// Fully disable CCIP Read functionality in your project,
// including ENS resolution

provider.disableCcipRead = true

ENS Wildcard Support (EIP-2544)

This is another feature near and dear to my heart, and something I’ve been overly excited about since I began badgering people about it during the second ENS workshop. And it’s finally here!

ENS Wildcards requires an entire article of its own, but in a nutshell it allows a single resolver to respond and resolve unlimited number of sub-names, which may not sound too impressive at first but this also has significant implications for L2 adoption.

For example, a toy project I’ve thrown together (currently only deployed to Ropsten), hatch.eth resolves all *.hatch.eth ENS names.

For any given ENS name, like ricmoo.eth the owner of that name has exclusive access to a Wisp Proxy Wallet that can be deployed to ricmoo.hatch.eth. Sending ether or tokens to ricmoo.hatch.eth sends them in such a way that only the owner of ricmoo.eth can access them.

There was never a need for that owner to perform any operation. Infinite active and useful ENS sub-names were instantly available without any user interaction required.

In fact, you can send assets to any ENS name, even if the owner has not configured it.

Even stranger, you can actually send assets to an ENS name that has never been registered, although, this would be unwise, as whoever successfully registers that name first can then claim the assets.

So, TL;DR, ENS wildcards enable lots of crazy awesome ideas, the likes of which I am beyond excited to see what hackathon hackers dream up.

Cloudflare Workers

I’m quite happy someone pointed out the problem with ethers in the Cloudflare Worker environment, as it is quickly becoming one of my favourite services.

The issue came down to a limitation in the server-side implementation of the fetch function within the Cloudflare Worker API, which is incompatible with some of the internal setup that ethers performs to improve compatibility between node.js and browsers.

So, there is now a skipFetchSetup property that can be set on a Connection object, which enables Cloudflare Workers to work without a hitch!

// Create your provider, specifying to skip fetch setup
const provider = new StaticJsonRpcProvider({
url: URL,
skipFetchSetup: true
});

Bits ‘n Bytes

  • Now that we have more data in a post-EIP-1559 world, we have some updated numbers on approximate uncle and MEV risk, so the default maxPriorityFeePerGas has been changed from 2.5 gwei to 1.5 gwei
  • The WebSocketProvider, in addition to a websocket URL, can now accept an existing WebSocket object, which allows for custom configuration, such as custom headers, certificates, or anything else your heart desires
  • Errors now include a URL, which links to more details about the problem, causes and possible solutions, which will be updated over time to include even more help when debugging unexpected problems
  • All functions and methods which accept a signature can now also accept a compact signature, as EIP-2098 has become final
  • This has been a pain point for many people, for quite some time and I apologize for taking so long to get to it, but pending blocks are now fully supported by provider.getBlock
  • Somewhat of a white whale, after a lot of experimentation and research, the issue on fast-block-time networks (e.g. Polygon and BSC) for events should be resolved, using a more robust algorithm to efficiently handle polling events when a network’s getLogs is not always reliable

That’s all folks…

Thanks for reading! Any and all feedback is welcome, and to keep up with my meanderings, follow me on Twitter.

Thanks! And remember to hack the planet!

“Lettuce and shells, cheese and meat. It’s raining tacos, yum yum yum yum, yummidy, yum. It’s like a dream.”

--

--