2020-06-10 06:56:58 +03:00
|
|
|
-----
|
|
|
|
|
2020-07-03 08:54:56 +03:00
|
|
|
Documentation: [html](https://docs.ethers.io/)
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
-----
|
|
|
|
|
|
|
|
Getting Started
|
|
|
|
===============
|
|
|
|
|
|
|
|
Installing
|
|
|
|
----------
|
|
|
|
|
|
|
|
```
|
2020-09-08 08:12:15 +03:00
|
|
|
/home/ricmoo> npm install --save ethers
|
2020-06-10 06:56:58 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
Importing
|
|
|
|
---------
|
|
|
|
|
|
|
|
### Node.js
|
|
|
|
|
|
|
|
```
|
|
|
|
const { ethers } = require("ethers");
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
import { ethers } from "ethers";
|
|
|
|
```
|
|
|
|
|
|
|
|
### Web Browser
|
|
|
|
|
|
|
|
```
|
2020-07-05 07:02:47 +03:00
|
|
|
<script type="module">
|
|
|
|
import { ethers } from "https://cdn.ethers.io/lib/ethers-5.0.esm.min.js";
|
|
|
|
// Your code here...
|
|
|
|
</script>
|
2020-06-10 06:56:58 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
<script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js"
|
|
|
|
type="application/javascipt"></script>
|
|
|
|
```
|
|
|
|
|
|
|
|
Common Terminology
|
|
|
|
------------------
|
|
|
|
|
|
|
|
Common Terms
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connecting to Ethereum: 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.
|
2020-09-08 08:12:15 +03:00
|
|
|
// For this, you need the account signer...
|
|
|
|
const signer = provider.getSigner()
|
|
|
|
```
|
|
|
|
|
|
|
|
Connecting to Ethereum: RPC
|
|
|
|
---------------------------
|
|
|
|
|
|
|
|
```
|
|
|
|
// If you don't specify a //url//, Ethers connects to the default
|
|
|
|
// (i.e. ``http:/\/localhost:8545``)
|
|
|
|
const provider = new ethers.providers.JsonRpcProvider();
|
|
|
|
|
|
|
|
// The provider also allows signing transactions to
|
|
|
|
// send ether and pay to change state within the blockchain.
|
2020-06-10 06:56:58 +03:00
|
|
|
// For this, we need the account signer...
|
|
|
|
const signer = provider.getSigner()
|
|
|
|
```
|
|
|
|
|
|
|
|
### Querying the Blockchain
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Look up the current block number
|
|
|
|
provider.getBlockNumber()
|
2020-09-08 08:12:15 +03:00
|
|
|
// { Promise: 10819010 }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
2020-09-08 08:12:15 +03:00
|
|
|
// Get the balance of an account (by address or ENS name, if supported by network)
|
2020-06-10 06:56:58 +03:00
|
|
|
balance = await provider.getBalance("ethers.eth")
|
2020-06-13 02:21:37 +03:00
|
|
|
// { BigNumber: "2337132817842795605" }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
2020-09-08 08:12:15 +03:00
|
|
|
// Often you need to format the output to something more user-friendly,
|
|
|
|
// such as in ether (instead of wei)
|
2020-06-10 06:56:58 +03:00
|
|
|
ethers.utils.formatEther(balance)
|
2020-06-13 02:21:37 +03:00
|
|
|
// '2.337132817842795605'
|
2020-06-10 06:56:58 +03:00
|
|
|
|
2020-09-08 08:12:15 +03:00
|
|
|
// If a user enters a string in an input field, you may need
|
2020-06-10 06:56:58 +03:00
|
|
|
// to convert it from ether (as a string) to wei (as a BigNumber)
|
|
|
|
ethers.utils.parseEther("1.0")
|
2020-06-13 02:21:37 +03:00
|
|
|
// { BigNumber: "1000000000000000000" }
|
2020-06-10 06:56:58 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
### Writing to the Blockchain
|
|
|
|
|
|
|
|
```
|
|
|
|
// Send 1 ether to an ens name.
|
|
|
|
const tx = signer.sendTransaction({
|
|
|
|
to: "ricmoo.firefly.eth",
|
|
|
|
value: ethers.utils.parseEther("1.0")
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Contracts
|
|
|
|
---------
|
|
|
|
|
|
|
|
```javascript
|
2020-09-08 08:12:15 +03:00
|
|
|
// You can also use an ENS name for the contract address
|
2020-06-10 06:56:58 +03:00
|
|
|
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 = [
|
2020-09-08 08:12:15 +03:00
|
|
|
// Some details about the token
|
2020-06-10 06:56:58 +03:00
|
|
|
"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
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Get the ERC-20 token name
|
|
|
|
daiContract.name()
|
2020-06-13 02:21:37 +03:00
|
|
|
// { Promise: 'Dai Stablecoin' }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// Get the ERC-20 token synbol (for tickers and UIs)
|
|
|
|
daiContract.symbol()
|
2020-06-13 02:21:37 +03:00
|
|
|
// { Promise: 'DAI' }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// Get the balance of an address
|
|
|
|
balance = await daiContract.balanceOf("ricmoo.firefly.eth")
|
2020-09-08 08:12:15 +03:00
|
|
|
// { BigNumber: "11386855832278858351495" }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// Format the DAI for displaying to the user
|
|
|
|
ethers.utils.formatUnits(balance, 18)
|
2020-09-08 08:12:15 +03:00
|
|
|
// '11386.855832278858351495'
|
2020-06-10 06:56:58 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
### State Changing Methods
|
|
|
|
|
|
|
|
```
|
|
|
|
// The DAI Contract is currently connected to the Provider,
|
2020-09-08 08:12:15 +03:00
|
|
|
// which is read-only. You need to connect to a Signer, so
|
|
|
|
// that you can pay to send state-changing transactions.
|
2020-06-10 06:56:58 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// 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)
|
2020-06-13 02:21:37 +03:00
|
|
|
// {
|
|
|
|
// address: 'dai.tokens.ethers.eth',
|
|
|
|
// topics: [
|
|
|
|
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
|
|
// null,
|
|
|
|
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
|
|
|
|
// ]
|
|
|
|
// }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Get the address of the Signer
|
|
|
|
myAddress = await signer.getAddress()
|
2020-06-13 02:21:37 +03:00
|
|
|
// '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// Filter for all token transfers to me
|
|
|
|
filterFrom = daiContract.filters.Transfer(myAddress, null);
|
2020-06-13 02:21:37 +03:00
|
|
|
// {
|
|
|
|
// address: 'dai.tokens.ethers.eth',
|
|
|
|
// topics: [
|
|
|
|
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
|
|
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
|
|
|
|
// ]
|
|
|
|
// }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// Filter for all token transfers from me
|
|
|
|
filterTo = daiContract.filters.Transfer(null, myAddress);
|
2020-06-13 02:21:37 +03:00
|
|
|
// {
|
|
|
|
// address: 'dai.tokens.ethers.eth',
|
|
|
|
// topics: [
|
|
|
|
// '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
|
|
// null,
|
|
|
|
// '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72'
|
|
|
|
// ]
|
|
|
|
// }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// List all transfers sent from me a specific block range
|
|
|
|
daiContract.queryFilter(filterFrom, 9843470, 9843480)
|
2020-06-13 02:21:37 +03:00
|
|
|
// { 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
|
|
|
|
// }
|
|
|
|
// ] }
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
//
|
|
|
|
// The following have had the results omitted due to the
|
|
|
|
// number of entries; but they provide some useful examples
|
|
|
|
//
|
|
|
|
|
2020-09-08 08:12:15 +03:00
|
|
|
// List all transfers sent in the last 10,000 blocks
|
2020-06-10 06:56:58 +03:00
|
|
|
daiContract.queryFilter(filterFrom, -10000)
|
|
|
|
|
|
|
|
// List all transfers ever sent to me
|
|
|
|
daiContract.queryFilter(filterTo)
|
|
|
|
```
|
|
|
|
|
|
|
|
Signing Messages
|
|
|
|
----------------
|
|
|
|
|
|
|
|
```javascript
|
2020-09-08 08:12:15 +03:00
|
|
|
// To sign a simple string, which are used for
|
|
|
|
// logging into a service, such as CryptoKitties,
|
2020-06-10 06:56:58 +03:00
|
|
|
// pass the string in.
|
|
|
|
signature = await signer.signMessage("Hello World");
|
2020-09-08 08:12:15 +03:00
|
|
|
// '0x94fac815fc18f295c4860128d8960dfdb1d88acf891a48e345368f3f4d52c95e59d0f8b35d05d554905a39c63c11b66f61abf0211fcaba36bef5dfaf1ea5f1331c'
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
//
|
|
|
|
|
2020-09-08 08:12:15 +03:00
|
|
|
// This string is 66 characters long
|
2020-06-10 06:56:58 +03:00
|
|
|
message = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
|
|
|
|
|
|
|
|
// This array representation is 32 bytes long
|
|
|
|
messageBytes = ethers.utils.arrayify(message);
|
2020-06-13 02:21:37 +03:00
|
|
|
// 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 ]
|
2020-06-10 06:56:58 +03:00
|
|
|
|
|
|
|
// To sign a hash, you most often want to sign the bytes
|
|
|
|
signature = await signer.signMessage(messageBytes)
|
2020-09-08 08:12:15 +03:00
|
|
|
// '0xa77f9018a3ad3078056d529d5ccaca8796cdb5bc84e799d13b63a53646ab73f87f0895df7bbe2ee6016c95eb78a2e77013ab8f8d4855143d3567932cb5331e881c'
|
2020-06-10 06:56:58 +03:00
|
|
|
```
|
|
|
|
|