# 第四屆QC筆試考題 **及格線:6題/10題** ## Solidity 基礎 **1. 如何在部屬合約時,指定 owner,以程式碼舉例** ```solidity= contract exam {modifier ownerOnly() { require(msg.sender == owner); _; } } ``` **2. ERC721 合約部屬至 goerli,寫上部屬的合約地址** 回答寫在這裡 ``` # 回答區 https://goerli.etherscan.io/tx/0x5b78745b09939c47dc46de7b99ff9caeaa6ba4e3cc974a947fd688edd2f96460 ``` **3. 試寫出兩種錯誤處理(require, revert),並解釋兩種的不同** 回答寫在這裡 ``` # 回答區 ```Require require為false時會退回剩下的gas,所以才會放在function的一開始,這樣就能節省gas fee了,後面的程式碼不會執行。 require(判斷式, 返回字串) function deposit(int _amount) public { require(_amount > 0, "Deposited amount must be greater than zero"); } Revert revert 退 gas fee 時,是會檢查裡面所有的條件後才會退回剩下的 gas fee. revert(返回字串) int public balance; function deposit(int _amount) public {   int oldBanance = 0;   if(balance < oldBanance) {revert("Impossible!"); } } **4. 試寫出 transfer, call 的不同** - transfer 會限制 gas 花費量 2,300,可防止重入攻擊、call 可以做更多的事 回答寫在這裡 call 沒限制 gas ``` # 回答區 ``` ```solidity= // call (bool sent, bytes memory data) = _to.call{value: msg.value}(""); // transfer _to.transfer(msg.value); ``` **5.承上題,transfer 與 ERC20 的 transfer 有何不同?** 回答寫在這裡 ``` # 回答區 transfer 是從自己轉給對方 ``` **6. 以下哪個選項為較安全產生隨機數?** - A:block.timestamp - B:block.hash - C:Chainlink VRF 預言機 回答寫在這裡 ``` # 回答區 ```c ## Solidity 進階 **1. 請問以下是Solidity允許的數值?(複選)** - A. 0x1cb89a3833bc070 - B. 129348349684596843 - C. 0.1 回答寫在這裡 AC ``` # 回答區 ``` **3. 說明 proxy 概念,如何更新智能合約?** 回答寫在這裡 proxy 是將智能合約分割成很小塊,來一塊塊更新 ``` # 回答區 ``` **4. 合約裡的 receive() 用途是?** 回答寫在這裡 receive() 只用於處理接收 ETH , 一個合約最多有一個 receive() 函數 ``` # 回答區 ```receive() 只用於處理接收 ETH , 一個合約最多有一個 receive() 函數 **5. 做 Dapp 以下是不需要的?** - A. ethers.js - B. RPC Provider - C. 智能合約 ABI - D. 智能合約地址 回答寫在這裡 B ``` # 回答區 ```B **6. 說明 EOA 與 Contract Address 是如何產生的** 回答寫在這裡 EOA地址產生的過程中是使用橢圓曲線產生公鑰與私鑰,採用secp256k1曲線 合約帳戶與EOA相同的是一樣都是由16進位數兜出來的42個字所組成的,它通常都是在開發者將合約部屬到以太坊上時所產生出來的,裡面包含了自己的EOA與交易流水號(nonce) # 回答區 ``` **7. 承上題,兩者有何不同?要如何區分?** ``` # 回答區 差別在 EOA 可以免費創建帳戶,僅能接受代幣間的轉移而合約帳戶的功能 ``` **8. 實作 ERC20 質押某代幣,timelock(固定鎖倉期,自定義), reward (回饋該代幣)** 回答寫在這裡 ``` # 回答區 ``` ## Solidity 資安相關 **1. 標註程式碼哪幾行可能有資安問題,並說明何種資安問題,並提出解法** ```=solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; contract AuthorizeDepositContract { uint256 public fee; mapping(address => uint256) public balance; bool private _lock = false; modifier lock { require(!_lock); _lock = true; _; _lock = false; } function deposit() external payable { uint256 depositFee = msg.value / 100; balance[msg.sender] += msg.value - depositFee; fee += depositFee; } function withdraw(uint256 amount) external { require(balance[msg.sender] >= amount, "Account balance is not enough"); balance[msg.sender] -= amount; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed."); } function withdrawFee() external lock { (bool success, ) = msg.sender.call{value: fee}(""); require(success, "Transfer failed."); fee = 0; } } ``` **2. 試寫出多簽錢包程式碼,調整同意比例(1/3)** 回答寫在這裡 ```solidity= ``` **3. 標註程式碼哪幾行可能有資安問題,並說明何種資安問題,並提出解法** ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract AuthorizeDepositContract is ReentrancyGuard { using SafeMath for uint256; uint256 public fee; mapping(address => uint256) public balance; function deposit() external payable { uint256 depositFee = msg.value / 100; balance[msg.sender] += msg.value - depositFee; fee += depositFee; } function withdraw(uint256 amount) external { require(balance[msg.sender] >= amount, "Account balance is not enough"); balance[msg.sender] -= amount; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed."); } function withdrawFee() external { (bool success, ) = msg.sender.call{value: fee}(""); require(success, "Transfer failed."); fee = 0; } } ``` ``` # 回答區 ``` **4. 此[合約](https://goerli.etherscan.io/address/0x03C928FFF7609849Ce3d7428804Fd7dE4BE3a643#code) 呼叫 mint 預估 Gas 為多少?請寫下你預估 Gas 的詳細步驟、預估 Gas 是多少數字?** ``` # 回答區 ``` **5. 這是一個績點奪獎金遊戲,請找出漏洞在哪,讓你可以跳過原本的通關條件領光合約裡所有的錢** ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.16; contract KryptoGame { mapping(address => int256) public playerPoints; uint256 public pointstoWin = 1e10; uint256 public prize; bool public status; address public winner; address payable public owner; constructor() payable { owner = payable(msg.sender); status = true; prize += msg.value; } modifier onlyowner() { require(msg.sender == owner, "You are not owner"); _; } function getPrizePool() public view returns (uint) { return address(this).balance; } function addPoints(int256 _points) public { require(status == true, "Game is over."); require(_points <= 10, "Only allow to add less than 10 points!"); playerPoints[msg.sender] += _points; } function winTheGame() public { require(uint256(playerPoints[msg.sender]) >= pointstoWin, "Not yet."); winner = msg.sender; status = false; payable(msg.sender).transfer(address(this).balance); } function BOMB() public onlyowner { selfdestruct(owner); } } ``` 請描述你是如何破解的,詳細寫下步驟 ###### tags: `Solidity 工程師實戰營第 4 期`