# KryptoCamp 模擬考題
<style>
.question {
color: red;
}
.answer {
color: blue;
}
</style>
#### <span class="question">及格線:6題/10題</span>
## Solidity 基礎
Q1. 列舉五種 Solidity 的型別並且寫出實際例子
### <span class="answer">Answer</span>
``` solidity=
contract JasonKryptoCampTest {
// 布林,以true, false表示
bool public _isBool = false;
// interger
// uint256, uint32.... 正整數
// int256, int32.... 可以有負整數
uint256 public _ubank = 0;
//字串可以轉為 bytes型態,會更節省 gas
string public _sName = "Jason";
bytes32 public _bName = "Jason";
address public _aOwner = 0xf8789AB568eC6155Eb3F9056144346590dbF14cc;
}
```
Q2. 寫出一 function 可接受 eth value 並且 return 自定義結果
### <span class="answer">Answer</span>
``` solidity=
function addMyBank() public payable returns ( uint256 ){
_bank += msg.value;
return _bank;
}
```
Q3. ERC20 合約部屬至 rinkeby,寫上部屬的合約地址
### <span class="answer">Answer</span>
[contract address = 0x97c00D241b7A9544D796E8a1D0280DFf6Df97287](https://rinkeby.etherscan.io/address/0x97c00D241b7A9544D796E8a1D0280DFf6Df97287)
## Solidity 進階
Q1. 以文字描述為智能合約增加流動性的流程
### <span class="answer">Answer</span>
* <span class="answer">要先approve多少ERC20的Token給交易所使用。</span>
* <span class="answer">增加流動性時,記得要給錢(msg.value)</span>
Q2. 實作 ERC20 質押某代幣,timelock(固定鎖倉期,自定義), reward (回饋該代幣)
<span class="answer">Answer</span>
``` solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract BaseBank {
//質押代幣ERC20的interface
IERC20 private _stakingToken;
//質押的總數量
uint256 private _totalSupply = 0;
//個人質押數量
mapping( address => uint256 ) private _balanceOf;
//鎖質押 10 秒
uint256 private _lockBaseTime = 10 seconds;
//鎖質押的開始時間
mapping( address => uint256 ) private _lockTimeStart;
//鎖質押的結束時間
mapping( address => uint256 ) private _lockTimeEnd;
//回饋獎勵
uint256 private _rewardRate = 1;
//個人回饋的總獎勵
mapping( address => uint256 ) private _rewardOf;
//建構子
constructor( IERC20 stakingToken ) {
_stakingToken = stakingToken;
}
//存入多少代幣
function deposit( uint256 coin ) public {
//轉給合約多少代幣
_stakingToken.transferFrom( msg.sender, address(this), coin );
//加總合約代幣
_totalSupply += coin;
//加總合約的個人代幣
_balanceOf[ msg.sender ] += coin;
//設定個人存入的開始時間
_lockTimeStart[ msg.sender ] = block.timestamp;
//設定個人存入的結束時間
_lockTimeEnd[ msg.sender ] = block.timestamp + _lockBaseTime;
}
function withdraw( uint256 coin ) external {
//判斷取出時間是否大於存入的結束時間
require( block.timestamp >= _lockTimeEnd[ msg.sender ] , "lock time..." );
//判斷個人目前在合約的代幣是否比取出的代幣多
require( _balanceOf[ msg.sender ] >= coin, "withdraw too much" );
//加總個人的回饋獎勵
_rewardOf[ msg.sender ] += getReward();
//從合約轉移代幣給個人
_stakingToken.transfer( msg.sender, coin );
//合約的代幣減少總量
_totalSupply -= coin;
//個人的合約代幣減少總量
_balanceOf[ msg.sender ] -= coin;
}
//回饋方式
function getReward() public view returns ( uint256 ) {
uint256 reward = block.timestamp - _lockTimeEnd[ msg.sender ];
return reward * _rewardRate * _balanceOf[ msg.sender ];
}
//查看個人目前總回饋量
function getTotalRewardOf( address owner ) public view returns ( uint256 ) {
return _rewardOf[ owner ];
}
//查看目前合約代幣總量
function getTotalSupply() public view returns( uint256 ){
return _totalSupply;
}
//查看目前個人在合約的代幣量
function getBalanceOf( address owner ) public view returns( uint256 ){
return _balanceOf[ owner ];
}
}
```
## Solidity 整合開發相關
Q1. 試說明如何整合 VSCode, 以及 Remix 的開發環境
### <span class="answer">Answer</span>
* <span class="answer">Remixd: 連結本地端檔案</span>
* <span class="answer">Remix 亦可串接 Hardhat 本地鏈</span>
* <span class="answer">安裝:</span>
```
npm install -g @remix-project/remixd
```
* <span class="answer">使用:</span>
```
remixd -s path-to-your-project
```
* <span class="answer">進 Remix 點擊以下按鈕即可串連本地端: Connect to Localhost</span>
* <span class="answer">並可在 Remix DEPLOY & RUN TRANSACTIONS,將環境改成 Hardhat 區塊鏈</span>
Q2. 試寫出 contract auto verify 的 相關內容(npm install xxx, hardhat.config.js, npm hardhat xxx)
### <span class="answer">Answer</span>
```
npm install --save-dev hardhat
npm install --save dotenv
npm install --save @nomiclabs/hardhat-etherscan
```
* <span class="answer">hardhat.config.js</span>
``` solidity=
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-etherscan");
require("dotenv").config();
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const ENDPOINT_URL = process.env.ENDPOINT_URL;
module.exports = {
solidity: "0.8.4",
networks: {
rinkeby: {
url: ENDPOINT_URL,
accounts: [`0x${PRIVATE_KEY}`],
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};
```
```
npx hardhat run scripts/deploy.js --network rinkeby
```
Q3. 試寫出部屬 ERC721 合約並且先 mint 10 個 NFT 的 hardhat.js script
### <span class="answer">Answer</span>
* <span class="answer">ERC721</span>
``` solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract TestToken is ERC721, Ownable {
constructor( uint256 number ) ERC721("TestToken", "TT") {
for( uint i = 0 ; i < number ; i++ )
{
safeMint( msg.sender, i );
}
}
function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
}
```
* <span class="answer">部屬</span>
``` solidity=
const hre = require("hardhat");
async function main() {
const TestToken = await hre.ethers.getContractFactory("TestToken");
const testtoken = await TestToken.deploy( 10 );
await testtoken.deployed();
console.log("Deployed to:", testtoken.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
```
## Solidity 資安相關
標註程式碼第幾行可能有資安問題,並說明何種資安問題,並提出解法
``` solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract DepositContract {
using SafeMath for uint256;
mapping(address => uint256) public balance;
function deposit() external payable {
balance[msg.sender] += msg.value;
}
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 withdrawAll() external {
(bool success, ) = msg.sender.call{value: balance[msg.sender]}("");
require(success, "Transfer failed.");
balance[msg.sender] = 0;
}
}
```
### <span class="answer">Answer</span>
* <span class="answer">在withdrawAll函式中, 使用msg.sender.call的方式提款, 攻擊者可以使用 Re-Entrancy Attack 無限提款</span>
* <span class="answer">使用mapping(address => bool) islock確保呼叫時,不會被Re-Entrancy</span>
``` solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract DepositContract {
using SafeMath for uint256;
mapping(address => uint256) public balance;
mapping(address => bool) private islock;
function deposit() external payable {
balance[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external {
require(balance[msg.sender] >= amount, "Account balance is not enough");
//鎖倉
require(islock[msg.sender] == false, "is lock");
islock[msg.sender] = true;
balance[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed.");
islock[msg.sender] = false;
}
function withdrawAll() external {
//鎖倉
require(islock[msg.sender] == false, "is lock");
islock[msg.sender] = true;
(bool success, ) = msg.sender.call{value: balance[msg.sender]}("");
require(success, "Transfer failed.");
balance[msg.sender] = 0;
islock[msg.sender] = false;
}
}
```
## Solidity 節省 Gasfee 相關
嘗試閱讀以下程式碼,如何寫出更節省Gas fee 的方法
``` solidity=
address[] whitelistedAddresses;
function isWhitelisted(address _user) public view returns (bool) {
for (uint i = 0; i < whitelistedAddresses.length; i++) {
if (whitelistedAddresses[i] == _user) {
return true;
}
}
return false;
}
```
完整程式碼
https://etherscan.io/address/0xae122962331c2b02f837b2aa501d3c5d903ed28a#code
### <span class="answer">Answer</span>
<span class="answer">使用mapping來解決這問題</span>
``` solidity=
mapping( address => bool ) private iswhitelistedAddresses;
function isWhitelisted( address _user ) public view returns (bool) {
return iswhitelistedAddresses[ _user ];
}
```