DEV Community

Cover image for How to use TypeScript with EIP-2535: Diamonds
Erhan Tezcan
Erhan Tezcan

Posted on


How to use TypeScript with EIP-2535: Diamonds

Ethereum Diamond Standard is a finalized standard, which provides a method to create modular smart contract systems that can be extended after deployment. In this post, we will look at how it can be used with TypeScript and TypeChain. Let's begin.

Diamond Standard

I will not go much into the details of diamonds as there are many that explain it in great detail; Nick Mudge, the author of this standard, has several posts on that explain more details, see for example: Understanding Diamonds on Ethereum.

In very short terms, a diamond consists of the following:

  • At the core, you have a Diamond contract. This contract implements a fallback function that can delegate call functions on other contracts, each defined as a Facet.
  • Facet is like a face of a diamond, and you can think of the Pink Floyd - Dark Side of the Moon album cover to really imagine what is going on here. You make a call, and that call will be routed somewhere else depending on the calldata.
  • There are many more details on how entirely this logic works, so I can also refer you to this series of posts by Ferhat Kocan.

I would like to focus in particular from an implementational perspective. There are several reference implementations on GitHub such as this with scripts written in JavaScript. This post is about using TypeScript with the diamonds.

If you have been developing contracts and have used TypeScript, chances are you might have used TypeChain. TypeChain generates contract bindings automatically, for example as an Hardhat plugin that runs whenever you compile contracts.

Consider the following tiny ERC20 contract that mints all the supply to the owner on deployment:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyERC20 is ERC20 {
    string memory name_,
    string memory symbol_,
    uint256 supply_
  ) ERC20(name_, symbol_) {
    _mint(msg.sender, supply_);
Enter fullscreen mode Exit fullscreen mode

Here is what deploying that small ERC20 contract looks like:

// import the generated contract types
import {ethers} from 'hardhat';
import {MyERC20, MyERC20__factory} from '../types/typechain';

// get the factory
const erc20Factory = await ethers.getContractFactory('MyERC20', owner)

// deploy with type-safe constructor arguments!
const myERC20 = await erc20Factory.deploy(, constants.symbol,; // returns a MyERC20 type
await myERC20.deployed();

// myERC20 now has type-safe function calls!
Enter fullscreen mode Exit fullscreen mode

You can also connect to existing contracts, and cast them to be the type of contract you would like to interact:

myERC20 = (await ethers.getContractAt('MyERC20', <contract address>)) as MyERC20;
Enter fullscreen mode Exit fullscreen mode

In fact, if you have used TypeChain plugin for hardhat, and ethers is imported from hardhat, it sometimes overloads functions such as getContractAt to return a typed contract with respect to the contract name, which is awesome.

Well, what about diamonds and facets? Well, you can actually make type-safe calls to the facets too!

// assuming you have diamond with facets ready
const foobarFacet = (await ethers.getContractAt('FoobarFacet', diamondAddress)) as FoobarFacet;
Enter fullscreen mode Exit fullscreen mode

As you can see, it is exactly the same way as before; the only difference is that diamondAddress is not where FoobarFacet was actually deployed at. In this case, we make calls with calldata structures with respect to FoobarFacet, and make our calls to the Diamond contract. There they go into fallback, and with delegatecall we actually run code in our FoobarFacet.

I have implemented deployment & testing scripts in TypeScript at the following repo: The contracts are taken from the author Nick Mudge's reference implementation. You can analyze the tests for more information on how things work out. There examples of adding, removing, and replacing facet functions.

Happy coding :)

Cover Picture is generated by a prompt I made on MidJourney.

Latest comments (0)

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields


Read the whole post now!