---
# System prepended metadata

title: 第五屆QC筆試正式考題 penguin72487 作答
tags: [Solidity 工程師實戰營第 5 期]

---

# 第五屆QC筆試正式考題 penguin72487 作答

**及格線：6題/10題**

## Solidity 基礎

**1. 如何在部屬合約時，指定 owner，以程式碼舉例**

```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract exam{
    address public immutable owner;
    constructor(){
        owner = msg.sender;
    }
}
```

**2. 請列出 ERC721 safeMint & mint 兩者的差異？**
```
mint與safeMint都是在創建一個新的NFT給某地址，但是差異在safeMint會檢查地址能不能接收ERC721
```


**3. 試寫出對於操作 ERC20 代幣時，safeTransfer 的功能以及需要使用的時機**

```
通常是用在傳送對象是合約的時候使用，會檢查合約是否能接受代幣，避免轉移代幣時因接收者無法接收代幣而導致的資金損失。
```


**4. 試寫出兩種錯誤處理(require, revert)，並解釋兩種的不同**

回答寫在這裡
```
其實兩種很像
1. require我覺得比較像revert使用上的優化
revert在通常的使用場景要搭配if做狀態的檢查:
if (msg.sender != owner) {
    revert("Only owner can call this function");
}
require可以增加可讀性
require(msg.sender == owner,"Only owner can call this function");
2.require 和revert在Remix的環境也有些差異，通常Remix會幫你檢查require並且提醒你可能失敗，revert不會事前告訴你會失敗。
```



**5. 試寫出 transfer, call 的不同**
```
# 回答區

transfer:會限制Gas
call:比較底層，可以自定義gas上限等其他複雜邏輯，會回傳是否執行成功。成不成功不一定是由ETH網路決定，是由被Call的人決定，轉給EOA通常沒問題，如果是合約可能會借助fallback()讓合約以為真的沒有把前轉出去而失敗。
```



**6.承上題，transfer 與 ERC20 的 transfer 有何不同？**

回答寫在這裡
```
transfer:用在轉移原生的ETH
ERC20.transfer用在轉移對應的ERC20 token
```

**7. 以下哪個選項為較安全產生隨機數？**
- A：block.timestamp
- B：block.hash
- C：Chainlink VRF 預言機


回答寫在這裡
```
C
```

## Solidity 進階

**1. 請問以下是Solidity允許的數值？（複選）**

- A. 0x1cb89a3833bc070
- B. 129348349684596843
- C. 0.1


回答寫在這裡
```
AB
```

**2. 說明 proxy 概念，如何更新智能合約？**


回答寫在這裡
```
代理（Proxy）是一種智能合約設計模式，它允許合約的業務邏輯在不變更合約地址的情況下進行升級
可以由一個合約專門匹配什麼功能要去什麼合約,那個什麼合約是可以改的，匹配合約不用改。
```

**3. 合約裡的 receive() 用途是？**


回答寫在這裡
```
用來接收ETH，有function 有 payable並且有收到ETH就會執行receive()來接收ETH。
```

**4. 做 Dapp 以下是不需要的？**
- A. ethers.js
- B. RPC Provider
- C. 智能合約 ABI
- D. 智能合約地址


回答寫在這裡
```
A
```

**5. 說明 EOA 與 Contract Address 是如何產生的**


回答寫在這裡
```
EOA 是由私鑰控制的地址。它的地址是由公鑰經過 Keccak-256 哈希後取最後的 20 字節生成的。
Contract Address：合約地址是在合約創建時生成的，它的生成方式是將創建者的地址和創建者的 nonce 進行 RLP 編碼後進行 Keccak-256 哈希，取最後的 20 字節作為合約地址。
```

**6. 承上題，兩者有何不同？要如何區分？**


回答寫在這裡
```
EOA地址是由私鑰直接去受授權，進行交易等等，但是合約需要有人去觸發功能，才能進行交易等等，最快的方法可以直接上Etherscan查察，有看到這地址上有寫程式碼就是合約地址了。
```



## 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;
    }
}
```
function withdraw忘了上鎖，如果解收地址是攻擊者的合約可以很簡單的利用 fallback()讓遠本合約以為真的錢沒有轉出去，只要 function withdraw(uint256 amount) external  lock就可以解決。

## Solidity 合約應用

**1. 部署一個 ERC721 合約並開源合約，mint 一個 NFT 後轉移到以下地址 `0x6e24f0fF0337edf4af9c67bFf22C402302fc94D3`
請留下 transfer 的 tx hash**

```
# 回答區
tx hash: 0xeb31899dce5d091d9dc5307b085685aa234c47bff10844e85802b8a40f15e236
```


**2. 請部署一個能從 Chainlink 取得 ETH 價格的合約到 goerli 測試網上並開源合約**

```
# 回答區
合約連結：https://goerli.etherscan.io/address/0x276af5e3ef5e6ada70a2e950f9c85716a63498de
```

**3. 以下是一份名單，共有八個地址，請利用 merkle tree 原理製作出一個 root 以及當 address1 要證明自己在 leaves 中時所需要提交的 proof**
```
名單
address1: '0xdab15510af1425ba57499C2284cf420001A24D00'
address2: '0xA2F7B4eA63be89464bE01FB074d981F5917f53ef'
address3: '0x4197b82771654C0cE9049925845a8F942b58ccD0'
address4: '0x1b9024CFB1409c13f3B2ee422e9c196442c699E1'
address5: '0xEa36d9a9d90b7aFA41404CeCa0c06F9d3A75A8fa'
address6: '0x5ea8023bB1cca8aF07bcA9edB8FCE8b8a84C8B3f'
address7: '0x4bCae98Ab9912694af894D82658517782203a1dE'
address8: '0x2834A1487A841930b8b5b3C5812FB526A0189339'
```

```
# 回答區
1. root: ...
2. proof: ...

# 解題過程...
```

**4. 實作 ERC20 質押某代幣，timelock（固定鎖倉期，自定義）, reward （回饋該代幣）**


回答寫在這裡
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract StakingContract is ReentrancyGuard {
    ERC20 public token;
    uint256 public lockTime;

    struct Stake {
        uint256 amount;
        uint256 start;
    }

    mapping(address => Stake) public stakes;

    event Staked(address indexed user, uint256 amount, uint256 start);
    event Unstaked(address indexed user, uint256 amount);

    constructor(ERC20 _token, uint256 _lockTime) {
        token = _token;
        lockTime = _lockTime;
    }

    function stake(uint256 amount) external nonReentrant {
        token.transferFrom(msg.sender, address(this), amount);
        stakes[msg.sender].amount += amount;
        stakes[msg.sender].start = block.timestamp;
        emit Staked(msg.sender, amount, stakes[msg.sender].start);
    }

    function claim() external nonReentrant {
        require(block.timestamp > stakes[msg.sender].start + lockTime, "Stake is still locked!");
        uint256 amount = stakes[msg.sender].amount;
        stakes[msg.sender].amount = 0;
        token.transfer(msg.sender, amount);
        emit Unstaked(msg.sender, amount);
    }
}

```



###### tags: `Solidity 工程師實戰營第 5 期`