# 第四屆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 期`