鏈上智能合約互動

更新於 發佈於 閱讀時間約 36 分鐘

0. 大綱Outline

  1. 以太坊交易
  2. 發起交易
  3. 與智能合約互動
  4. receive & fallback function

1. 舊以太坊交易

Ethereum Gas Tracker - 7 Gwei - Etherscan

//交易技術, 表示特定帳戶的交易數量,是計數器, 每發一筆交易一直累加
1. nonce: 0x15

//gas價格 用戶願意為每個gas單位支付的價格
// gas fee= gas price*gas amount 
2. gasPrice 0x4a817c880

//gas限制, 交易可以消耗的最大gas數量
3. gasLimit 0x5208

//接收著地址, 可以是人, 也可以是合約(比方說mint function)
4. to 0x353535353535353533535

//轉移的ETH數量, 這裡是0(native token, 原生幣)
5. value 0x0

//與交易相關的數據, 這裡是空(送這筆交易的附加訊息)
6. data 0x

// ------以上為送交易出去的資訊---------
//-------以下為用私鑰加密這筆簽名-------

// 簽名後產生的參數
7. v 0x1c

// 簽名後產生的參數
8. r 0x....(十六進制數據)

// 簽名後產生的參數
9. s 0x....(十六進制數據)

2. EIP-1559新交易格式

2021年提出

  • maxFeePerGas:用戶願意支付的每單位 gas 的最高價格。
  • maxPriorityFeePerGas:用戶願意支付給礦工的每單位 gas 的小費。(Base + Priority(可以自己決定, 多給礦工, 礦工先處理) = Gas Price
  • gasLimit:用戶願意為交易支付的最多 gas 單位數。(不要超過個區間)
  • accesslist: 如果可以預知能夠訪問到哪些合約地址, 可以比較好的去預測gas fee
  • chainId: 用chainlist去查

    ChainList

3. 以太坊三種基本交易

a. 轉帳

{
"from": "0x17eeeeeeeeeeeeee", //傳送者
"to": "ox302222222333333333", //接收者
"value": "1000000", // 以wei為單位
"data": "0xe, //16進制的附加訊息,有的話就是0xxxxeeee, 沒有的話就是0x
//比方說curve附加訊息出的狀況是語言版本差異造成駭客掏空池子的錢,網址如下
"gasLimit": "300000",
}

掏空並返還Curve池子錢的駭客留下的訊息

b. 智能合約互動

{
"from": "0x17eeeeeeeeeeeeee", //傳送者
"to": "ox302222222333333333", //接收者
"value": "1000000", // 以wei為單位
"data": "0xeeeeeeeeeeeeee, // 0x後面前八個字串表達function, 後面一整串, 表達帶進這個function的參數
//讓節點知道是跟哪個function互動(function selector)
//後面用abi.encode封裝成16進制的字串
"gasLimit": "300000",
}

Keccak-256 - Online Tools

4. 實際鏈上交易

https://etherscan.io/tx/0xbf636aa5c7405377b42bfc6b154a09f4458f5e91c9d256741e9f0ec96a7c4caa

範例合約

// 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;
}

//1. 開盲盒的功能
function openBlindBox() external onlyOwner{
isOpened = true;
}


//2. 設定NFT的baseURI的功能(盲盒)
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmXxZBg4RnGxC2dDxfUSAmxgGooHsoncPQgCLiNw8kj3Ls/";
}

//3. 查看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"));

}

// 4. 實作mint function的功能,主要用來demo確認用
function mint (address to, uint256 amount) external payable{
require(amount + counter <= maxSupply, "over max supply.");
//這個合約NFT是賣錢的, 一個NFT賣0.01ETH
require(amount * 10000000000000000 == msg.value, "balance error!");
// 迴圈批量鑄造NFT
for(uint256 i=0; i < amount ; i++){
// 鑄造 NFT, counter為NFT的tokenId
_mint(to, counter);
counter ++ ;
}
}

}

Ethereum Unit Converter | Ether to Gwei, Wei, Finney, Szabo ...

鏈上互動套件

  • web3.js
  • ether.js
  • viem

5. Ethers.js

  • js的函式庫, 用於與以太坊RPC交互的工具
  • 以太坊RPC(Remote Procedure Call)是一種通信協議,使開發者能夠通過網絡請求遠程執行各種操作,比如查詢區塊鏈數據、發送交易、部署智能合約等。
  • 註冊RPC

RPC Provider


範例code

import {ethers} from "ethers"; // 引入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();

6. 與智能合約互動


[Reference]

1. 鏈上互動

  1. Solidity 教學工作坊第 4 場: 鏈上智能合約互動


尋大神腳印, 亦步亦趨。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
1. Outline Takeaway Setting up Git and verifying installation Configuring your Git username and email Initializing a new Git repository Stagin
React Hooks 是 React 16.8 中引入的一組新的 API,允許你在函數組件中使用狀態和其他 React 特性,而不需要寫類組件。 狀態管理: useState 鉤子允許在函數組件中添加狀態。 副作用管理: useEffect 鉤子允許處理副作用,如數據獲取、訂閱和手動 DO
06. React Forms: Dynamically Add New Input Fields On Click || Learn React Through Mini Projects
07. React useContext Hook (User Context Example) || Learn React Through Mini Projects
Overview 1. Request Wallet Connection from Metamask get account function get account function 2. Set your smart contract address injected provide
1. ERC-20 ERC20 (KryptoCamp) ERC20 (Naz Dumansky) 多簽錢包的設計 - 23:02 🌳 基礎題 1 解答說明: 發行總量100億顆、位數 18 的代幣 - 07:15 🌳 自製 ERC20 交換腦西幣 (HaHow 朱西西) 2. ER
1. Outline Takeaway Setting up Git and verifying installation Configuring your Git username and email Initializing a new Git repository Stagin
React Hooks 是 React 16.8 中引入的一組新的 API,允許你在函數組件中使用狀態和其他 React 特性,而不需要寫類組件。 狀態管理: useState 鉤子允許在函數組件中添加狀態。 副作用管理: useEffect 鉤子允許處理副作用,如數據獲取、訂閱和手動 DO
06. React Forms: Dynamically Add New Input Fields On Click || Learn React Through Mini Projects
07. React useContext Hook (User Context Example) || Learn React Through Mini Projects
Overview 1. Request Wallet Connection from Metamask get account function get account function 2. Set your smart contract address injected provide
1. ERC-20 ERC20 (KryptoCamp) ERC20 (Naz Dumansky) 多簽錢包的設計 - 23:02 🌳 基礎題 1 解答說明: 發行總量100億顆、位數 18 的代幣 - 07:15 🌳 自製 ERC20 交換腦西幣 (HaHow 朱西西) 2. ER
你可能也想看
Google News 追蹤
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~