diff --git a/LikKee.md b/LikKee.md
index fc0c2aa2..056a51fb 100644
--- a/LikKee.md
+++ b/LikKee.md
@@ -5111,7 +5111,183 @@ End of WTF Solidity 103
 
 ### 2024.10.15
 
-#### Ether.js 101: Interaction with Ethereum
+#### Ethers.js 101: Interaction with Ethereum
+
+[ethers](https://docs.ethers.org/) is a complete and compact open-source javascript library for interacting with the EVM. The usage has surpass the `web3.js` for a few reasons:
+
+- `ethers` take 116.5 kB while `web3.js` take 590.5 kB
+- `ethers` have `Provider` class to manage network connectivity, `Wallet` classs to manage private keys, more flexible
+- Native support for Ethereum Name Service (ENS)
+
+Installation
+`npm install ethers --save`
+
+Check address balance
+
+```
+import { ethers } from "ethers";
+const provider = ethers.getDefaultProvider(); // To use specific RPC: ethers.JsonRpcProvider(RPC_URL);
+const main = async () => {
+    const balance = await provider.getBalance(`vitalik.eth`); // Address or ENS
+    console.log(`ETH Balance of vitalik: ${ethers.formatEther(balance)} ETH`);
+}
+main();
+```
+
+Provider class
+
+`jsonRpcProvider` allow users to connect to a specific node service provider
+
+- [ChainList](https://chainlist.org/) have the most comprehensive collections of Chains and RPCs
+- Alchemy
+
+```
+const ALCHEMY_MAINNET_URL = 'https://eth-mainnet.g.alchemy.com/v2/API_KEY';
+const providerETH = new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
+const balance = await providerETH.getBalance(`vitalik.eth`);;
+const network = await providerETH.getNetwork(); // Retrieve network info
+const feeData = await providerETH.getFeeData(); // Retrieve current gas data
+const code = await providerETH.getCode("0xc778417e063141139fce010982780140aa0cd5ab"); // Retrieve bytecode of contract
+```
+
+Contract class
+
+Read only
+
+```
+const contract = new ethers.Contract(contractAddress, contractAbi, provider);
+```
+
+Read and write contract
+
+```
+const contract = new ethers.Contract(contractAddress, contractAbi, signer);
+```
+
+Example of Read/Write WETH Contract
+
+```
+const mnemonic = "announce room limb pattern dry unit scale effort smooth jazz weasel alcohol";
+const wallet = Wallet.fromMnemonic(mnemonic);
+
+const wethMainnet = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
+const wethAbi = [
+    "function deposit() payable",
+    "function totalSupply() view returns (uint)"
+];
+const weth = new ethers.Contract(wethMainnet, wethAbi, wallet);
+
+await weth.deposit({value: parseEther("1")}); // Wrap 1 ETH to 1 WETH
+const totalSupply = await weth.totalSupply(); // Retrieve current supply of WETH
+```
+
+Signer class and Wallet class
+
+- `Signer` class is an abstraction of an Ethereum account for signing messages and transactions, but `Signer` class is an abstract class and cannot be instantiated directly, so `Wallet` class which inherits from the `Signer` class is using as above example.
+
+```
+const walletRnd = ethers.Wallet.createRandom(); // Create wallet with random private key
+const walletPk = new ethers.Wallet(pkString); // Create wallet with known private key
+const walletMmn = ethers.Wallet.fromMnemonic(mnemonicString); // Create wallet with mnemonic phrase BIP39
+```
+
+Sending transaction
+
+```
+const wallet = new ethers.Wallet(pkString, provider);
+const receipt = await wallet.sendTransaction({
+        to: recipientAddress,
+        value: ethers.parseEther("1")
+    }); // Transfer 1 ETH to recipient
+await receipt.wait() // Wait for the transaction to be confirmed on the chain
+console.log(receipt) // Print the transaction details
+```
+
+Deploy Contract Demo
+
+```
+const abiERC20 = [
+    "constructor(string memory name_, string memory symbol_)",
+    "function name() view returns (string)",
+    "function symbol() view returns (string)",
+    "function totalSupply() view returns (uint256)",
+    "function balanceOf(address) view returns (uint)",
+    "function transfer(address to, uint256 amount) external returns (bool)",
+    "event Transfer(address indexed from, address indexed to, uint256 amount)"
+];
+const bytecodeERC20 = "608060405260646000553480156100...";
+const factoryERC20 = new ethers.ContractFactory(abiERC20, bytecodeERC20, wallet);
+const contractERC20 = await factoryERC20.deploy("WTF Token", "WTF");
+await contractERC20.waitForDeployment(); // Wait for the transaction to be confirmed on the chain
+
+await contractERC20.name(); // Retrieve ERC20 info after contract deployed
+await contractERC20.symbol();
+```
+
+Event
+
+- Events emitted by smart contracts are stored in the logs of the Ethereum virtual machine.
+
+Retrieving events
+
+```
+const endingBlock = await provider.getBlockNumber();
+const startingBlock = endingBlock - 10;
+const transferEvents = await contractERC20.queryFilter("Transfer", startingBlock, endingBlock); // Retrieve Transfer event within the 10 blocks
+```
+
+Listening events
+
+```
+contractERC20.on("Transfer", (from, to, value) => {
+    console.log(from, to, value)
+}); // Continuosly listening every Transfer event of contractERC20
+
+contractERC20.on("Transfer", (from, to, value) => {
+    console.log(from, to, value)
+}); // Listen just once
+```
+
+Event filtering
+
+- When a contract emits a log (fires an event), it can contain up to 4 indexed items. These indexed data items are hashed and included in a Bloom filter, which is a data structure that allows for efficient filtering
+
+```
+// event Transfer(address indexed from, address indexed to, uint256 amount)
+
+contractERC20.filters.Transfer(myAddress); // Retrieve only transfer from myAddress
+
+contractERC20.filters.Transfer(null, myAddress); // Retrieve only transfer to myAddress
+
+contractERC20.filters.Transfer(myAddress, otherAddress); // Retrieve only transfer from myAddress to otherAddress
+
+contractERC20.filters.Transfer(null, [myAddress, otherAddress]); // Retrieve only transfer to myAddress or otherAddress
+```
+
+Unit conversion
+
+- In Ethereum, the most used integer variables (`uint256`) are exceed the safe range of JavaScript integers. Thus, `Ethers` use BigInt class to securely perform the mathematical operations.
+
+```
+const bigint_one = 1n;
+const oneWei = ethers.getBigInt("1");
+console.log(bigint_one == oneWei); // True
+
+const oneGwei = ethers.getBigInt("1000000000");
+console.log(ethers.formatUnits(oneGwei, 0)); // '1000000000' wei
+console.log(ethers.formatUnits(oneGwei, "gwei")); // '1.0' gwei
+console.log(ethers.formatUnits(oneGwei, 9)); // '1.0' gwei
+console.log(ethers.formatUnits(oneGwei, "ether")); // '0.000000001' eth
+console.log(ethers.formatUnits(1000000000, "gwei")); // '1.0' eth
+console.log(ethers.formatEther(oneGwei)); // '0.000000001' eth, equivalent to formatUnits(value, "ether")
+
+console.log(ethers.parseUnits("1.0").toString()); // { BigNumber: "1000000000000000000" } wei
+console.log(ethers.parseUnits("1.0", "ether").toString()); // { BigNumber: "1000000000000000000" } wei
+console.log(ethers.parseUnits("1.0", 18).toString()); // { BigNumber: "1000000000000000000" } wei
+console.log(ethers.parseUnits("1.0", "gwei").toString()); // { BigNumber: "1000000000" } wei
+console.log(ethers.parseUnits("1.0", 9).toString()); // { BigNumber: "1000000000" } wei
+console.log(ethers.parseEther("1.0").toString()); // { BigNumber: "1000000000000000000" } wei, equivalent to parseUnits(value, "ether")
+```
 
 ### 2024.10.16