更新於 2024/06/12閱讀時間約 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 場: 鏈上智能合約互動


    分享至
    成為作者繼續創作的動力吧!
    © 2024 vocus All rights reserved.