{
"nonce": "0x15", // 交易計數,表示特定賬戶的交易數量
"gasPrice": "0x4a817c800", // Gas 價格,用戶願意為每個 Gas 單位支付的價格
"gasLimit": "0x5208", // Gas 限制,交易可以消耗的最大 Gas 數量
"to": "0x3535353535353535353535353535353535353535", // 接收者的地址
"value": "0x0", // 轉移的 ETH 數量,這裡是 0
"data": "0x", // 與交易相關的數據,這裡是空
"v": "0x1c", // 簽名參數
"r": "0x...(十六進制數據)", // 簽名參數
"s": "0x...(十六進制數據)" // 簽名參數
}
{
"nonce": "0x1", // 交易計數,表示特定賬戶的交易數量
"maxPriorityFeePerGas": "0x3B9ACA00", // 最大優先費用/每單位Gas(願意支付給礦工的額外費用)
"maxFeePerGas": "0x4E3B29200", // 最大費用/每單位Gas(包括基本費用和優先費用)
"gasLimit": "0x5208", // Gas限制,交易可以消耗的最大Gas數量
"to": "0xAbc123...(接收者地址)", // 接收者的地址
"value": "0x2386F26FC10000", // 轉移的ETH數量
"data": "0x...", // 與交易相關的數據
"accessList": [ // 訪問列表(可選)
{
"address": "0x...(地址)",
"storageKeys": [
"0x...(存儲鍵)",
"0x...(另一個存儲鍵)"
]
}
// 可能還有更多地址和存儲鍵
],
"chainId": "0x1", // 指定交易所屬的以太坊網絡
"type": "0x2", // 交易類型,0x2 表示 EIP-1559 交易
"v": "0x25", // 簽名參數
"r": "0x1C...(十六進制數據)", // 簽名參數
"s": "0x3D...(十六進制數據)" // 簽名參數
}
{
"from": "0x17F6adf05b64C033f8f5Fb360d6Be467ad9508BF",
"to": "0x1a3c……4d8",
"value": "10000000000000",
"data": "0x",
"gasLimit": "300000",
....
}
-> 含附加訊息
{
"from": "0x17F6adf05b64C033f8f5Fb360d6Be467ad9508BF",
"to": "0x1a3c……4d8",
"value": "10000000000000",
"data": "0x0118b9c115129006502d63ae0eb3c701502c4a99e1320803b000ce38ebc9805a",
"gasLimit": "300000",
....
}
https://etherscan.io/tx/0x23c4799784c91023204bd68a94ec7a963486f2485dc43c13d8b804d5301b8041
{
"from": "0x17F6adf05b64C033f8f5Fb360d6Be467ad9508BF",
"to": "0x1a3c……4d8",
"value": "10000000000000",
"data": "0x0118b9c115129006502d63ae0eb3c701502c4a99e1320803b000ce38ebc9805a",
"gasLimit": "300000",
....
}
https://etherscan.io/tx/0xbf636aa5c7405377b42bfc6b154a09f4458f5e91c9d256741e9f0ec96a7c4caa
定義:一個Javascript的函式庫,用於與以太坊RPC交互的工具。
Ethers 更加簡單直觀,有內嵌安全檢查。
運作架構圖
圖源:https://docs.alchemy.com/docs/ethereum-frontend-libraries
mkdir <my-typescript-project>
cd <my-typescript-project>
npm init -y
npm install typescript --save-dev
tsc --init
npm install ethers@5.7.2 --save
npm i --save-dev @types/node
npm install dotenv
npm install dotenv
import {ethers} from "ethers";
import dotenv from 'dotenv';
dotenv.config();
const RPC_URL = process.env.RPC_URL;
const PRIVATE_KEY= process.env.PRIVATE_KEY;
const main = async()=>{
if (!RPC_URL) {
throw new Error("RPC_URL 環境變數未設置。");
}
if (!PRIVATE_KEY) {
throw new Error("PRIVATE_KEY 環境變數未設置。");
}
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
try {
// 創建交易
const tx = {
to: signer.address,
value: ethers.utils.parseEther("0.1"),
};
// 發送交易
const transaction = await signer.sendTransaction(tx);
console.log("交易已發送:", transaction.hash);
// 等待交易被確認
const receipt = await transaction.wait();
console.log("交易已確認:", receipt);
} catch (error) {
console.error("交易錯誤:", error);
}
}
main();
import {ethers} from "ethers";
import dotenv from 'dotenv';
dotenv.config();
const RPC_URL = process.env.RPC_URL;
const PRIVATE_KEY= process.env.PRIVATE_KEY;
const main = async()=>{
if (!RPC_URL) {
throw new Error("RPC_URL 環境變數未設置。");
}
if (!PRIVATE_KEY) {
throw new Error("PRIVATE_KEY 環境變數未設置。");
}
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
try {
// 創建交易
const tx = {
to: signer.address,
value: ethers.utils.parseEther("0.1"),
data: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('hello world')),
};
// 發送交易
const transaction = await signer.sendTransaction(tx);
console.log("交易已發送:", transaction.hash);
// 等待交易被確認
const receipt = await transaction.wait();
console.log("交易已確認:", receipt);
} catch (error) {
console.error("交易錯誤:", error);
}
}
main();
import {ethers} from "ethers";
import dotenv from 'dotenv';
dotenv.config();
const RPC_URL = process.env.RPC_URL;
const PRIVATE_KEY= process.env.PRIVATE_KEY;
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY!, provider);
const main = async()=>{
if (!process.env.RPC_URL) {
throw new Error("RPC_URL 環境變數未設置。");
}
if (!process.env.PRIVATE_KEY) {
throw new Error("PRIVATE_KEY 環境變數未設置。");
}
// function & params
const functionSignature = "mint(address,uint256)";
const addressParam = "0x3D71D7DC971e9f8405f287A340E8f65a7a1d392a";
const amountParam = 3;
// function selector
const functionSelector = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(functionSignature)).slice(0, 10);
// abi.encode params
const mintParams = abiEncode(['address','uint256'], [addressParam, amountParam]);
const finalData = functionSelector + mintParams.slice(2);
sendTx("0xf16EE2377aE7CE17D664b2b27B0eefc568b6a969", "0.03", finalData );
}
const abiEncode = (types: string[], params: any)=>{
const encodeData = ethers.utils.defaultAbiCoder.encode(types, params);
return encodeData;
}
const sendTx = async(to: string, value: string, data: string, )=>{
try {
// 創建交易
const tx = {
to: to, //合約地址
value: ethers.utils.parseEther(value),
data: data,
gasLimit: ethers.utils.hexlify(1000000),
};
//
const transaction = await signer.sendTransaction(tx);
console.log("交易已發送:", transaction);
// 等待交易被確認
const receipt = await transaction.wait();
console.log("交易已確認:", receipt);
} catch (error) {
console.error("交易錯誤:", error);
}
}
main();
import {ethers} from "ethers";
import dotenv from 'dotenv';
dotenv.config();
const RPC_URL = process.env.RPC_URL;
const PRIVATE_KEY= process.env.PRIVATE_KEY;
const main = async()=>{
if (!RPC_URL) {
throw new Error("RPC_URL 環境變數未設置。");
}
if (!PRIVATE_KEY) {
throw new Error("PRIVATE_KEY 環境變數未設置。");
}
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
const contract_address = "0x9c6471C2a6099993299Ca7E1a7E5a22D1F26902C";
const nft_abi = '[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openBlindBox","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]';
const contract = new ethers.Contract(contract_address, nft_abi, signer);
try {
const res = await contract.mint("0x3D71D7DC971e9f8405f287A340E8f65a7a1d392a",3, {value: ethers.utils.parseEther("0.03")});
console.log("交易資訊", res);
await res.wait();
} catch (error) {
console.log(error);
}
}
main();
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MyNFT is ERC721{
using Strings for uint256;
address owner;
uint256 public maxSupply = 10; // 最大發行量
bool private isOpened = false;//盲盒是否打開
uint256 public counter = 0;
modifier onlyOwner{
require(msg.sender == owner);
_;
}
constructor (string memory _name, string memory _symbol) ERC721(_name, _symbol){
owner = msg.sender;
}
//開盲盒
function openBlindBox() external onlyOwner{
isOpened = true;
}
//設定NFT的baseURI(盲盒)
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmXxZBg4RnGxC2dDxfUSAmxgGooHsoncPQgCLiNw8kj3Ls/";
}
//查看NFT Metadata網址
function tokenURI(uint256 tokenId) public view override returns (string memory) {
if (!isOpened){
return _baseURI();
}
return string(abi.encodePacked("ipfs://QmWQcaFFCm9ofyVN2ZwGbTGopLEbQ6QSc2Xn1C7ekKAYDF/", tokenId.toString(), ".json"));
}
// 實作mint function,主要用來demo確認用
function mint (address to, uint256 amount) external payable{
require(amount + counter <= maxSupply, "over max supply.");
require(amount * 10000000000000000 == msg.value, "balance error!");
// 迴圈批量鑄造NFT
for(uint256 i=0; i < amount ; i++){
// 鑄造 NFT, counter為NFT的tokenId
_mint(to, counter);
counter ++ ;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReceiveExample {
event Received(address sender, uint amount);
// Receive function must be declared as external and payable.
receive() external payable {
// Emit an event with the sender and the amount received
emit Received(msg.sender, msg.value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FallbackExample {
event Log(uint gas);
// Fallback function must be declared as external.
fallback() external payable {
// Emit an event with the current gas left
emit Log(gasleft());
}
}