Getting Started

Installing

The various Classes and Functions are available to be imported manually from sub-packages under the @ethersproject organization but for most projects, the umbrella package is the easiest way to get started.

/home/ricmoo> npm install --save ethers@next

Importing

Node.js

node.js require
const { ethers } = require("ethers");
ES6 or TypeScript
import { ethers } from "ethers";

Web Browser

It is generally better practice (for security reasons) to copy the ethers library to your own webserver and serve it yourself.

For quick demos or prototyping though, it can be loaded in your Web Applications from our CDN.

ES6 in the Browser
<script type="module"> import { ethers } from "https://cdn.ethers.io/lib/ethers-5.0.esm.min.js"; // Your code here... </script>
ES3 (UMD) in the Browser
<script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js" type="application/javascipt"></script>

Common Terminology

This section needs work...

ProviderA Provider (in ethers) is a class which provides an abstraction for a connection to the Ethereum Network. It provides read-only access to the Blockchain and its status. 
SignerA Signer is a class which (usually) in some way directly or indirectly has access to a private key, which can sign messages and transactions to authorize the network to charge your account ether to perform operations. 
ContractA Contract is an abstraction which represents a connection to a specific contract on the Ethereum Network, so that it can be used like a normal JavaScipt object. 
Common Terms 

Connecting to Ethereum: Metamask

The quickest and easiest way to experiment and begin developing on Ethereum is to use Metamask, which is a browser extension that provides:

Connecting to Metamask
// A Web3Provider wraps a standard Web3 provider, which is // what Metamask injects as window.ethereum into each page const provider = new ethers.providers.Web3Provider(window.ethereum) // The Metamask plugin also allows signing transactions to // send ether and pay to change state within the blockchain. // For this, we need the account signer... const signer = provider.getSigner()

Querying the Blockchain

Once you have a Provider, you have a read-only connection to the blockchain, which can be used to query the current state, fetch historic logs, look up deployed code and so on.

Basic Queries
// Look up the current block number provider.getBlockNumber() // { Promise: 10397126 } // Get the balance of an account (by address or ENS name) balance = await provider.getBalance("ethers.eth") // { BigNumber: "2337132817842795605" } // Often you will need to format the output for the user // which prefer to see values in ether (instead of wei) ethers.utils.formatEther(balance) // '2.337132817842795605' // Or if a user enters a string in an input field, you may need // to convert it from ether (as a string) to wei (as a BigNumber) ethers.utils.parseEther("1.0") // { BigNumber: "1000000000000000000" }

Writing to the Blockchain

Sending Ether
// Send 1 ether to an ens name. const tx = signer.sendTransaction({ to: "ricmoo.firefly.eth", value: ethers.utils.parseEther("1.0") });

Contracts

A Contract is an abstraction of program code which lives on the Ethereum blockchain.

The Contract object makes it easier to use an on-chain Contract as a normal JavaScript object, with the method all mapped to encoding and decoding data for you.

If you are familiar with Databases, this is similar to ORM.

In order to communicate with the Contract on-chain, this class needs to know what methods are available and how to encode and decode the data, which is what the Application Binary Interface (API) provides.

This class is a meta-class, which means its methods are constructed at runtime, when you pass in the ABI to the constructor it uses that to determine which methods to add.

While a on-chain Contract may have many methods available, you can safely ignore any methods you don't need or use, providing a smaller subset of the ABI to the contract.

An ABI often comes from the Solidity or Vyper compiler, but may also be placed in the code easily using the Human-Readable ABI, which the following examples use.

Connecting to the DAI Contract
// We can use an ENS name for the contract address const daiAddress = "dai.tokens.ethers.eth"; // The ERC-20 Contract ABI, which is a common contract interface // for tokens (this is the Human-Readable ABI format) const daiAbi = [ // Some simple details about the token "function name() view returns (string)", "function symbol() view returns (string)", // Get the account balance "function balanceOf(address) view returns (uint)", // Send some of your tokens to someone else "function transfer(address to, uint amount)", // An event triggered whenever anyone transfers to someone else "event Transfer(address indexed from, address indexed to, uint amount)" ]; // The Contract object const daiContract = new ethers.Contract(daiAddress, daiAbi, provider);

Read-Only Methods

Querying the DAI Contract
// Get the ERC-20 token name daiContract.name() // { Promise: 'Dai Stablecoin' } // Get the ERC-20 token synbol (for tickers and UIs) daiContract.symbol() // { Promise: 'DAI' } // Get the balance of an address balance = await daiContract.balanceOf("ricmoo.firefly.eth") // { BigNumber: "9709905125722568213383" } // Format the DAI for displaying to the user ethers.utils.formatUnits(balance, 18) // '9709.905125722568213383'

State Changing Methods

Sending DAI
// The DAI Contract is currently connected to the Provider, // which is read-only. We need to connect to a Signer, so // that we can pay to send state-changing transactions. const daiWithSigner = contract.connect(signer); // Each DAI has 18 decimal places const dai = ethers.utils.parseUnits("1.0", 18); // Send 1 DAI to "ricmoo.firefly.eth" tx = daiWithSigner.transfer("ricmoo.firefly.eth", dai);

Listening to Events

Listening to Events
// Receive an event when ANY transfer occurs daiContract.on("Transfer", (from, to, amount, event) => { console.log(`${ from } sent ${ formatEther(amount) } to ${ to}`); // The event object contains the verbatim log data, the // EventFragment and functions to fetch the block, // transaction and receipt and event functions }); // A filter for when a specific address receives tokens myAddress = "0x8ba1f109551bD432803012645Ac136ddd64DBA72"; filter = daiContract.filters.Transfer(null, myAddress) // { // address: 'dai.tokens.ethers.eth', // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // null, // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72' // ] // } // Receive an event when that filter occurs daiContract.on(filter, (from, to, amount, event) => { // The to will always be "address" console.log(`I got ${ formatEther(amount) } from ${ from }.`); });

Query Historic Events

Filtering Historic Events
// Get the address of the Signer myAddress = await signer.getAddress() // '0x8ba1f109551bD432803012645Ac136ddd64DBA72' // Filter for all token transfers to me filterFrom = daiContract.filters.Transfer(myAddress, null); // { // address: 'dai.tokens.ethers.eth', // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72' // ] // } // Filter for all token transfers from me filterTo = daiContract.filters.Transfer(null, myAddress); // { // address: 'dai.tokens.ethers.eth', // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // null, // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72' // ] // } // List all transfers sent from me a specific block range daiContract.queryFilter(filterFrom, 9843470, 9843480) // { Promise: [ // { // address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // args: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0x8B3765eDA5207fB21690874B722ae276B96260E0', // { BigNumber: "4750000000000000000" } // ], // blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78', // blockNumber: 9843476, // data: '0x00000000000000000000000000000000000000000000000041eb63d55b1b0000', // decode: [Function], // event: 'Transfer', // eventSignature: 'Transfer(address,address,uint256)', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 69, // removeListener: [Function], // removed: false, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72', // '0x0000000000000000000000008b3765eda5207fb21690874b722ae276b96260e0' // ], // transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1', // transactionIndex: 81 // }, // { // address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // args: [ // '0x8ba1f109551bD432803012645Ac136ddd64DBA72', // '0x00De4B13153673BCAE2616b67bf822500d325Fc3', // { BigNumber: "250000000000000000" } // ], // blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78', // blockNumber: 9843476, // data: '0x00000000000000000000000000000000000000000000000003782dace9d90000', // decode: [Function], // event: 'Transfer', // eventSignature: 'Transfer(address,address,uint256)', // getBlock: [Function], // getTransaction: [Function], // getTransactionReceipt: [Function], // logIndex: 70, // removeListener: [Function], // removed: false, // topics: [ // '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72', // '0x00000000000000000000000000de4b13153673bcae2616b67bf822500d325fc3' // ], // transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1', // transactionIndex: 81 // } // ] } // // The following have had the results omitted due to the // number of entries; but they provide some useful examples // // List all transfers I sent in the last 10,000 blocks daiContract.queryFilter(filterFrom, -10000) // List all transfers ever sent to me daiContract.queryFilter(filterTo)

Signing Messages

Signing Messages
// To sign a simple string, which can often be used for // logging into a service, such as CryptoKitties simply // pass the string in. signature = await signer.signMessage("Hello World"); // '0x7b8d663c680b165bb7b0601a65d730f532fa6427b2e30f1d91ff1d929712b3a50b427a672b90c1dc48a4e5fbde292fbded51f670ab57d15d5794b6ff015649611c' // // A common case is also signing a hash, which is 32 // bytes. It is important to note, that to sign binary // data it MUST be an Array (or TypedArray) // // This string is 66 chacacters long message = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" // This array representation is 32 bytes long messageBytes = ethers.utils.arrayify(message); // Uint8Array [ 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239 ] // To sign a hash, you most often want to sign the bytes signature = await signer.signMessage(messageBytes) // '0xc791b3d29aa1754f9e392784273f076ef39ca5d81f2729c92af61f89db724a604074acbd725d9d95e1d3c9630211c8eee8e34f6d948d537dd82c11be4bcf676e1c'