Interact Smart Contract ด้วย Ethers.js

วิธีการ Interact กับ Smart Contract ด้วยการใช้ Ethers.js ไม่ว่าจะเป็นการ call function ธรรมดา ที่ไม่ได้ไปยุ่งเกี่ยวกับ state ไม่ต้องเสียค่า gas หรือการ send transaction ที่ต้องมี signer (ทั้งจาก Private Key หรือจากการ sign, การ confirm ผ่าน Client) ทั้งหมด สามารถทำผ่าน Ethers.js ได้ทั้งหมด
แต่ในตัวอย่างนี้ จะเป็นตัวอย่างง่ายๆ ในการ call function เท่านั้นนะครับ โดยตัวอย่าง ผมยกตัวอย่าง การ call function ของเหรียญ Token USDC ซึ่งเป็น ERC-20 ครับ
- เหรียญ USDC บน Ethereum mainnet - USDC บน Ethereum Mainnet
รายละเอียด ERC-20 - https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
Setup Project
เราใช้ JavaScript ธรรมดา (Node.js) เริ่มต้น init project แล้วก็ติดตั้ง ethers
npm init -y
npm install ethers
ตัวอย่างผมใช้ Ethers.js version 6 นะครับ ถ้าใช้ v5 สามารถดู migrating guide ได้ที่นี่ https://docs.ethers.org/v6/migrating/
วิธีการต่อ Blockchain จาก JS Client
ตัวอย่างนี้ จะใช้การต่อผ่าน RPC (ต่อได้หลายรูปแบบ)
โค๊ดในการ connect RPC จะเป็นแบบนี้ (ลองสร้างไฟล์ app.js ขึ้นมา)
const { ethers } = require('ethers')
const RPC_URL = 'https://rpc.ankr.com/eth'
const provider = new ethers.JsonRpcProvider(RPC_URL)
ส่วน RPC URL เราสามารถหาได้จาก Infura, Alchemy หรือในตัวอย่างผมใช้ Public RPC ของ Ankr คือ https://rpc.ankr.com/eth
เมื่อเราต่อ Blockchain ผ่าน RPC ได้แล้ว เราสามารถดึงข้อมูลต่างๆ ของ blockchain ได้ เช่น blocknumber, balance, transaction เช่น
ตัวอย่าง การดู block number ของ ethereum
const { ethers } = require('ethers')
const RPC_URL = 'https://rpc.ankr.com/eth'
const run = async () => { const provider = new ethers.JsonRpcProvider(RPC_URL)
const block = await provider.getBlockNumber() console.log('block', block)}
run() .then() .catch((error) => console.log(error))
ตัวอย่างการดู balance
ตัวอย่าง การดู balance ของ wallet address ที่เราต้องการ (ผมดูของ vitalik.eth)
const { ethers } = require('ethers')
const RPC_URL = 'https://rpc.ankr.com/eth'
const run = async () => { const provider = new ethers.JsonRpcProvider(RPC_URL)
const balance = await provider.getBalance('vitalik.eth') console.log('balance', balance)}
run() .then() .catch((error) => console.log(error))
แต่ผลลัพธ์ที่ได้จะเป็นตัวเลข ที่เป็นหน่วน wei เราต้องแปลงเป็น ethers ใช้ formatEthers
ของ ethers.js ได้เลย
const { formatEther } = require('ethers')console.log('balance', formatEther(balance))
ทดสอบรันดู
node app.jsbalance 5149.639854481921682572
ค่าที่ได้จะเท่ากับที่ etherscan
ต่อ Contract
เราสามารถ connect Contract เพื่อ call function ของ Contract ได้ เช่น ผมจะ connect USDC เพื่อเรียก function ของ USDC เช่น totalSupply() balanceOf
สิ่งที่ต้องมีคือ
contractAddress
- Contract Address ที่เราจะ connectABI
- ABI ของ Contract ถ้าเป็น ERC20 ส่วนใหญ่ ใช้ร่วมกันได้
หรือ Copy Contract ABI จาก หน้า etherscan แต่อีกวิธีที่ง่าย คือ ethers.js รองรับ ABI แบบ Human Readable เราไม่ต้องใช้ JSON ก็ได้ ตัว method ERC20 มีแบบนี้ใช่มั้ย
function name() public view returns (string)function symbol() public view returns (string)function decimals() public view returns (uint8)function totalSupply() public view returns (uint256)function balanceOf(address _owner) public view returns (uint256 balance)function transfer(address _to, uint256 _value) public returns (bool success)function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)function approve(address _spender, uint256 _value) public returns (bool success)function allowance(address _owner, address _spender) public view returns (uint256 remaining)
ทีนี้ ผมจะเอาแค่ decimals, balanceOf และ totalSupply ผมก็ทำแบบนี้แทน
const ERC20ABI = [ 'function symbol() public view returns (string)', 'function decimals() public view returns (uint8)', 'function totalSupply() public view returns (uint256)', 'function balanceOf(address _owner) public view returns (uint256 balance)']
ทีนี้ เราก็สามารถ connect USDT ได้แบบนี้
const provider = new ethers.JsonRpcProvider(RPC_URL)const usdcContract = new ethers.Contract(USDC, ERC20ABI, provider)
โค๊ดเต็มๆ คือ
const { ethers, formatUnits } = require('ethers')
const USDC = '0xdac17f958d2ee523a2206206994597c13d831ec7'const RPC_URL = 'https://rpc.ankr.com/eth'
const ERC20ABI = [ 'function symbol() public view returns (string)', 'function decimals() public view returns (uint8)', 'function totalSupply() public view returns (uint256)', 'function balanceOf(address _owner) public view returns (uint256 balance)']
const run = async () => { const provider = new ethers.JsonRpcProvider(RPC_URL) const usdcContract = new ethers.Contract(USDC, ERC20ABI, provider)
const decimals = await usdcContract.decimals()
const symbol = await usdcContract.symbol() const totalSupply = await usdcContract.totalSupply() console.log(`${symbol} totalSupply : `, formatUnits(totalSupply, decimals))
const balance = await usdcContract.balanceOf('vitalik.eth') console.log('balance', formatUnits(balance, decimals))}
run() .then() .catch((error) => console.log(error))
ลองรันดูว่าได้ผลลัพธ์เป็นยังไง
node app.js
ผลลัพธ์
USDT totalSupply : 35283904986.788565balance 38.13
ทำไมใช้ formatUnits
? เพราะว่า formatEthers จะแปลงเลขด้วยการใช้ 18 decimals แต่ว่า USDT decimals ไม่ใช่ 18 เลยต้องเรียก function decimals เพื่อให้ได้ decimals จริงๆ และแปลงด้วย formatUnits นั่นเอง
🎉 จบแล้ว ตัวอย่างง่ายๆ ในการ interact Smart Contract ด้วย Ethers.js
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust