# KryptoCamp Week3 HW ## 基礎作業 1. 透過 remix JavaScript VM 部署合約 2. 部署任一自訂 ERC20 Token 3. 部署質押合約 - Deposit(存入) - Reward(回饋) - TimeLock(固定鎖倉期) ### ERC20 Token ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract HooToken is ERC20 { constructor() ERC20("HooToken", "HT") payable { _mint(msg.sender, msg.value); } } ``` ### Bank ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Bank { IERC20 public stakingToken; uint256 public totalSupply; mapping(address => uint256) public balanceOf; uint256 public lockDuration = 10 seconds; mapping(address => uint256) public lockStartOf; mapping(address => uint256) public lockEndOf; uint256 public rewardRate = 1; mapping(address => uint256) public rewardOf; constructor(IERC20 _stakingToken) { stakingToken = _stakingToken; } function deposit(uint256 _amount) external { stakingToken.transferFrom(msg.sender, address(this), _amount); totalSupply += _amount; balanceOf[msg.sender] += _amount; lockStartOf[msg.sender] = block.timestamp; lockEndOf[msg.sender] = block.timestamp + lockDuration; } function withdraw(uint256 _amount) external { require(block.timestamp > lockEndOf[msg.sender], "withdraw too fast"); require(_amount <= balanceOf[msg.sender] , "insufficient" ); rewardOf[msg.sender] = getReward(); stakingToken.transfer(msg.sender, _amount); totalSupply -= _amount; balanceOf[msg.sender] -= _amount; } function getReward() public view returns (uint256) { uint256 duration = block.timestamp - lockStartOf[msg.sender]; uint256 reward = duration * rewardRate * balanceOf[msg.sender]; return reward; } } ``` ## 進階作業 1. 透過 remix Injected Web3 部署至 Rinkeby 2. 部署任一自訂 ERC20 Token 3. 部署質押合約 - Deposit(存入) - Reward(回饋) - TimeLock(根據鎖倉期給予不同回饋加乘) ### ERC20 Token https://rinkeby.etherscan.io/tx/0x41f3e52edc550a2bedcb4fe25d67d215b62d6df3d53928cdb0493e18180ee242 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract HooToken is ERC20 { constructor() ERC20("HooToken", "HT") payable { _mint(msg.sender, msg.value); } } ``` ### Bank https://rinkeby.etherscan.io/tx/0xf6caf66e2d36a4faab6164160a347cb657f5c29349847f260f626586744aa6b4 ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Bank { IERC20 public stakingToken; uint256 public totalSupply; mapping(address => uint256) public balanceOf; uint256 public lockDuration = 10 seconds; //mapping(address => uint256) public lockStartOf; //mapping(address => uint256) public lockEndOf; uint256 public rewardRate = 1; uint256 public lastTime; uint256 public rewardRealtime; mapping(address => uint256) public rewardOf; struct Deposit { uint256 amount; uint256 start; // lockStartOf uint256 end; //lockEndOf } mapping(address => Deposit[]) public depositOf; constructor(IERC20 _stakingToken) { stakingToken = _stakingToken; } function earn() public view returns (uint256) { uint256 duration = block.timestamp - lastTime; uint256 total = rewardOf[msg.sender] + balanceOf[msg.sender] *duration * rewardRate; return total; } modifier updateReward() { rewardOf[msg.sender] = earn(); lastTime = block.timestamp; _; } function deposit(uint256 _amount) external updateReward { // ******* comment for testing ******** // stakingToken.transferFrom(msg.sender, address(this), _amount); totalSupply += _amount; balanceOf[msg.sender] += _amount; //lockStartOf[msg.sender] = block.timestamp; //lockEndOf[msg.sender] = block.timestamp + lockDuration; depositOf[msg.sender].push( Deposit({ amount: _amount, start: block.timestamp, end: block.timestamp + lockDuration }) ); } // *** // uint256 _amount function withdraw(uint256 _depositId) external updateReward { // storage: call by refenece // memory: call by value Deposit[] storage deposits = depositOf[msg.sender]; uint256 amount = deposits[_depositId].amount; //require(block.timestamp > lockEndOf[msg.sender], "withdraw too fast"); //require(_amount <= balanceOf[msg.sender] , "insufficient" ); require(_depositId < deposits.length, "not exist"); require(block.timestamp >= deposits[_depositId].end, "too soon"); //rewardOf[msg.sender] = getReward(); //rewardOf[msg.sender] += getRewardID(_depositId); stakingToken.transfer(msg.sender, amount); totalSupply -= amount; balanceOf[msg.sender] -= amount; // remove uint256 last = deposits.length -1; deposits[_depositId] = deposits[last]; deposits.pop(); } function getRewardDebug() external updateReward () { rewardRealtime = rewardOf[msg.sender]; } function getReward() external updateReward returns (uint256) { uint256 reward = rewardOf[msg.sender]; return reward; } /* function computeReward(uint256 _depositId) public view returns (uint256) { //uint256 duration = block.timestamp - lockStartOf[msg.sender]; //uint256 reward = duration * rewardRate * balanceOf[msg.sender]; uint256 start = depositOf[msg.sender][_depositId].start; uint256 amount = depositOf[msg.sender][_depositId].amount; uint256 reward = (block.timestamp - start) * amount * rewardRate; return reward; } */ /* function getRewardID(uint256 _depositId) public view returns (uint256) { uint256 N = depositOf[msg.sender].length; uint256 rewards; for (uint256 i = 0; i < N; i++) { rewards += computeReward(i); } return rewards; } */ } ``` ## 終極作業 1. 透過 remix Injected Web3 部署至 Rinkeby 2. 部署任兩種自訂 ERC20 Token(一個質押,一個回饋) 3. 部署質押合約 - Deposit(存入) - Reward(回饋另一個 Token) - TimeLock(根據鎖倉期給予不同回饋加乘) 4. 利用範例 AMM 交易所進行 Reward Token 的 ETH 兌換 ### ERC20 Token #### Staking Token https://rinkeby.etherscan.io/tx/0xa6a948f7635c2520c389d968190c3a32354d4405601c218a8413708501879ec6 #### Reward Token https://rinkeby.etherscan.io/tx/0x4e484438ed34785658d9b1df6595d5b381a438a3e8d95d75fcaccff5c29edb4f ```solidity= // contracts/GLDToken.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract HooToken is ERC20 { constructor() ERC20("HooToken", "HT") payable { _mint(msg.sender, msg.value); } } contract QEToken is ERC20 { constructor() ERC20("QEToken", "QT") payable { _mint(msg.sender, 1 * 10**18); } } ``` ### Bank https://rinkeby.etherscan.io/tx/0xdc4fdd5b5459b21d707e3bce3256929e8073ed6c6ca01aeb1b40af6baf0b213f ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Bank { IERC20 public stakingToken; IERC20 public rewardToken; uint256 public totalSupply; mapping(address => uint256) public balanceOf; uint256 public lockDuration = 10 seconds; //mapping(address => uint256) public lockStartOf; //mapping(address => uint256) public lockEndOf; uint256 public rewardRate = 1; uint256 public lastTime; uint256 public rewardRealtime; mapping(address => uint256) public rewardOf; struct Deposit { uint256 amount; uint256 start; // lockStartOf uint256 end; //lockEndOf } mapping(address => Deposit[]) public depositOf; constructor(IERC20 _stakingToken, IERC20 _rewardToken) { stakingToken = _stakingToken; rewardToken = _rewardToken; } function earn() public view returns (uint256) { uint256 duration = block.timestamp - lastTime; uint256 total = rewardOf[msg.sender] + balanceOf[msg.sender] *duration * rewardRate; return total; } modifier updateReward() { rewardOf[msg.sender] = earn(); lastTime = block.timestamp; _; } function deposit(uint256 _amount) external updateReward { // ******* comment for testing ******** // stakingToken.transferFrom(msg.sender, address(this), _amount); totalSupply += _amount; balanceOf[msg.sender] += _amount; //lockStartOf[msg.sender] = block.timestamp; //lockEndOf[msg.sender] = block.timestamp + lockDuration; depositOf[msg.sender].push( Deposit({ amount: _amount, start: block.timestamp, end: block.timestamp + lockDuration }) ); } // *** // uint256 _amount function withdraw(uint256 _depositId) external updateReward { // storage: call by refenece // memory: call by value Deposit[] storage deposits = depositOf[msg.sender]; uint256 amount = deposits[_depositId].amount; //require(block.timestamp > lockEndOf[msg.sender], "withdraw too fast"); //require(_amount <= balanceOf[msg.sender] , "insufficient" ); require(_depositId < deposits.length, "not exist"); require(block.timestamp >= deposits[_depositId].end, "too soon"); //rewardOf[msg.sender] = getReward(); //rewardOf[msg.sender] += getRewardID(_depositId); stakingToken.transfer(msg.sender, amount); totalSupply -= amount; balanceOf[msg.sender] -= amount; // remove uint256 last = deposits.length -1; deposits[_depositId] = deposits[last]; deposits.pop(); } function getRewardDebug() external updateReward () { rewardRealtime = rewardOf[msg.sender]; } function getReward() external updateReward () { uint256 reward = rewardOf[msg.sender]; rewardOf[msg.sender] = 0; rewardToken.transfer(msg.sender, reward); } /* function computeReward(uint256 _depositId) public view returns (uint256) { //uint256 duration = block.timestamp - lockStartOf[msg.sender]; //uint256 reward = duration * rewardRate * balanceOf[msg.sender]; uint256 start = depositOf[msg.sender][_depositId].start; uint256 amount = depositOf[msg.sender][_depositId].amount; uint256 reward = (block.timestamp - start) * amount * rewardRate; return reward; } */ /* function getRewardID(uint256 _depositId) public view returns (uint256) { uint256 N = depositOf[msg.sender].length; uint256 rewards; for (uint256 i = 0; i < N; i++) { rewards += computeReward(i); } return rewards; } */ } ``` ###### tags: `KryptoCamp`