# Team HW5 作業連結:https://github.com/DeFiHackLabs-BootCamp/Fall-HW5-Team ![笑](https://hackmd.io/_uploads/rkqmohUQye.gif) ## Note - NFT 的能力值只能透過 Training 合約來更新 ## 漏洞分析 ### RequestQuest.sol 1. Line-3 `_safeMint()` 會造成 Reentrancy attack, 使用合約能不斷重複呼叫 `mintChiikawa()` ```solidity= function mintChiikawa() public { uint256 tokenId = _nextTokenId++; _safeMint(msg.sender, tokenId); // Initialize metadata for the minted token chiikawaStats[tokenId] = ChiikawaStats({smallFeet: true, weapon: true, miniBag: true, calmAndReady: false, battlesWon: 0}); } ``` ### Training.sol 1. Line-43 使用 `transferFrom()` 可能會將 NFT 轉入無法再轉出的合約地址,應該要使用 `safeTransferFrom()` ```solidity= function unstake(uint256 tokenId) external { require(stakes[tokenId].owner == msg.sender, "Not the token owner"); uint256 stakedDuration = block.timestamp - stakes[tokenId].startTime; uint256 daysStaked = stakedDuration / 1 days; // Assuming Conquest contract has a function to update metadata properties IRequestQuest.ChiikawaStats memory stakedChiikawaStats = requestQuest.getChiikawaStats(tokenId); emit Unstaked(msg.sender, tokenId, stakedDuration); delete stakes[tokenId]; // Clear staking info // Apply changes based on the days staked if (daysStaked >= 1) { stakedChiikawaStats.smallFeet = false; CompensationContract.mint(msg.sender, 1); } if (daysStaked >= 2) { stakedChiikawaStats.weapon = false; CompensationContract.mint(msg.sender, 1); } if (daysStaked >= 3) { stakedChiikawaStats.miniBag = false; CompensationContract.mint(msg.sender, 1); } if (daysStaked >= 4) { stakedChiikawaStats.calmAndReady = true; CompensationContract.mint(msg.sender, 1); } // Only call the update function if the token was staked for at least one day if (daysStaked >= 1) { requestQuest.updateChiikawaStats( tokenId, stakedChiikawaStats.smallFeet, stakedChiikawaStats.weapon, stakedChiikawaStats.miniBag, stakedChiikawaStats.calmAndReady, stakedChiikawaStats.battlesWon ); } // Continue with unstaking logic (e.g., transferring the token back to the owner) requestQuest.transferFrom(address(this), msg.sender, tokenId); } ``` ### Conquest.sol 1. `weedingOrBattle()` 可以被惡意合約調用,如果沒對戰成功獲得 CompensationToken [從Line-39] 就 revert transaction,造成遊戲公平性問題 2. Line-23和Line-41 `totalPrize` 這個變數沒實際功用,可以刪除來節省 gas ```solidity= function weedingOrBattle(uint256 _tokenId, uint256 _compensationBet) external { if (defender == address(0)) { defender = msg.sender; defenderBet = _compensationBet; defenderTokenId = _tokenId; emit Weeding(msg.sender, _tokenId, _compensationBet); requestQuestNft.transferFrom(msg.sender, address(this), _tokenId); CompensationToken.transferFrom(msg.sender, address(this), _compensationBet); } else { // CompensationToken.transferFrom(msg.sender, address(this), _compensationBet); _battle(_tokenId, _compensationBet); } } function _battle(uint256 _tokenId, uint256 _compensationBet) internal { address _defender = defender; require(defenderBet == _compensationBet, "Conquest: Bet amounts do not match"); uint256 defenderChiikawaSkill = getChiikawaSkill(defenderTokenId); uint256 challengerChiikawaSkill = getChiikawaSkill(_tokenId); uint256 totalBattleSkill = defenderChiikawaSkill + challengerChiikawaSkill; uint256 totalPrize = defenderBet + _compensationBet; uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % totalBattleSkill; // Reset the defender defender = address(0); emit Battle(msg.sender, _tokenId, random < defenderChiikawaSkill ? _defender : msg.sender); // If random <= defenderChiikawaSkill -> defenderChiikawaSkill wins, otherwise they lose if (random <= defenderChiikawaSkill) { // We give them the money the defender deposited, and the challenger's bet CompensationToken.transfer(_defender, defenderBet); CompensationToken.transferFrom(msg.sender, _defender, _compensationBet); } else { // Otherwise, since the challenger never sent us the money, we just give the money in the contract CompensationToken.transfer(msg.sender, _compensationBet); } totalPrize = 0; // Return the defender's NFT requestQuestNft.transferFrom(address(this), _defender, defenderTokenId); } ```