Ethers' various Classes and Functions are available to import 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
const { ethers } = require("ethers");
import { ethers } from "ethers";
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, you can load it in your Web Applications from our CDN.
<script type="module">
import { ethers } from "https://cdn.ethers.io/lib/ethers-5.2.esm.min.js";
// Your code here...
</script>
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"
type="application/javascript"></script>
This section needs work...
Provider | A 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. | |
Signer | A 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. | |
Contract | A Contract is an abstraction which represents a connection to a specific contract on the Ethereum Network, so that applications can use it like a normal JavaScript 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:
- A connection to the Ethereum network (a Provider)
- Holds your private key and can sign things (a Signer)
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner()
Connecting to Ethereum: RPC
The JSON-RPC API is another popular method for interacting with Ethereum and is available in all major Ethereum node implementations (e.g. Geth and Parity) as well as many third-party web services (e.g. INFURA). It typically provides:
- A connection to the Ethereum network (a Provider)
- Holds your private key and can sign things (a Signer)
Connecting to an RPC client
const provider = new ethers.providers.JsonRpcProvider();
const signer = provider.getSigner()
Once you have a Provider, you have a read-only connection to the blockchain, which you can use to query the current state, fetch historic logs, look up deployed code and so on.
await provider.getBlockNumber()
// 16987688
balance = await provider.getBalance("ethers.eth")
// { BigNumber: "182334002436162568" }
ethers.utils.formatEther(balance)
// '0.182334002436162568'
ethers.utils.parseEther("1.0")
// { BigNumber: "1000000000000000000" }
Writing to the Blockchain
const tx = signer.sendTransaction({
to: "ricmoo.firefly.eth",
value: ethers.utils.parseEther("1.0")
});
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 methods mapped to encoding and decoding data for you.
If you are familiar with Databases, this is similar to an Object Relational Mapper (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 (ABI) provides.
This class is a meta-class, which means its methods are constructed at runtime, and when you pass in the ABI to the constructor it uses it to determine which methods to add.
While an 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 you can use the Human-Readable ABI in code, which the following examples use.
Connecting to the DAI Contract
const daiAddress = "dai.tokens.ethers.eth";
const daiAbi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function balanceOf(address) view returns (uint)",
"function transfer(address to, uint amount)",
"event Transfer(address indexed from, address indexed to, uint amount)"
];
const daiContract = new ethers.Contract(daiAddress, daiAbi, provider);
Querying the DAI Contract
await daiContract.name()
// 'Dai Stablecoin'
await daiContract.symbol()
// 'DAI'
balance = await daiContract.balanceOf("ricmoo.firefly.eth")
// { BigNumber: "19862540059122458201631" }
ethers.utils.formatUnits(balance, 18)
// '19862.540059122458201631'
const daiWithSigner = contract.connect(signer);
const dai = ethers.utils.parseUnits("1.0", 18);
tx = daiWithSigner.transfer("ricmoo.firefly.eth", dai);
daiContract.on("Transfer", (from, to, amount, event) => {
console.log(`${ from } sent ${ formatEther(amount) } to ${ to}`);
});
myAddress = "0x8ba1f109551bD432803012645Ac136ddd64DBA72";
filter = daiContract.filters.Transfer(null, myAddress)
// {
// address: 'dai.tokens.ethers.eth',
// topics: [
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
// null,
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
// ]
// }
daiContract.on(filter, (from, to, amount, event) => {
console.log(`I got ${ formatEther(amount) } from ${ from }.`);
});
Filtering Historic Events
myAddress = await signer.getAddress()
// '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
filterFrom = daiContract.filters.Transfer(myAddress, null);
// {
// address: 'dai.tokens.ethers.eth',
// topics: [
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
// ]
// }
filterTo = daiContract.filters.Transfer(null, myAddress);
// {
// address: 'dai.tokens.ethers.eth',
// topics: [
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
// null,
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
// ]
// }
await daiContract.queryFilter(filterFrom, 9843470, 9843480)
// [
// {
// address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
// args: [
// '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// '0x8B3765eDA5207fB21690874B722ae276B96260E0',
// { BigNumber: "4750000000000000000" },
// amount: { BigNumber: "4750000000000000000" },
// from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// to: '0x8B3765eDA5207fB21690874B722ae276B96260E0'
// ],
// blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78',
// blockNumber: 9843476,
// data: '0x00000000000000000000000000000000000000000000000041eb63d55b1b0000',
// decode: [Function (anonymous)],
// event: 'Transfer',
// eventSignature: 'Transfer(address,address,uint256)',
// getBlock: [Function (anonymous)],
// getTransaction: [Function (anonymous)],
// getTransactionReceipt: [Function (anonymous)],
// logIndex: 69,
// removeListener: [Function (anonymous)],
// removed: false,
// topics: [
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
// '0x0000000000000000000000008b3765eda5207fb21690874b722ae276b96260e0'
// ],
// transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1',
// transactionIndex: 81
// },
// {
// address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
// args: [
// '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// '0x00De4B13153673BCAE2616b67bf822500d325Fc3',
// { BigNumber: "250000000000000000" },
// amount: { BigNumber: "250000000000000000" },
// from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
// to: '0x00De4B13153673BCAE2616b67bf822500d325Fc3'
// ],
// blockHash: '0x8462eb2fbcef5aa4861266f59ad5f47b9aa6525d767d713920fdbdfb6b0c0b78',
// blockNumber: 9843476,
// data: '0x00000000000000000000000000000000000000000000000003782dace9d90000',
// decode: [Function (anonymous)],
// event: 'Transfer',
// eventSignature: 'Transfer(address,address,uint256)',
// getBlock: [Function (anonymous)],
// getTransaction: [Function (anonymous)],
// getTransactionReceipt: [Function (anonymous)],
// logIndex: 70,
// removeListener: [Function (anonymous)],
// removed: false,
// topics: [
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
// '0x00000000000000000000000000de4b13153673bcae2616b67bf822500d325fc3'
// ],
// transactionHash: '0x1be23554545030e1ce47391a41098a46ff426382ed740db62d63d7676ff6fcf1',
// transactionIndex: 81
// }
// ]
await daiContract.queryFilter(filterFrom, -10000)
await daiContract.queryFilter(filterTo)
signature = await signer.signMessage("Hello World");
// '0x550a28a0fab4e30de31ed651851bb2908ff4dd1b7c6a5cdcf62a8e2accbec1d53e27a6081538759a971d0c71d35ab571eda1e3a565da9948ac83361aa28566e21c'
message = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
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 ]
signature = await signer.signMessage(messageBytes)
// '0x471250f1080eb2686c3cdc94491eb52e56e45a69225f3ca82b72e5c2ad7195ff3146ce8ec2445d8593dface9526a180e9df35ac043135f40fe8cdd64278bdd591c'