# 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 ]; } ```