Ether - JS
Ether - JS
It's
lightweight, secure, and commonly used for building decentralized applications
(dApps). Here's a quick breakdown:
Key Features:
1. Wallet Management:
1. Create wallets (mnemonic or private key).
2. Sign transactions/messages.
2. Provider API:
3. Contract Interaction:
4. Utilities:
1. Encode/decode data.
2. Work with Ethereum addresses, BigNumber, and more.
Installation:
bash
Copy code
npm install ethers
Example Use:
1.
Connect to Ethereum:
2.
3.
javascript
4.
5.
Copy code
6.
7.
const { ethers } = require("ethers");const provider = new
ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_API_K
EY");
8.
9.
10.
Wallet Interaction:
11.
12.
javascript
13.
14.
Copy code
15.
16.
17.
18.
19.
20.
21.
javascript
22.
23.
Copy code
24.
25.
const abi = [ /* Contract ABI */ ];const contractAddress =
"0x...";const contract = new ethers.Contract(contractAddress,
abi, wallet);const value = await contract.someFunction();
26.
27.
28.
Event Listening:
29.
30.
javascript
31.
32.
Copy code
33.
34.
35.
36.
Advantages:
Simplicity.
Extensive documentation.
Compatible with browser and Node.js.
Alternatives:
Web3.js.
Web3.py (Python).
Modules in Ether.js
1.
Providers:
2.
javascript
Copy code
3.
Wallets:
4.
javascript
Copy code
const wallet = ethers.Wallet.createRandom(); // Generates
a new walletconsole.log(wallet.privateKey,
wallet.address);
5.
Contracts:
6.
javascript
Copy code
7.
Utils:
8.
javascript
Copy code
console.log(ethers.isAddress("0xAddress")); // Validate
Ethereum addressconst value = ethers.parseEther("1.0");
// Convert ETH to Weiconsole.log(value.toString());
9.
Signers:
10.
javascript
Copy code
1.
2.
mate gas:
javascript
Copy code
3.
Event Filters:
4.
javascript
Copy code
const filter = contract.filters.Transfer("0xFromAddress",
"0xToAddress");
contract.on(filter, (from, to, amount) => {
console.log({ from, to, amount });
});
5.
6.
javascript
Copy code
7.
Transaction Overrides:
8.
javascript
Copy code
Ether.js vs Web3.js
1.
dApp Development:
2.
javascript
Copy code
3.
Backend Services:
4.
5.
Token Interaction:
javascript
Copy code
6.
NFT Handling:
7.
Ethers.js Ecosystem
Integrations:
Testing:
o Commonly used with testing frameworks like Hardhat for smart contracts.
Here’s an in-depth guide on Ether.js with detailed topics and advanced concepts:
1. Providers
Providers act as the interface to the Ethereum blockchain, enabling data retrieval and
transactions.
Types of Providers:
Code Example:
javascript
Copy code
2. Wallets
Wallet Operations:
javascript
Copy code
const wallet =
ethers.Wallet.createRandom();console.log("Address:",
wallet.address);console.log("Private Key:",
wallet.privateKey);
javascript
Copy code
Encrypted Wallets:
javascript
Copy code
javascript
Copy code
const decryptedWallet = await
ethers.Wallet.fromEncryptedJson(encryptedJson,
"strongPassword");
3. Contracts
Used for interacting with smart contracts via ABI and addresses.
Example:
javascript
Copy code
Deploy a Contract:
javascript
Copy code
4. BigNumber Operations
Examples:
javascript
Copy code
Advanced Topics
Create a Filter:
javascript
Copy code
Listening to Blocks:
javascript
Copy code
2. Transaction Handling
Send ETH:
javascript
Copy code
Estimate Gas:
javascript
Copy code
const gasEstimate = await
contract.estimateGas.someFunction(arg1, arg2);console.log("Gas
Estimate:", gasEstimate.toString());
javascript
Copy code
Reverse Lookup:
javascript
Copy code
javascript
Copy code
javascript
Copy code
Common Patterns
1.
Connecting to MetaMask:
2.
3.
javascript
4.
5.
Copy code
6.
7.
Error Handling:
11.
javascript
Copy code
try {
const tx = await wallet.sendTransaction(txData);
} catch (error) {
console.error("Transaction failed:", error.message);
}
12.
13.
javascript
Copy code
const { ethers } = require("hardhat");const [deployer] = await
ethers.getSigners();
const contract = await ethers.deployContract("MyContract",
[arg1, arg2]);console.log("Deployed at:", contract.address);
1. Transaction Lifecycle
Steps:
Code Example:
javascript
Copy code
const tx = {
to: "0xRecipientAddress",
value: ethers.parseEther("0.1"),
gasLimit: 21000,
gasPrice: ethers.parseUnits("30", "gwei"),
};
const signedTx = await wallet.signTransaction(tx);const sentTx
= await provider.sendTransaction(signedTx);const receipt =
await sentTx.wait(); // Wait for
confirmationconsole.log("Transaction Receipt:", receipt);
2. Offline Signing
Example:
javascript
Copy code
const unsignedTx = {
to: "0xRecipientAddress",
value: ethers.parseEther("0.1"),
nonce: 0,
gasLimit: 21000,
gasPrice: ethers.parseUnits("20", "gwei"),
};
const signedTx = await
wallet.signTransaction(unsignedTx);console.log("Signed
Transaction:", signedTx);
// Broadcast the signed transaction laterconst txResponse =
await
provider.sendTransaction(signedTx);console.log("Transaction
Hash:", txResponse.hash);
3. Meta-Transactions
Flow:
Example:
javascript
Copy code
const domain = {
name: "MyDApp",
version: "1",
chainId: 1, // Mainnet
verifyingContract: "0xContractAddress",
};
const types = {
MetaTransaction: [
{ name: "nonce", type: "uint256" },
{ name: "functionSignature", type: "bytes" },
],
};
const message = {
nonce: 1,
functionSignature: "0xFunctionData",
};
const signature = await wallet._signTypedData(domain, types,
message);console.log("Signature:", signature);
4. Handling Nonces
javascript
Copy code
javascript
Copy code
const tx = {
to: "0xRecipient",
value: ethers.parseEther("1"),
nonce: nonce + 1, // Use next nonce
};
Gas Optimization
1. Gas Estimation
javascript
Copy code
EIP-1559 introduces
maxFeePerGas and
maxPriorityFeePerGas:
javascript
Copy code
const tx = {
to: "0xRecipient",
value: ethers.parseEther("0.1"),
maxFeePerGas: ethers.parseUnits("100", "gwei"),
maxPriorityFeePerGas: ethers.parseUnits("2", "gwei"),
};
const txResponse = await
wallet.sendTransaction(tx);console.log("Transaction Hash:",
txResponse.hash);
Switching Networks:
javascript
Copy code
L2-Specific Transactions:
Security Practices
1.
2.
javascript
Copy code
require("dotenv").config();const wallet = new
ethers.Wallet(process.env.PRIVATE_KEY);
3.
4.
5.
Error Handling:
6.
javascript
Copy code
try {
const result = await contract.someFunction();
} catch (error) {
console.error("Error:", error.message);
}
Example:
javascript
Copy code
1.
Hardhat Integration:
2.
javascript
Copy code
const { ethers } = require("hardhat");
const MyContract = await
ethers.deployContract("MyContract");console.log("Contract
Address:", MyContract.address);
const result = await
MyContract.someFunction();console.log("Result:", result);
3.
4.
1.
Hardhat:
2.
Etherscan Plugin:
4.
5.
Alchemy/Infura:
6.
7.
Web3Modal:
8.
1.
Transaction Errors:
2.
3.
Invalid Signatures:
4.
5.
Network Mismatch:
6.
o Verify the provider and network chain ID match.
Here’s even more on Ether.js, diving into specialized topics and advanced usage:
Specialized Topics
Gasless transactions allow users to interact without owning ETH (a relayer pays the gas).
Workflow:
Code Example:
javascript
Copy code
const messageHash =
ethers.utils.keccak256(ethers.utils.toUtf8Bytes("Message for
MetaTx"));const signature = await
wallet.signMessage(ethers.utils.arrayify(messageHash));
console.log("Signature:", signature);// Relayer broadcasts the signed
message to the smart contract
Example Integration:
javascript
Copy code
const forwarderAddress = "0xForwarderAddress";const forwarderAbi =
[/* Minimal Forwarder ABI */];const forwarder = new
ethers.Contract(forwarderAddress, forwarderAbi, provider);
const forwarderNonce = await
forwarder.getNonce(wallet.address);console.log("Nonce:",
forwarderNonce);
// Prepare a meta-transactionconst txData = {
from: wallet.address,
to: "0xTargetContract",
data: "0xFunctionData",
nonce: forwarderNonce,
};const signature = await
wallet.signMessage(ethers.utils.keccak256(txData));
Setup Example:
javascript
Copy code
const flashbotsProvider = await
ethers.FlashbotsBundleProvider.create(
provider,
ethers.Wallet.createRandom() // Flashbots signer
);
const bundle = [
{
signer: wallet,
transaction: {
to: "0xRecipient",
value: ethers.parseEther("0.5"),
gasLimit: 21000,
},
},
];
const tx = await flashbotsProvider.sendBundle(bundle,
targetBlockNumber);console.log("Flashbots Bundle Sent:", tx);
4. Multisig Wallets
javascript
Copy code
const safeAbi = [/* Gnosis Safe ABI */];const safeAddress =
"0xGnosisSafeAddress";const safe = new ethers.Contract(safeAddress,
safeAbi, wallet);
const tx = await safe.submitTransaction("0xTarget",
ethers.parseEther("1"), "0xData");console.log("Multisig Transaction
Hash:", tx.hash);
5. Layer 3 Solutions
javascript
Copy code
const zkSyncProvider = new ethers.JsonRpcProvider("https://zksync2-
rpc.zksync.io");const balance = await
zkSyncProvider.getBalance("0xYourAddress");console.log("zkSync
Balance:", ethers.formatEther(balance));
1. Common Errors:
"insufficient funds":
Happens when sending a transaction with insufficient ETH.
Fix: Ensure wallet balance covers value + gas.
2. Transaction Debugging:
Simulate a transaction: Use
callStatic to run a
transaction locally without
broadcasting.
javascript
Copy code
1. Provider Abstraction
javascript
Copy code
const getProvider = (network) => {
const rpcUrls = {
mainnet: "https://mainnet.infura.io/v3/YOUR_API_KEY",
polygon: "https://polygon-rpc.com",
};
return new ethers.JsonRpcProvider(rpcUrls[network]);
};
const provider = getProvider("polygon");
Example:
javascript
Copy code
const multicallAbi = [/* Minimal Multicall ABI */];const
multicallAddress = "0xMulticallAddress";
const multicall = new ethers.Contract(multicallAddress, multicallAbi,
provider);const calls = [
{ target: "0xContract1", callData:
contract1.interface.encodeFunctionData("method1", []) },
{ target: "0xContract2", callData:
contract2.interface.encodeFunctionData("method2", []) },
];
const { returnData } = await multicall.aggregate(calls);const decoded
= returnData.map((data, i) =>
ethers.AbiCoder.defaultAbiCoder().decode(["type"],
data));console.log(decoded);
javascript
Copy code
const minimalAbi = ["function balanceOf(address) view returns
(uint256)"];const erc20 = new ethers.Contract(tokenAddress,
minimalAbi, provider);
const balance = await
erc20.balanceOf("0xAddress");console.log("Balance:",
ethers.formatUnits(balance, 18));
Performance Tips
1.
2.
3.
javascript
4.
5.
Copy code
6.
7.
8.
9.
10.
Cache Frequent Calls: Avoid redundant network requests for static data like
contract ABIs or token metadata.
11.
12.
13.
14.
javascript
15.
16.
Copy code
17.
18.
19.
20.
Extending Ether.js
1. Custom Plugins
You can create custom modules to extend Ether.js functionality:
javascript
Copy code
class MyCustomProvider extends ethers.JsonRpcProvider {
async getCustomData(address) {
const balance = await this.getBalance(address);
return ethers.formatEther(balance) + " ETH";
}
}const customProvider = new
MyCustomProvider("https://mainnet.infura.io/v3/YOUR_API_KEY");const
data = await
customProvider.getCustomData("0xAddress");console.log(data);
javascript
Copy code
const query = `
{
transfers(where: { to: "0xRecipient" }) {
from
to
value
}
}
`;const response = await
fetch("https://api.thegraph.com/subgraphs/name/example", {
method: "POST",
body: JSON.stringify({ query }),
});const data = await
response.json();console.log(data.data.transfers);