整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          簡單的html網頁(社保計算器)

          lt;!DOCTYPE html>

          <html>

          <head>

          <meta charset="UTF-8">

          <title>個人社保計算器--網頁版</title>

          <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>

          <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>

          <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

          <script><!--腳本-->

          function calc(){

          //1.把用戶輸入的工資拿到

          //jQuery函數

          sal = $("#salary").val();

          //2.把sal轉成數字

          salnum = parseInt(sal);

          //3.開始計算

          //3.1 算各種數據

          ylgr = salnum * 0.08;

          ylgs = salnum * 0.2;

          ybgr = salnum * 0.02;

          ybgs = salnum * 0.06;

          sygr = salnum * 0.0005;

          sygs = salnum * 0.0015;

          gsgs = salnum * 0.0005;

          sybx = salnum * 0.0008;

          gjjgr = salnum * 0.12;

          gjjgs = salnum * 0.12;

          grhj = ylgr+ybgr+sygr+gjjgr;

          gshj = ylgs+ybgs+sygs+gsgs+sybx+gjjgs;

          total = grhj+gshj;

          //4.把計算結果放到格子里

          $("#ylgr").text(ylgr);

          $("#ylgs").text(ylgs);

          $("#ybgr").text(ybgr);

          $("#ybgs").text(ybgs);

          $("#sygr").text(sygr);

          $("#sygs").text(sygs);

          $("#gsgs").text(gsgs);

          $("#sybx").text(sybx);

          $("#gjjgr").text(gjjgr);

          $("#gjjgs").text(gjjgs);

          $("#grhj").text(grhj);

          $("#gshj").text(gshj);

          $("#total").text(total);


          }

          </script>

          </head>

          <body>

          <div class="container text-center">

          <h1>個人社保計算器</h1>

          <table class="table table-bordered">

          <tr>

          <td>工資</td>

          <td colspan="3">

          <input id="salary" class="form-control" placeholder="請輸入工資" type="text" >

          </td>

          <td>

          <button onclick="calc()" class="btn btn-danger btn-block">計算</button>

          </td>

          </tr>

          <tr class="bg-primary">

          <td>險種</td>

          <td>個人%</td>

          <td>個人</td>

          <td>公司%</td>

          <td>公司</td>

          </tr>

          <tr>

          <td>養老</td>

          <td>8%</td>

          <td id="ylgr"></td>

          <td>20%</td>

          <td id="ylgs"></td>

          </tr>

          <tr>

          <td>醫保</td>

          <td>2%</td>

          <td id="ybgr"></td>

          <td>6%</td>

          <td id="ybgs"></td>

          </tr>

          <tr>

          <td>失業</td>

          <td>0.5%</td>

          <td id="sygr"></td>

          <td>1.5%</td>

          <td id="sygs"></td>

          </tr>

          <tr>

          <td>工傷</td>

          <td></td>

          <td></td>

          <td>0.5%</td>

          <td id="gsgs"></td>

          </tr>

          <tr>

          <td>生育</td>

          <td></td>

          <td></td>

          <td>0.8%</td>

          <td id="sybx"></td>

          </tr>

          <tr>

          <td>公積金</td>

          <td>12%</td>

          <td id="gjjgr"></td>

          <td>12%</td>

          <td id="gjjgs"></td>

          </tr>

          <tr>

          <td>合計</td>

          <td>個人合計</td>

          <td id="grhj"></td>

          <td>公司合計</td>

          <td id="gshj"></td>

          </tr>

          <tr>

          <td>總額</td>

          <td colspan="4" id="total"></td>

          </tr>

          </table>

          開發者:一字賭團隊2021

          </div>

          </body>

          </html>

          本教程中,您將學習如何開發一個具有利息累加、削減和存款/取款功能的簡單質.押. DApp。

          在過去課程中,我們學習了很多不同的Solidity和Javascript原語,這些原語為我們提供了Web3開發的基本構件。

          我們學會了如何從頭開始使用Hardhat,建立我們自己的前臺,甚至編寫Solidity。

          雖然所有這些技能對于希望建立堅實基礎的開發者來說都是非常有價值的,但也有一些工具可以幫助抽象出一些復雜的環境設置和依賴關系,使構建者可以更容易地進行修補

          我們推薦的這些工具之一是Scaffold-eth[1]!

          Scaffold-eth的核心是為以太坊上的快速原型開發提供了一個現成的堆棧,使開發者能夠獲得最先進的工具來快速學習/交付基于以太坊的dApp。

          使用Scaffold-eth和Alchemy,你可以輕松地在區塊鏈上合成和部署代碼。

          在本教程中,我們將使用SpeedRunEthereum[2] Challenge #1[3]的基礎代碼,并且共同構建一個簡單的質.押. dApp。

          如果你不熟悉加密貨幣質.押.,它最好被概括為將持有的加密貨幣鎖定/存入DeFi協議或智能合約以獲得利息的過程。

          質.押.加密貨幣已成為許多DeFi協議的基石,并允許開發人員創建復雜的金融衍生產品。

          雖然大多數DeFi質.押.合同都非常復雜,但我們將通過一個最基本的合同來學習關鍵概念。

          我們將一起學習以下有關質.押.的構件。

          1. 用Scaffold-Eth構建:? 一起破解前端 ? 打造 Solidity “后端”
          2. 將ETH從錢包轉移到智能合約,反之亦然
          3. 利用Solidity修改器

          讓我們從了解Scaffold-Eth的工作原理開始吧!

          1. 下載Scaffold-Eth

          在本教程中,我們將使用Scaffold-Eth開發者環境來制作我們的智能合約,并將我們的前端UI放在一起。

          在開始之前,我想傳達幾個重要的細節,讓大家牢記在心!

          Scaffold-Eth在抽象化環境設置和前端依賴方面非常棒,這使它成為一個強大的工具。

          雖然有很多功能是由Scaffold-Eth自動處理的,但當你對整個開發者環境有了更扎實的掌握后,深入到代碼中去了解其中一些功能是如何產生的,這一點很重要。

          讓我們從Fork SpeedRunEthereum[4]Challenge #1[5] 的基礎代碼庫開始。

          git clone https://github.com/scaffold-eth/scaffold-eth-challenges.git challenge-1-decentralized-staking
          
          cd challenge-1-decentralized-staking
          
          git checkout challenge-1-decentralized-staking
          
          yarn install

          如果你已經成功跟上,你將能夠在你的基礎文件目錄中出現一個名為challenge-1-decentralized-staking的新文件夾。

          在運行上述命令后,我們留下了一個充滿文件的大文件夾。

          即使在繼續學習代碼之前,我們也應該熟悉Scaffold-Eth中關鍵文件的存儲位置,這樣我們就知道應該把重點放在哪里。

          在本教程中,我們將主要在Staker.solApp.jsx上工作。

          2. 設置你的環境

          接下來,你需要為以下三個命令建立三個獨立的終端。

          啟動你的React前端。

          yarn start

          啟動你的Hardhat后端。

          yarn chain

          編譯、部署和發布你的package/contracts文件中的所有合同。

          yarn deploy

          每當你更新你的合同時,運行yarn deploy --reset來 "刷新 "Scaffold-Eth中的合同。

          很好! 現在你應該可以在http://localhost:3000/,看到這個倉庫的UI前臺了。

          3. 熟悉Scaffold-Eth

          雖然我知道你很想開始寫代碼,但還有一些細節需要處理

          在我們的默認視圖中,我們有兩個標簽--Staker UI & Debug Contracts

          來吧,在你的前端頁面上來回切換,看看不同的功能。

          Staker UI包含了所有我們將主要與之互動的前端組件。

          如果你點擊所提供的按鈕,你會發現大多數按鈕還沒有完全連接起來,你會立即遇到錯誤。

          看一下Staker UI。你會注意到它是如此的簡陋的! 這就是我們主要要改變的東西。

          由于以太坊上的任何鏈上互動都需要testnet ETH,所以你需要本地testnet ETH來開始砍價。

          首先,抓住你的本地主機錢包地址。

          點擊右上角的 "復制 "按鈕。

          接下來,前往左下角。你將能夠在這里訪問本地龍頭。

          • ? 要么在打開的字段中復制/粘貼你的地址,要么點擊 "錢包 "圖標
          • ? 在擴展視圖中粘貼您的地址
          • ? 給自己發送一些測試ETH

          在為你的本地錢包充值后,你就可以與你的合約進行互動了

          第二個標簽,Debug Contracts,是另一個前端顯示,它包含了Scaffold-Eth的一個超級大功能!在這個標簽中,你可以看到你的合同。

          一旦你yarn deploy你的合約,并配置它正確地讀取合約數據,它將自動生成一個裸露的UI,允許你與你的合約功能互動。

          例如,在下面的例子中,我們可以通過我們的智能合約讀取和寫入信息,只需放入參數并點擊 "發送"。有了Scaffold-Eth,我們不需要只使用CLI命令,而是有一個更直觀的原型設計方式。

          如果你想在Debug Contracts 標簽頁中存儲和查看一個變量,請確保將該變量設置為public!

          棒極了!

          現在我們已經熟悉了Scaffold-Eth,我們可以更深入地研究代碼了。

          4. 潛入Solidity

          看一下我們的Staker.sol文件,我們發現我們有一個相當空的Solidity文件,里面有一堆注釋,說明需要填寫的內容。

          由于教程偏離了Scaffold-Eth的 Challenge #1,我們可以忽略這些注釋,從以下代碼開始。

          pragma solidity >=0.6.0 <0.7.0;
          
          import "hardhat/console.sol";
          import "./ExampleExternalContract.sol";
          
          contract Staker {
            ExampleExternalContract public exampleExternalContract;
            
            constructor(address exampleExternalContractAddress) public {
                exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);
            }
            
          }

          項目參數

          在寫出我們的智能合約代碼之前,讓我們來看看我們期望我們的質.押.DApp如何工作

          1. 1. 為了簡單起見,我們只希望有一個用戶與我們的質.押.DApp互動
          2. 2. 我們需要能夠從Staker合約中存款和取款。? 質.押.是一個一次性的行為,這意味著一旦我們質.押.,就不能再重新質.押.。? 從合約中取款會移除整個本金余額和任何應計利息
          3. 3. Staker合約的利息支付率為每秒鐘0.1個ETH,存入的ETH有資格獲得利息累積。
          4. 4. 合同部署后,Staker合同應該從Hh2時間戳計數器開始。第一個期限應設置為2分鐘,第二個期限設置為4分鐘? 2分鐘的期限決定了釘子戶能夠存入資金的時期。(在t=0分鐘和t=2分鐘之間,質.押.用戶可以存款)? 從存入資金到2分鐘期限之間發生的所有區塊都是有效的應計利息。? 在2分鐘的提款期限過后,質.押.用戶可以提取全部本金余額和任何應計利息,直到4分鐘的期限到來。? 在額外的2分鐘提款窗口過后,用戶被阻止提取他們的資金,因為他們已經超時了。
          5. 5. 如果質.押.用戶還有資金,我們可以調用最后一個函數,將資金 "鎖定 "在已經預裝在Scaffold-Eth環境中的外部合同中,即ExampleExternalContract.sol。

          雖然上面列出的質.押.參數可能看起來有點復雜,但許多現實生活中的質.押.dApps都有類似的基元,用戶有一個有限的存款和提款期。

          而且,許多DApps將抑制 "非生產性 "資本,這些資本在質.押.期結束后只是閑置在那里。

          有時,DeFi協議甚至可能在等待期結束后吸收未付的存款,這與我們在教程中所說的最后一個參數類似。

          Solidity Mappingss

          在我們的智能合約中,我們將需要兩個映射來幫助我們存儲一些數據。

          特別是,我們需要一些東西來跟蹤。

          1. 1. 有多少ETH被存入合約中
          2. 2. 存款發生的時間

          我們可以通過以下方式實現這一目標。

          mapping(address => uint256) public balances; 
          mapping(address => uint256) public depositTimestamps;

          公共變量

          根據上面列出的準則[6],我們還需要一些不同的變量。

          uint256 public constant rewardRatePerSecond = 0.1 ether; 
          uint256 public withdrawalDeadline = block.timestamp + 120 seconds; 
          uint256 public claimDeadline = block.timestamp + 240 seconds; 
          uint256 public currentBlock = 0;

          獎勵率設定了質.押.本金的ETH的發放利率。

          提款和索賠的最后期限幫助我們設定質.押.機制開始/結束的最后期限。

          最后,我們有一個變量,用來保存當前區塊。

          我們使用block.timestamp + XXX seconds來創建最后期限,正好是我們的合約啟動后的XXX秒。作為一種計時機制,這肯定有點 "天真";你能想出更好的方法來實現這一點,例如,它更具有通用性?

          事件

          盡管我們不會將事件推送到我們的前端,但我們仍然應該確保我們在合同的關鍵部分發出事件,以確保我們保持最佳的編程實踐。

          event Stake(address indexed sender, uint256 amount); 
          event Received(address, uint); 
          event Execute(address indexed sender, uint256 amount);

          現在我們已經鎖定了關鍵參數/變量,我們可以繼續制作我們將在教程中使用的具體函數。

          只讀的時間函數

          正如項目參數中所述,許多不同的質.押.DApp的功能都受制于 "時間鎖",在特定的時間點啟用/禁止某些行動。

          在這里,我們有兩個不同的功能來管理提款窗口的開始和結束。

            function withdrawalTimeLeft() public view returns (uint256 withdrawalTimeLeft) {
              if( block.timestamp >= withdrawalDeadline) {
                return (0);
              } else {
                return (withdrawalDeadline - block.timestamp);
              }
            }
          
            function claimPeriodLeft() public view returns (uint256 claimPeriodLeft) {
              if( block.timestamp >= claimDeadline) {
                return (0);
              } else {
                return (claimDeadline - block.timestamp);
              }
            }

          這兩個函數在設計上其實都非常熟悉。

          它們都有一個標準的if -> else語句。

          條件只是檢查當前時間是否大于或小于公共變量部分[7]規定的最后期限。

          如果當前時間大于預先安排的最后期限,我們就知道最后期限已過,并返回0以表示 "狀態變化 "已經發生。

          否則,我們只是返回在最后期限到來之前的剩余時間。

          修改器

          對于一個更深入的修改器的例子,請看 Solidity By Example[8]

          簡而言之,Solidity 修改器是可以在函數調用之前和/或之后運行的代碼片段。

          雖然它們有許多不同的用途,但最常見和最基本的用例之一是在特定條件未完全滿足的情況下限制對某些功能的訪問。

          在本教程中,我們將精確地使用修改器來幫助對關鍵功能進行把關,這些功能決定了我們的入股、提款和返還功能。

          下面是我們使用的三個修改器。

            modifier withdrawalDeadlineReached( bool requireReached ) {
              uint256 timeRemaining = withdrawalTimeLeft();
              if( requireReached ) {
                require(timeRemaining == 0, "Withdrawal period is not reached yet");
              } else {
                require(timeRemaining > 0, "Withdrawal period has been reached");
              }
              _;
            }
          
            modifier claimDeadlineReached( bool requireReached ) {
              uint256 timeRemaining = claimPeriodLeft();
              if( requireReached ) {
                require(timeRemaining == 0, "Claim deadline is not reached yet");
              } else {
                require(timeRemaining > 0, "Claim deadline has been reached");
              }
              _;
            }
          
            modifier notCompleted() {
              bool completed = exampleExternalContract.completed();
              require(!completed, "Stake already completed!");
              _;
            }

          修改器 withdrawalDeadlineReached(bool requireReached) &claimDeadlineReached(bool requireReached)都接受一個布爾參數,并檢查以確保其各自的最后期限為真或假。

          修改器notCompleted()的操作方式類似,但實際上它的性質更復雜一點,盡管它包含的代碼行數更少。

          它實際上是從Staker外部的合同中調用一個函數 completed(),并檢查它的返回值是真還是假,以確認該標志是否被切換。

          現在,讓我們在接下來的幾個函數上實現我們剛剛創建的修改器,用閘門限制訪問。

          存款/質.押.功能

          在我們的入金函數中,我們使用先前創建的修改器,將withdrawingDeadlineReached()中的參數設置為false,將claimDeadlineReached()設置為false,因為我們不希望這兩個期限已經過去。

            // Stake function for a user to stake ETH in our contract
            
            function stake() public payable withdrawalDeadlineReached(false) claimDeadlineReached(false) {
              balances[msg.sender] = balances[msg.sender] + msg.value;
              depositTimestamps[msg.sender] = block.timestamp;
              emit Stake(msg.sender, msg.value);
            }

          該函數的其余部分在一個典型的 "存款 "場景中是相當標準的,我們的余額映射被更新以包括送入的資金。

          我們還用存款的當前時間來設置我們的存款時間戳,這樣我們就可以在以后的利息計算中訪問這個存儲值。

          提款功能

          在我們的取款函數中,我們再次使用先前創建的修改器,但這次我們希望drawalDeadlineReached()為真, claimDeadlineReached()為假。

          這組修改器/參數意味著我們處于提款窗口的最佳位置,因為提款時間到了,不會有任何處罰,而且我們還能得到利息。

            /*
            Withdraw function for a user to remove their staked ETH inclusive
            of both the principle balance and any accrued interest
            */
            
            function withdraw() public withdrawalDeadlineReached(true) claimDeadlineReached(false) notCompleted{
              require(balances[msg.sender] > 0, "You have no balance to withdraw!");
              uint256 individualBalance = balances[msg.sender];
              uint256 indBalanceRewards = individualBalance + ((block.timestamp-depositTimestamps[msg.sender])*rewardRatePerBlock);
              balances[msg.sender] = 0;
          
              // Transfer all ETH via call! (not transfer) cc: https://solidity-by-example.org/sending-ether
              (bool sent, bytes memory data) = msg.sender.call{value: indBalanceRewards}("");
              require(sent, "RIP; withdrawal failed :( ");
            }

          該函數的其余部分做了幾個重要步驟。

          1. 1. 它檢查以確保試圖提取ETH的人實際上有一個非零的股份。
          2. 2. 它通過計算從存款到取款的區塊數,并乘以我們的利息常數,來計算欠下的ETH的利息金額。
          3. 3. 它將用戶的余額質.押.ETH設為0,這樣就不會發生重復計算。
          4. 4. 它將ETH從智能合約轉移回用戶的錢包。

          執行返還功能

          在這里,我們希望 claimDeadlineReached() 為真,因為非生產性資金的返還只能在4分鐘后發生。

          同樣地,我們希望notCompleted為真,因為這個DApp只設計為單一用途。

            /*
            Allows any user to repatriate "unproductive" funds that are left in the staking contract
            past the defined withdrawal period
            */
            
            function execute() public claimDeadlineReached(true) notCompleted {
              uint256 contractBalance = address(this).balance;
              exampleExternalContract.complete{value: address(this).balance}();
            }

          其余的功能。

          1. 1. 抓取Staker合約中的ETH的當前余額
          2. 2. 將ETH發送到repo的exampleExternalContract中。

          如果你到目前為止一直跟著Solidity走,你的Staker.sol應該是下面這個樣子。

          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.4;
          
          import "hardhat/console.sol";
          import "./ExampleExternalContract.sol";
          
          contract Staker {
          
            ExampleExternalContract public exampleExternalContract;
          
            mapping(address => uint256) public balances;
            mapping(address => uint256) public depositTimestamps;
          
            uint256 public constant rewardRatePerSecond = 0.1 ether;
            uint256 public withdrawalDeadline = block.timestamp + 120 seconds;
            uint256 public claimDeadline = block.timestamp + 240 seconds;
            uint256 public currentBlock = 0;
          
            // Events
            event Stake(address indexed sender, uint256 amount);
            event Received(address, uint);
            event Execute(address indexed sender, uint256 amount);
          
            // Modifiers
            /*
            Checks if the withdrawal period has been reached or not
            */
            modifier withdrawalDeadlineReached( bool requireReached ) {
              uint256 timeRemaining = withdrawalTimeLeft();
              if( requireReached ) {
                require(timeRemaining == 0, "Withdrawal period is not reached yet");
              } else {
                require(timeRemaining > 0, "Withdrawal period has been reached");
              }
              _;
            }
          
            /*
            Checks if the claim period has ended or not
            */
            modifier claimDeadlineReached( bool requireReached ) {
              uint256 timeRemaining = claimPeriodLeft();
              if( requireReached ) {
                require(timeRemaining == 0, "Claim deadline is not reached yet");
              } else {
                require(timeRemaining > 0, "Claim deadline has been reached");
              }
              _;
            }
          
            /*
            Requires that the contract only be completed once!
            */
            modifier notCompleted() {
              bool completed = exampleExternalContract.completed();
              require(!completed, "Stake already completed!");
              _;
            }
          
            constructor(address exampleExternalContractAddress){
                exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);
            }
          
            // Stake function for a user to stake ETH in our contract
            function stake() public payable withdrawalDeadlineReached(false) claimDeadlineReached(false){
              balances[msg.sender] = balances[msg.sender] + msg.value;
              depositTimestamps[msg.sender] = block.timestamp;
              emit Stake(msg.sender, msg.value);
            }
          
            /*
            Withdraw function for a user to remove their staked ETH inclusive
            of both principal and any accrued interest
            */
            function withdraw() public withdrawalDeadlineReached(true) claimDeadlineReached(false) notCompleted{
              require(balances[msg.sender] > 0, "You have no balance to withdraw!");
              uint256 individualBalance = balances[msg.sender];
              uint256 indBalanceRewards = individualBalance + ((block.timestamp-depositTimestamps[msg.sender])*rewardRatePerBlock);
              balances[msg.sender] = 0;
          
              // Transfer all ETH via call! (not transfer) cc: https://solidity-by-example.org/sending-ether
              (bool sent, bytes memory data) = msg.sender.call{value: indBalanceRewards}("");
              require(sent, "RIP; withdrawal failed :( ");
            }
          
            /*
            Allows any user to repatriate "unproductive" funds that are left in the staking contract
            past the defined withdrawal period
            */
            function execute() public claimDeadlineReached(true) notCompleted {
              uint256 contractBalance = address(this).balance;
              exampleExternalContract.complete{value: address(this).balance}();
            }
          
            /*
            READ-ONLY function to calculate the time remaining before the minimum staking period has passed
            */
            function withdrawalTimeLeft() public view returns (uint256 withdrawalTimeLeft) {
              if( block.timestamp >= withdrawalDeadline) {
                return (0);
              } else {
                return (withdrawalDeadline - block.timestamp);
              }
            }
          
            /*
            READ-ONLY function to calculate the time remaining before the minimum staking period has passed
            */
            function claimPeriodLeft() public view returns (uint256 claimPeriodLeft) {
              if( block.timestamp >= claimDeadline) {
                return (0);
              } else {
                return (claimDeadline - block.timestamp);
              }
            }
          
            /*
            Time to "kill-time" on our local testnet
            */
            function killTime() public {
              currentBlock = block.timestamp;
            }
          
            /*
            \Function for our smart contract to receive ETH
            cc: https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function
            */
            receive() external payable {
                emit Received(msg.sender, msg.value);
            }
          
          }
          

          5. 進入前臺

          太棒了! 我們剛剛經歷了一堆Solidity。當涉及到前端顯示時,Scaffold-Eth試圖讓事情變得簡單而美好。它包含了很多不同的反應組件,為用戶提供了低代碼的解決方案,以實現令人敬畏的UI!我鼓勵你玩玩這些組件。我鼓勵你玩玩不同的組件,但與此同時,我們將在spartan方面學習更多。

          看看我們的App.jsx文件,特別是在鏈接573附近的代碼塊[9],我們看到一個代碼塊,用于從我們的Solidity合約中捕獲發射的事件,并將其顯示為一個列表。

          有效地,它允許我們記錄從我們的智能合約發射的不同行動,解析存儲的信息,然后直觀地允許dApp用戶查看他們的鏈上歷史。

          雖然我們將通過在Solidity合約中發射事件來實踐良好的編程標準,但這次,為了簡單起見,我們將在前端忽略事件,所以讓我們完全刪除這個代碼塊。

          如果你看一下你的Staker UI標簽,你會發現事件框已經被抹去了。

          前臺編輯

          在大多數情況下,前臺的許多代碼將保持與默認的相同 在前面的步驟中,我們已經刪除了事件反應組件。

          我們的最終目標是要有一個漂亮的、簡單的用戶界面,看起來像下面這樣。

          請注意,默認的前臺缺少一些默認 repo 的視覺元素。

          每秒獎勵率

          為了構建這部分內容,我們在Staker合同塊下直接插入以下代碼。

            <div style={{ padding: 8, marginTop: 16 }}>
              <div>Reward Rate Per Second:</div>
              <Balance balance={rewardRatePerSecond} fontSize={64} /> ETH
            </div>

          在這里,我們利用了Scaffold-Eth的平衡反應組件來幫助格式化!

          最后期限的UI元素

          為了構建截止日期視覺組件,我們使用了以下2段代碼。

            // ** keep track of a variable from the contract in the local React state:
            const claimPeriodLeft = useContractReader(readContracts, "Staker", "claimPeriodLeft");
            console.log("? Claim Period Left:", claimPeriodLeft);
          
            const withdrawalTimeLeft = useContractReader(readContracts, "Staker", "withdrawalTimeLeft");
            console.log("? Withdrawal Time Left:", withdrawalTimeLeft);
          <div style={{ padding: 8, marginTop: 16, fontWeight: "bold" }}>
            <div>Claim Period Left:</div>
            {claimPeriodLeft && humanizeDuration(claimPeriodLeft.toNumber() * 1000)}
          </div>
          
          <div style={{ padding: 8, marginTop: 16, fontWeight: "bold"}}>
            <div>Withdrawal Period Left:</div>
            {withdrawalTimeLeft && humanizeDuration(withdrawalTimeLeft.toNumber() * 1000)}
          </div>

          雖然第二個代碼片斷很熟悉,與我們已經看到的相似,但第一個代碼塊有點獨特。不是說我們調用 claimPeriodLeft withdrawalTimeLeft來訪問前端的存儲變量值。

          然而,我們實際上必須先從智能合約中讀取這些值本身。第一個代碼片斷處理了這個邏輯!

          雜項:對其他現有的UI組件的編輯

          現在你已經看到了2個不同的前臺UI組件的例子,你能想出如何對前臺進行其余的修改,使其看起來像上面提供的樣本嗎!?

          你應該只需要在這里調整一些參數,所以不要想得太多了

          對用戶界面的自由發揮

          雖然Scaffold-Eth有很多默認組件,使用戶能夠簡單地利用 "低代碼 "解決方案,但它也使用戶能夠訪問更大的前端庫。

          默認情況下,它有一個與Ant Design react components(https://ant.design/components/overview/)的掛鉤,允許任何人從那里提取組件

          在我們的前端樣本中,我們實際上看到了 "線 "來劃分每個大塊的視覺組件。

          通過探索Ant Design中的不同選項,可以重現這些線條

          如果你想得到提示,請看一下Ant Dividers!

          不要忘記,我們需要在App.jsx文件的頂部導入我們計劃使用的組件

          從 "antd "導入 { Alert, Button, Col, Menu, Row, List, Divider }。

          如果你已經跟上了前端的代碼,你的App.jsx應該看起來像下面這樣。

          import WalletConnectProvider from "@walletconnect/web3-provider";
          //import Torus from "@toruslabs/torus-embed"
          import WalletLink from "walletlink";
          import { Alert, Button, Col, Menu, Row, List, Divider } from "antd";
          import "antd/dist/antd.css";
          import React, { useCallback, useEffect, useState } from "react";
          import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
          import Web3Modal from "web3modal";
          import "./App.css";
          import { Account, Address, Balance, Contract, Faucet, GasGauge, Header, Ramp, ThemeSwitch } from "./components";
          import { INFURA_ID, NETWORK, NETWORKS } from "./constants";
          import { Transactor } from "./helpers";
          import {
            useBalance,
            useContractLoader,
            useContractReader,
            useGasPrice,
            useOnBlock,
            useUserProviderAndSigner,
          } from "eth-hooks";
          import { useEventListener } from "eth-hooks/events/useEventListener";
          import { useExchangeEthPrice } from "eth-hooks/dapps/dex";
          // import Hints from "./Hints";
          import { ExampleUI, Hints, Subgraph } from "./views";
          
          import { useContractConfig } from "./hooks";
          import Portis from "@portis/web3";
          import Fortmatic from "fortmatic";
          import Authereum from "authereum";
          import humanizeDuration from "humanize-duration";
          
          const { ethers } = require("ethers");
          /*
              Welcome to  scaffold-eth !
          
              Code:
              https://github.com/austintgriffith/scaffold-eth
          
              Support:
              https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA
              or DM @austingriffith on Twitter or Telegram
          
              You should get your own Infura.io ID and put it in `constants.js`
              (this is your connection to the main Ethereum network for ENS etc.)
          
          
               EXTERNAL CONTRACTS:
              You can also bring in contract artifacts in `constants.js`
              (and then use the `useExternalContractLoader()` hook!)
          */
          
          ///  What chain are your contracts deployed to?
          const targetNetwork = NETWORKS.localhost; // <------- select your target frontend network (localhost, rinkeby, xdai, mainnet)
          
          //  Sorry for all the console logging
          const DEBUG = true;
          const NETWORKCHECK = true;
          
          //  providers
          if (DEBUG) console.log(" Connecting to Mainnet Ethereum");
          // const mainnetProvider = getDefaultProvider("mainnet", { infura: INFURA_ID, etherscan: ETHERSCAN_KEY, quorum: 1 });
          // const mainnetProvider = new InfuraProvider("mainnet",INFURA_ID);
          //
          // attempt to connect to our own scaffold eth rpc and if that fails fall back to infura...
          // Using StaticJsonRpcProvider as the chainId won't change see https://github.com/ethers-io/ethers.js/issues/901
          const scaffoldEthProvider = navigator.onLine
            ? new ethers.providers.StaticJsonRpcProvider("https://rpc.scaffoldeth.io:48544")
            : null;
          const poktMainnetProvider = navigator.onLine
            ? new ethers.providers.StaticJsonRpcProvider(
                "https://eth-mainnet.gateway.pokt.network/v1/lb/611156b4a585a20035148406",
              )
            : null;
          const mainnetInfura = navigator.onLine
            ? new ethers.providers.StaticJsonRpcProvider("https://mainnet.infura.io/v3/" + INFURA_ID)
            : null;
          // ( ?? Getting "failed to meet quorum" errors? Check your INFURA_ID
          
          //  Your local provider is usually pointed at your local blockchain
          const localProviderUrl = targetNetwork.rpcUrl;
          // as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.env
          const localProviderUrlFromEnv = process.env.REACT_APP_PROVIDER ? process.env.REACT_APP_PROVIDER : localProviderUrl;
          if (DEBUG) console.log(" Connecting to provider:", localProviderUrlFromEnv);
          const localProvider = new ethers.providers.StaticJsonRpcProvider(localProviderUrlFromEnv);
          
          //  block explorer URL
          const blockExplorer = targetNetwork.blockExplorer;
          
          // Coinbase walletLink init
          const walletLink = new WalletLink({
            appName: "coinbase",
          });
          
          // WalletLink provider
          const walletLinkProvider = walletLink.makeWeb3Provider(`https://mainnet.infura.io/v3/${INFURA_ID}`, 1);
          
          // Portis ID: 6255fb2b-58c8-433b-a2c9-62098c05ddc9
          /*
            Web3 modal helps us "connect" external wallets:
          */
          const web3Modal = new Web3Modal({
            network: "mainnet", // Optional. If using WalletConnect on xDai, change network to "xdai" and add RPC info below for xDai chain.
            cacheProvider: true, // optional
            theme: "light", // optional. Change to "dark" for a dark theme.
            providerOptions: {
              walletconnect: {
                package: WalletConnectProvider, // required
                options: {
                  bridge: "https://polygon.bridge.walletconnect.org",
                  infuraId: INFURA_ID,
                  rpc: {
                    1: `https://mainnet.infura.io/v3/${INFURA_ID}`, // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required
                    42: `https://kovan.infura.io/v3/${INFURA_ID}`,
                    100: "https://dai.poa.network", // xDai
                  },
                },
              },
              portis: {
                display: {
                  logo: "https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png",
                  name: "Portis",
                  description: "Connect to Portis App",
                },
                package: Portis,
                options: {
                  id: "6255fb2b-58c8-433b-a2c9-62098c05ddc9",
                },
              },
              fortmatic: {
                package: Fortmatic, // required
                options: {
                  key: "pk_live_5A7C91B2FC585A17", // required
                },
              },
              // torus: {
              //   package: Torus,
              //   options: {
              //     networkParams: {
              //       host: "https://localhost:8545", // optional
              //       chainId: 1337, // optional
              //       networkId: 1337 // optional
              //     },
              //     config: {
              //       buildEnv: "development" // optional
              //     },
              //   },
              // },
              "custom-walletlink": {
                display: {
                  logo: "https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0",
                  name: "Coinbase",
                  description: "Connect to Coinbase Wallet (not Coinbase App)",
                },
                package: walletLinkProvider,
                connector: async (provider, _options) => {
                  await provider.enable();
                  return provider;
                },
              },
              authereum: {
                package: Authereum, // required
              },
            },
          });
          
          function App(props) {
            const mainnetProvider =
              poktMainnetProvider && poktMainnetProvider._isProvider
                ? poktMainnetProvider
                : scaffoldEthProvider && scaffoldEthProvider._network
                ? scaffoldEthProvider
                : mainnetInfura;
          
            const [injectedProvider, setInjectedProvider] = useState();
            const [address, setAddress] = useState();
          
            const logoutOfWeb3Modal = async () => {
              await web3Modal.clearCachedProvider();
              if (injectedProvider && injectedProvider.provider && typeof injectedProvider.provider.disconnect == "function") {
                await injectedProvider.provider.disconnect();
              }
              setTimeout(() => {
                window.location.reload();
              }, 1);
            };
          
            /*  This hook will get the price of ETH from  Uniswap: */
            const price = useExchangeEthPrice(targetNetwork, mainnetProvider);
          
            /*  This hook will get the price of Gas from ?? EtherGasStation */
            const gasPrice = useGasPrice(targetNetwork, "fast");
            // Use your injected provider from  Metamask or if you don't have it then instantly generate a  burner wallet.
            const userProviderAndSigner = useUserProviderAndSigner(injectedProvider, localProvider);
            const userSigner = userProviderAndSigner.signer;
          
            useEffect(() => {
              async function getAddress() {
                if (userSigner) {
                  const newAddress = await userSigner.getAddress();
                  setAddress(newAddress);
                }
              }
              getAddress();
            }, [userSigner]);
          
            // You can warn the user if you would like them to be on a specific network
            const localChainId = localProvider && localProvider._network && localProvider._network.chainId;
            const selectedChainId =
              userSigner && userSigner.provider && userSigner.provider._network && userSigner.provider._network.chainId;
          
            // For more hooks, check out eth-hooks at: https://www.npmjs.com/package/eth-hooks
          
            // The transactor wraps transactions and provides notificiations
            const tx = Transactor(userSigner, gasPrice);
          
            // Faucet Tx can be used to send funds from the faucet
            const faucetTx = Transactor(localProvider, gasPrice);
          
            //  scaffold-eth is full of handy hooks like this one to get your balance:
            const yourLocalBalance = useBalance(localProvider, address);
          
            // Just plug in different  providers to get your balance on different chains:
            const yourMainnetBalance = useBalance(mainnetProvider, address);
          
            const contractConfig = useContractConfig();
          
            // Load in your local  contract and read a value from it:
            const readContracts = useContractLoader(localProvider, contractConfig);
          
            // If you want to make  write transactions to your contracts, use the userSigner:
            const writeContracts = useContractLoader(userSigner, contractConfig, localChainId);
          
            // EXTERNAL CONTRACT EXAMPLE:
            //
            // If you want to bring in the mainnet DAI contract it would look like:
            const mainnetContracts = useContractLoader(mainnetProvider, contractConfig);
          
            // If you want to call a function on a new block
            useOnBlock(mainnetProvider, () => {
              console.log(`? A new mainnet block is here: ${mainnetProvider._lastBlockNumber}`);
            });
          
            // Then read your DAI balance like:
            const myMainnetDAIBalance = useContractReader(mainnetContracts, "DAI", "balanceOf", [
              "0x34aA3F359A9D614239015126635CE7732c18fDF3",
            ]);
          
            //keep track of contract balance to know how much has been staked total:
            const stakerContractBalance = useBalance(
              localProvider,
              readContracts && readContracts.Staker ? readContracts.Staker.address : null,
            );
            if (DEBUG) console.log(" stakerContractBalance", stakerContractBalance);
          
            const rewardRatePerSecond = useContractReader(readContracts, "Staker", "rewardRatePerSecond");
            console.log(" Reward Rate:", rewardRatePerSecond);
          
            // ** keep track of a variable from the contract in the local React state:
            const balanceStaked = useContractReader(readContracts, "Staker", "balances", [address]);
            console.log(" balanceStaked:", balanceStaked);
          
            // **  Listen for broadcast events
            const stakeEvents = useEventListener(readContracts, "Staker", "Stake", localProvider, 1);
            console.log(" stake events:", stakeEvents);
          
            const receiveEvents = useEventListener(readContracts, "Staker", "Received", localProvider, 1);
            console.log(" receive events:", receiveEvents);
          
            // ** keep track of a variable from the contract in the local React state:
            const claimPeriodLeft = useContractReader(readContracts, "Staker", "claimPeriodLeft");
            console.log("? Claim Period Left:", claimPeriodLeft);
          
            const withdrawalTimeLeft = useContractReader(readContracts, "Staker", "withdrawalTimeLeft");
            console.log("? Withdrawal Time Left:", withdrawalTimeLeft);
          
          
            // ** Listen for when the contract has been 'completed'
            const complete = useContractReader(readContracts, "ExampleExternalContract", "completed");
            console.log("? complete:", complete);
          
            const exampleExternalContractBalance = useBalance(
              localProvider,
              readContracts && readContracts.ExampleExternalContract ? readContracts.ExampleExternalContract.address : null,
            );
            if (DEBUG) console.log(" exampleExternalContractBalance", exampleExternalContractBalance);
          
            let completeDisplay = "";
            if (complete) {
              completeDisplay = (
                <div style={{padding: 64, backgroundColor: "#eeffef", fontWeight: "bold", color: "rgba(0, 0, 0, 0.85)" }} >
                  --  Staking App Fund Repatriation Executed  --
                  <Balance balance={exampleExternalContractBalance} fontSize={32} /> ETH locked!
                </div>
              );
            }
          
            /*
            const addressFromENS = useResolveName(mainnetProvider, "austingriffith.eth");
            console.log(" Resolved austingriffith.eth as:", addressFromENS)
            */
          
            //
            //  DEBUG ?
            //
            useEffect(() => {
              if (
                DEBUG &&
                mainnetProvider &&
                address &&
                selectedChainId &&
                yourLocalBalance &&
                yourMainnetBalance &&
                readContracts &&
                writeContracts &&
                mainnetContracts
              ) {
                console.log("_____________________________________  scaffold-eth _____________________________________");
                console.log(" mainnetProvider", mainnetProvider);
                console.log(" localChainId", localChainId);
                console.log("? selected address:", address);
                console.log("?♂? selectedChainId:", selectedChainId);
                console.log(" yourLocalBalance", yourLocalBalance ? ethers.utils.formatEther(yourLocalBalance) : "...");
                console.log(" yourMainnetBalance", yourMainnetBalance ? ethers.utils.formatEther(yourMainnetBalance) : "...");
                console.log(" readContracts", readContracts);
                console.log(" DAI contract on mainnet:", mainnetContracts);
                console.log(" yourMainnetDAIBalance", myMainnetDAIBalance);
                console.log(" writeContracts", writeContracts);
              }
            }, [
              mainnetProvider,
              address,
              selectedChainId,
              yourLocalBalance,
              yourMainnetBalance,
              readContracts,
              writeContracts,
              mainnetContracts,
            ]);
          
            let networkDisplay = "";
            if (NETWORKCHECK && localChainId && selectedChainId && localChainId !== selectedChainId) {
              const networkSelected = NETWORK(selectedChainId);
              const networkLocal = NETWORK(localChainId);
              if (selectedChainId === 1337 && localChainId === 31337) {
                networkDisplay = (
                  <div style={{ zIndex: 2, position: "absolute", right: 0, top: 60, padding: 16 }}>
                    <Alert
                      message="?? Wrong Network ID"
                      description={
                        <div>
                          You have <b>chain id 1337</b> for localhost and you need to change it to <b>31337</b> to work with
                          HardHat.
                          <div>(MetaMask -> Settings -> Networks -> Chain ID -> 31337)</div>
                        </div>
                      }
                      type="error"
                      closable={false}
                    />
                  </div>
                );
              } else {
                networkDisplay = (
                  <div style={{ zIndex: 2, position: "absolute", right: 0, top: 60, padding: 16 }}>
                    <Alert
                      message="?? Wrong Network"
                      description={
                        <div>
                          You have <b>{networkSelected && networkSelected.name}</b> selected and you need to be on{" "}
                          <Button
                            onClick={async () => {
                              const ethereum = window.ethereum;
                              const data = [
                                {
                                  chainId: "0x" + targetNetwork.chainId.toString(16),
                                  chainName: targetNetwork.name,
                                  nativeCurrency: targetNetwork.nativeCurrency,
                                  rpcUrls: [targetNetwork.rpcUrl],
                                  blockExplorerUrls: [targetNetwork.blockExplorer],
                                },
                              ];
                              console.log("data", data);
          
                              let switchTx;
                              // https://docs.metamask.io/guide/rpc-api.html#other-rpc-methods
                              try {
                                switchTx = await ethereum.request({
                                  method: "wallet_switchEthereumChain",
                                  params: [{ chainId: data[0].chainId }],
                                });
                              } catch (switchError) {
                                // not checking specific error code, because maybe we're not using MetaMask
                                try {
                                  switchTx = await ethereum.request({
                                    method: "wallet_addEthereumChain",
                                    params: data,
                                  });
                                } catch (addError) {
                                  // handle "add" error
                                }
                              }
          
                              if (switchTx) {
                                console.log(switchTx);
                              }
                            }}
                          >
                            <b>{networkLocal && networkLocal.name}</b>
                          </Button>
                        </div>
                      }
                      type="error"
                      closable={false}
                    />
                  </div>
                );
              }
            } else {
              networkDisplay = (
                <div style={{ zIndex: -1, position: "absolute", right: 154, top: 28, padding: 16, color: targetNetwork.color }}>
                  {targetNetwork.name}
                </div>
              );
            }
          
            const loadWeb3Modal = useCallback(async () => {
              const provider = await web3Modal.connect();
              setInjectedProvider(new ethers.providers.Web3Provider(provider));
          
              provider.on("chainChanged", chainId => {
                console.log(`chain changed to ${chainId}! updating providers`);
                setInjectedProvider(new ethers.providers.Web3Provider(provider));
              });
          
              provider.on("accountsChanged", () => {
                console.log(`account changed!`);
                setInjectedProvider(new ethers.providers.Web3Provider(provider));
              });
          
              // Subscribe to session disconnection
              provider.on("disconnect", (code, reason) => {
                console.log(code, reason);
                logoutOfWeb3Modal();
              });
            }, [setInjectedProvider]);
          
            useEffect(() => {
              if (web3Modal.cachedProvider) {
                loadWeb3Modal();
              }
            }, [loadWeb3Modal]);
          
            const [route, setRoute] = useState();
            useEffect(() => {
              setRoute(window.location.pathname);
            }, [setRoute]);
          
            let faucetHint = "";
            const faucetAvailable = localProvider && localProvider.connection && targetNetwork.name.indexOf("local") !== -1;
          
            const [faucetClicked, setFaucetClicked] = useState(false);
            if (
              !faucetClicked &&
              localProvider &&
              localProvider._network &&
              localProvider._network.chainId === 31337 &&
              yourLocalBalance &&
              ethers.utils.formatEther(yourLocalBalance) <= 0
            ) {
              faucetHint = (
                <div style={{ padding: 16 }}>
                  <Button
                    type="primary"
                    onClick={() => {
                      faucetTx({
                        to: address,
                        value: ethers.utils.parseEther("0.01"),
                      });
                      setFaucetClicked(true);
                    }}
                  >
                     Grab funds from the faucet ??
                  </Button>
                </div>
              );
            }
          
            return (
              <div className="App">
                {/* ?? Edit the header and change the title to your project name */}
                <Header />
                {networkDisplay}
                <BrowserRouter>
                  <Menu style={{ textAlign: "center" }} selectedKeys={[route]} mode="horizontal">
                    <Menu.Item key="/">
                      <Link
                        onClick={() => {
                          setRoute("/");
                        }}
                        to="/"
                      >
                        Staker UI
                      </Link>
                    </Menu.Item>
                    <Menu.Item key="/contracts">
                      <Link
                        onClick={() => {
                          setRoute("/contracts");
                        }}
                        to="/contracts"
                      >
                        Debug Contracts
                      </Link>
                    </Menu.Item>
                  </Menu>
          
                  <Switch>
                    <Route exact path="/">
                      {completeDisplay}
          
                      <div style={{ padding: 8, marginTop: 16 }}>
                        <div>Staker Contract:</div>
                        <Address value={readContracts && readContracts.Staker && readContracts.Staker.address} />
                      </div>
          
                      <Divider />
          
                      <div style={{ padding: 8, marginTop: 16 }}>
                        <div>Reward Rate Per Second:</div>
                        <Balance balance={rewardRatePerSecond} fontSize={64} /> ETH
                      </div>
          
                      <Divider />
          
                      <div style={{ padding: 8, marginTop: 16, fontWeight: "bold" }}>
                        <div>Claim Period Left:</div>
                        {claimPeriodLeft && humanizeDuration(claimPeriodLeft.toNumber() * 1000)}
                      </div>
          
                      <div style={{ padding: 8, marginTop: 16, fontWeight: "bold"}}>
                        <div>Withdrawal Period Left:</div>
                        {withdrawalTimeLeft && humanizeDuration(withdrawalTimeLeft.toNumber() * 1000)}
                      </div>
          
                      <Divider />
          
                      <div style={{ padding: 8, fontWeight: "bold"}}>
                        <div>Total Available ETH in Contract:</div>
                        <Balance balance={stakerContractBalance} fontSize={64} />
                      </div>
          
                      <Divider />
          
                      <div style={{ padding: 8,fontWeight: "bold" }}>
                        <div>ETH Locked  in Staker Contract:</div>
                        <Balance balance={balanceStaked} fontSize={64} />
                      </div>
          
                      <div style={{ padding: 8 }}>
                        <Button
                          type={"default"}
                          onClick={() => {
                            tx(writeContracts.Staker.execute());
                          }}
                        >
                           Execute!
                        </Button>
                      </div>
          
                      <div style={{ padding: 8 }}>
                        <Button
                          type={"default"}
                          onClick={() => {
                            tx(writeContracts.Staker.withdraw());
                          }}
                        >
                           Withdraw
                        </Button>
                      </div>
          
                      <div style={{ padding: 8 }}>
                        <Button
                          type={balanceStaked ? "success" : "primary"}
                          onClick={() => {
                            tx(writeContracts.Staker.stake({ value: ethers.utils.parseEther("0.5") }));
                          }}
                        >
                           Stake 0.5 ether!
                        </Button>
                      </div>
          
                      {/*
                           this scaffolding is full of commonly used components
                          this <Contract/> component will automatically parse your ABI
                          and give you a form to interact with it locally
                      */}
          
                      {/* uncomment for a second contract:
                      <Contract
                        name="SecondContract"
                        signer={userProvider.getSigner()}
                        provider={localProvider}
                        address={address}
                        blockExplorer={blockExplorer}
                        contractConfig={contractConfig}
                      />
                      */}
                    </Route>
                    <Route path="/contracts">
                      <Contract
                        name="Staker"
                        signer={userSigner}
                        provider={localProvider}
                        address={address}
                        blockExplorer={blockExplorer}
                        contractConfig={contractConfig}
                      />
                      <Contract
                        name="ExampleExternalContract"
                        signer={userSigner}
                        provider={localProvider}
                        address={address}
                        blockExplorer={blockExplorer}
                        contractConfig={contractConfig}
                      />
                    </Route>
                  </Switch>
                </BrowserRouter>
          
                <ThemeSwitch />
          
                {/* ? Your account is in the top right with a wallet at connect options */}
                <div style={{ position: "fixed", textAlign: "right", right: 0, top: 0, padding: 10 }}>
                  <Account
                    address={address}
                    localProvider={localProvider}
                    userSigner={userSigner}
                    mainnetProvider={mainnetProvider}
                    price={price}
                    web3Modal={web3Modal}
                    loadWeb3Modal={loadWeb3Modal}
                    logoutOfWeb3Modal={logoutOfWeb3Modal}
                    blockExplorer={blockExplorer}
                  />
                  {faucetHint}
                </div>
          
                <div style={{ marginTop: 32, opacity: 0.5 }}>
                  {/* Add your address here */}
                  Created by <Address value={"Your...address"} ensProvider={mainnetProvider} fontSize={16} />
                </div>
          
                <div style={{ marginTop: 32, opacity: 0.5 }}>
                  <a target="_blank" style={{ padding: 32, color: "#000" }} href="https://github.com/scaffold-eth/scaffold-eth">
                     Fork me!
                  </a>
                </div>
          
                {/*  Extra UI like gas price, eth price, faucet, and support: */}
                <div style={{ position: "fixed", textAlign: "left", left: 0, bottom: 20, padding: 10 }}>
                  <Row align="middle" gutter={[4, 4]}>
                    <Col span={8}>
                      <Ramp price={price} address={address} networks={NETWORKS} />
                    </Col>
          
                    <Col span={8} style={{ textAlign: "center", opacity: 0.8 }}>
                      <GasGauge gasPrice={gasPrice} />
                    </Col>
                    <Col span={8} style={{ textAlign: "center", opacity: 1 }}>
                      <Button
                        onClick={() => {
                          window.open("https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA");
                        }}
                        size="large"
                        shape="round"
                      >
                        <span style={{ marginRight: 8 }} role="img" aria-label="support">
                          
                        </span>
                        Support
                      </Button>
                    </Col>
                  </Row>
          
                  <Row align="middle" gutter={[4, 4]}>
                    <Col span={24}>
                      {
                        /*  if the local provider has a signer, let's show the faucet:  */
                        faucetAvailable ? (
                          <Faucet localProvider={localProvider} price={price} ensProvider={mainnetProvider} />
                        ) : (
                          ""
                        )
                      }
                    </Col>
                  </Row>
                </div>
              </div>
            );
          }
          
          export default App;
          

          真棒!我們已經做了很多工作。

          在開發者環境、Solidity和前端代碼方面,我們已經一起完成了很多新的組件。

          驗證您的dApp的功能是否符合預期!

          1. 1. dApp是否具有單次使用押金的功能?
          2. 2. 提款/資金返還條件是否得到尊重?繼續點擊yarn deploy --reset幾次,檢查每個時間窗口。

          挑戰時間!

          好了,現在到了最精彩的部分。我將留給你一些擴展挑戰,讓你自己去嘗試,看看你是否完全理解了你在這里學到的東西!

          1. 1. 更新Staker.sol合約中的利息機制,這樣你就可以根據存款和提款之間的區塊獲得 "非線性 "的ETH數額

          我建議實現一個基本的指數函數!

          1. 1. 允許用戶向智能合約存入任意數量的ETH,而不僅僅是0.5ETH。
          2. 2. 不要使用vanilla ExampleExternalContract合約,在Staker.sol中實現一個函數,允許你取回鎖定在ExampleExternalContract中的ETH,并將其重新存入Staker合約中。
          • ? 請確保只有 "白名單 "中的一個地址可以調用這個新函數,以控制其使用。
          • ? 確保你創建了邏輯/刪除了現有的代碼,以確保用戶能夠一次又一次地與Staker合同互動 我們希望能夠反復地從Staker -> ExampleExternalContract進行ping-pong。

          引用鏈接

          [1] Scaffold-eth: http://scaffoldeth.io/
          [2] SpeedRunEthereum:
          https://speedrunethereum.com/
          [3] Challenge #1:
          https://speedrunethereum.com/challenge/decentralized-staking
          [4] SpeedRunEthereum:
          https://speedrunethereum.com/
          [5] Challenge #1:
          https://speedrunethereum.com/challenge/decentralized-staking
          [6] 準則:
          https://docs.alchemy.com/docs/how-to-build-a-staking-dapp#project-parameters
          [7] 公共變量部分:
          https://docs.alchemy.com/docs/how-to-build-a-staking-dapp#public-variables
          [8] Solidity By Example:
          https://solidity-by-example.org/function-modifier
          [9] 鏈接573附近的代碼塊:
          https://github.com/scaffold-eth/scaffold-eth/blob/challenge-1-decentralized-staking/packages/react-app/src/App.jsx#L573

          到前端技術,不少朋友一定會感到有些陌生。但其實,前端,你每天都在接觸。

          你正在使用的APP,你正在瀏覽的網頁,這些你能看到的界面,都屬于前端。

          而前端最重要的三大技術,HTML,CSS,JavaScript,則是每一個前端開發者必須具備的技能。

          掌握這些技能,你可以快速地做出一個酷炫的APP界面或者一個簡單大方的網站頁面。因此,就讓我們一起來快速學習一下這三門技術吧。



          以下內容節選自實驗樓訓練營課程《Vue.js 和 Node.js 構建內容發布系統》。

          實驗介紹

          本實驗主要介紹一下前端的基礎知識,對比認識一下各個框架的代碼編寫方式,并介紹我們本次技術選型的主要思路。對于前端三大技術 HTML、CSS、JavaScript,簡單的介紹了基本情況和常用語法。中間介紹了現代框架的一些情況,并通過實際的案例,用代碼直觀的認識一下各種框架的實現方式。最后分析一下項目的技術選型。

          知識點

          • HTML、CSS、JavaScript 快速概覽
          • 前端框架概覽和選型
          • 后端選型
          • 數據庫選型
          • Web 服務器選型

          前端技術簡介

          本節我們簡單介紹一下前端最基礎 HTML, CSS, JavaScript 三駕馬車。雖然本課程預設的讀者為零基礎開發者,但是前端開發一定會這三種技術的運用有要求。建議抽空學習 《 Web 前端工程師路徑》 中的階段 1 甚至階段 2。這里僅做語法介紹和基本使用的概覽。

          在此之前先認識一下實驗環境。實驗環境和 VS Code 使用體驗基本一致。你可以啟動一個終端,并在其中輸入 Linux 命令。

          后面的命令無特殊說明的都是在此終端命令行中輸入。大多數命令可以多開終端窗口分別執行。

          那么下面我們就快速的了解一下。

          HTML

          HTML 全稱超文本標記語言,幾乎是從萬維網和瀏覽器產生伊始就存在的。主要用于結構化信息來方便瀏覽器展示。

          以標簽對作為主要特征,如<p>這是一個段落</p>。這些標簽會被瀏覽器解析成不同的模塊,比如 p 標簽就是一個段落,img 標簽就是一個圖片,a 標簽就是一個超鏈接,標簽名不區分大小寫。

          立刻就來嘗試一下吧。首先通過命令行新建一個 demo 目錄:

          mkdir demo

          然后命令行進入 demo 目錄:

          cd ./demo

          新建一個 hello.html 文件,可以在實驗環境左邊的瀏覽框內在 demo 上右鍵選擇 New File 然后命名為 hello.html;或者也可在命令行終端輸入 touch hello.html,同樣是新建文件。

          在其中輸入以下內容:

          <!DOCTYPE html>
          <html>
            <head>
              <meta charset="UTF-8" />
              <title>標題</title>
            </head>
            <body>
              正文
            </body>
          </html>

          然后右擊文件選擇 Open With → Preview。

          看到了嗎?其實我們就是新建了一個 .html 后綴的文本文件,然后瀏覽器就可以將其中的內容展示出來。你也可以在自己的桌面上新建一個,然后使用瀏覽器打開看看效果。

          這里嵌套代碼的縮進格式是為了美觀和可讀性,并無嚴格要求。

          head 標簽中是一些暫時無需用到的頭部信息,渲染的主體是 body 標簽。下面我們修改 body 標簽里面的內容,填入一些常用標簽來直觀感受一下。

          <body>
            <h1>頁面標題</h1>
            <div>一個塊容器</div>
            <div>又一個塊容器</div>
            <p>這里是段落了,間距變大</p>
            <div>一個塊容器</div>
            <div>
              <div>
                多層嵌套:
                <div>內部第一個</div>
                <div>內部第二個</div>
              </div>
            </div>
          </body>

          保存之后切換到瀏覽標簽看一看,有沒有感覺被忽悠了?嵌套沒嵌套根本沒體現出來,就像 Word 里排了一下版,按了幾個回車。

          因為我們沒有對顯示樣式進行修改,那是 CSS 的事。HTML 主要管內容的組織結構。

          這里有一點針對學習的小建議,本課程中給到的全部代碼請手動輸入,忘記復制和粘貼快捷鍵。

          而且最好不要機械的一個字符一個字符照著抄,盡量看過一行或一小段代碼之后,靠短暫的印象去輸出,別怕出錯,只有過腦子并輸出實踐,才是最快掌握一項技能的捷徑。

          以上兩句話是本課程中最有價值內容之一。

          下面我們接著修改剛才的代碼,再給 body 中添加幾個常用標簽。每次修改和保存之后記得到預覽頁看看樣式的變化。

          <h4>4 級標題</h4>
          <ul>
            <li>
              HTML
            </li>
            <li>
              CSS
            </li>
            <li>
              JavaScript
            </li>
          </ul>
          <div>
            <a href="https://www.shiyanlou.com" target="_blank"
              >點擊超鏈接跳轉至實驗樓首頁</a
            >
          </div>
          <div>
            <img
              src="https://static.shiyanlou.com/frontend/dist/img/9f43b00.svg"
              alt=""
              width="200"
            />
          </div>

          最后的鏈接標簽 a 和圖片標簽 img 出現了標簽屬性,屬性為 attr="value" 格式,可以給標簽增加更豐富的信息。

          同時 img 標簽還是一個單標簽,不需要在后面添加 </img> 配合使用。

          至此對 HTML 的簡要介紹告一段落。

          互聯網上看到的各種五彩繽紛網頁都是由這些 HTML 組成的,但是為什么我們寫的這么難看?下一節我們就要學習一下如何用 CSS 美化頁面。

          CSS

          CSS 全稱層疊樣式表,是專門用來修飾 HTML 樣式的一種語言。我們修改一下上節的 hello.html 文件來直觀感受一下。


          內部代碼塊引入

          在 head 標簽內部增加以下 style 代碼塊:

          <head>
            <meta charset="UTF-8" />
            <title>標題</title>
            <style type="text/css">
              div {
                border: 1px solid blue;
                padding: 2px;
                margin: 10px;
              }
            </style>
          </head>

          這是再切換到預覽頁,發現沒那么平鋪直敘了。

          這就是 CSS 的第一種引入方式,HTML 內置代碼塊。

          大括號外面的 div 是標簽選擇器,這里選中了本頁面中的所有 div 元素。大括號里面是屬性名與賦值,屬性名都是固定的關鍵字,并已規定好了值的類型和可選范圍。

          讀代碼也就大概知道了,我們將 div 的邊框設置為 1 像素寬、固體(單線型)、藍色,填充(內邊距)2 像素,邊緣空白(外邊距)10 像素。現在可以練習調整一下各個數字,預覽看看發生了什么?

          再說點題外話,懂一些英文對程序員來說非常必要,除了可以憑感覺就讀懂沒學過的代碼,還可以在面向 Google 編程、面向 Stack Overflow 編程、面向 Github Issues 編程時游刃有余。


          外部文件引入

          然后我們再試一下外部文件引入,在 hello.html 的同級目錄新建 hello.css,輸入以下內容保存:

          div {
            color: green;
            border: 2px dotted red;
          }

          然后修改 hello.html,在 style 標簽后面增加一行 link 標簽,添加引入類型和地址:

          <style type="text/css">
            div {
              border: 1px solid blue;
              padding: 2px;
              margin: 10px;
            }
          </style>
          <link rel="stylesheet" href="hello.css" />

          預覽看看,文字顏色變為綠色,邊框的樣式也被更新為 2 像素寬、點線型紅色。

          同樣是 div 選擇器,為什么邊框的樣式被覆蓋了呢?注意 CSS 在同樣條件下會后面代碼覆蓋前面,可以嘗試交換 link 標簽和 style 標簽塊的順序看看。


          行間樣式

          最后一種叫行間樣式,這個結構更簡單。修改 hello.html 中的 <div>內部第一個</div> 為

          <div style="margin: 60px 0 10px 30px ;color:purple;">內部第一個</div>

          樣式覆蓋前兩種方式了,因為行間樣式的優先級較高。這里涉及到選擇器權重,先給一個簡單公式了解一下。

          !important > 行間樣式 > ID > class | 偽類 | 屬性選擇 > 標簽 > 繼承 | 通配符。

          多個選擇器作用時權重相加。這里算 CSS 里有點復雜的部分,暫時不展開。

          這里還有個小知識點是內外邊距 margin 和 padding 接受的完整的值是四個,順序固定為“上 右 下 左”。如果省略參數則從末尾計算對向合并。比如:

          • margin:40px 20px 50px; 三個參數時,左右同為 20px。
          • margin:40px 20px; 兩個參數時 上下同為 40px, 左右同為 20px。
          • margin:40px; 一個參數時呢?請自行嘗試理解。

          CSS 先講這么多,雖然沒有把我們的頁面變多好看,但最起碼知道努力的方向了。

          JavaScript

          制作 JavaScript 的快速入門簡直非常傷腦筋。比起前兩種技術 HTML 和 CSS,這是貨真價實的編程語言了。

          也是我們后面要用到的 Vue.js 和 Node.js 中的根基,一下子又很難講很多,所以還是希望同學們能重視起來系統學習一下,最起碼讀到后面的代碼時不至于陷入“這是啥這又是啥”的窘境。

          來段代碼直觀認知一下,還是先內部代碼塊引入。

          在 hello.html 的 head 標簽內增加一個代碼塊:

              <link rel="stylesheet" href="./hello.css">
              <script>
                let message = "字符串提示";
                function showMSG(msg) {
                  alert(msg);
                }
              </script>

          修改 hello.html 的 h1 標簽為:

          <h1 onclick="showMSG(message)">頁面標題</h1>

          保存預覽,點擊“頁面標題”,會彈出提示框。

          JavaScript 代碼加載之后就會執行,不存在編譯階段。行末的分號絕大多數時候可以省略。

          我們先定義了一個變量 message,并賦值為“字符串提示”。定義變量關鍵字原是 var,ES6 新增關鍵字 let 有更清晰的作用域,可替代使用。

          學習 JavaScript 經常會碰到 ES6、ES7 之類的名詞,實際上是 ECMAScript 標準的版本號的意思。可以簡單理解為新版標準為 JavaScript 添加特定新特性。

          然后我們定義了一個函數 showMSG,并添加一個形參 msg。在函數體內部調用瀏覽器彈框方法,顯示 msg 的值。function 是定義函數的關鍵字,暫時先把它當做一個功能封閉的盒子,當函數調用時,執行函數體內的代碼。

          調用部分是先給 h1 標簽添加了 onclick 點擊事件,被點擊時觸發 showMSG(message),也就是把 message 傳給了 msg。

          之后再試一下調用外部 js 文件,新建 demo.js 文件,寫入下面內容并保存。

          message = "修改一下字符串";

          然后修改 hello.html 文件,在 script 代碼塊后面增加一行:

          <script src="./demo.js"></script>

          這次保存預覽,點擊“頁面標題”,可以看到彈窗的文字變了。這個演示了 script 代碼塊在頁面可以同時存在多個,也是順序調用,而且互相之間可以直接訪問。文件命名也沒有要求,希望不會逼死強迫癥。

          JavaScript 就是為什么網頁可以做那么多交互的源頭了。掌握起來任重道遠。

          以上內容節選自實驗樓訓練營課程《Vue.js 和 Node.js 構建內容發布系統》。


          這三門前端技術先了解到這里,想要更深入學習如何使用前端技術構建內容發布系統,比如做個高逼格的博客,搭建一個交流社區,或者為企業制作官網等,可以訪問實驗樓官網搜索《Vue.js 和 Node.js 構建內容發布系統》這門課。

          課程會提供完整的虛擬機環境,手把手教大家如何從頭構建實現一個前后端分離的內容發布系統,包括了前端頁面、后端服務、數據庫等。


          主站蜘蛛池模板: 91大神在线精品视频一区| 91成人爽a毛片一区二区| 精品国产一区二区麻豆| 久久国产精品一区免费下载| 久久99精品免费一区二区| 亚洲视频一区网站| 一本大道在线无码一区| 少妇精品无码一区二区三区| 3d动漫精品啪啪一区二区免费| 久久国产精品一区免费下载| 亚洲国产AV一区二区三区四区 | 日韩一区二区在线免费观看| 日韩社区一区二区三区| 99偷拍视频精品一区二区| 一区二区三区视频免费| 糖心vlog精品一区二区三区| 中文字幕无码一区二区免费 | 一区二区三区高清在线| 国产品无码一区二区三区在线| 亚洲av无码一区二区三区四区| 日韩精品成人一区二区三区| 国产一区二区三区免费| 亚洲成AV人片一区二区密柚| 亚洲AV色香蕉一区二区| 波多野结衣久久一区二区| 国模无码一区二区三区| 动漫精品一区二区三区3d| 波多野结衣中文一区| 精品一区二区三区中文字幕| 亚洲成在人天堂一区二区| 国产一区在线mmai| 精品一区二区三区在线观看l| 国产一区二区成人| 亚洲国模精品一区| 精品深夜AV无码一区二区老年| 人妻夜夜爽天天爽一区| 无码人妻久久一区二区三区免费| 亚洲欧美日韩中文字幕一区二区三区 | 日韩在线不卡免费视频一区| 无码视频一区二区三区在线观看| 国产成人一区二区三区|