@safe-global/safe-contracts
Version:
Ethereum multisig contract
167 lines (141 loc) • 7.63 kB
text/typescript
import { expect } from "chai";
import hre, { deployments, waffle } from "hardhat";
import { BigNumber } from "ethers";
import "@nomiclabs/hardhat-ethers";
import { AddressZero } from "@ethersproject/constants";
import {
defaultTokenCallbackHandlerContract,
defaultTokenCallbackHandlerDeployment,
deployContract,
getSafeTemplate,
} from "../utils/setup";
import { executeContractCallWithSigners } from "../../src/utils/execution";
describe("FallbackManager", async () => {
const setupWithTemplate = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture();
const source = `
contract Mirror {
function lookAtMe() public returns (bytes memory) {
return msg.data;
}
function nowLookAtYou(address you, string memory howYouLikeThat) public returns (bytes memory) {
return msg.data;
}
}`;
const mirror = await deployContract(user1, source);
return {
safe: await getSafeTemplate(),
mirror,
};
});
const [user1, user2] = waffle.provider.getWallets();
describe("setFallbackManager", async () => {
it("is correctly set on deployment", async () => {
const { safe } = await setupWithTemplate();
const handler = await defaultTokenCallbackHandlerDeployment();
// Check fallback handler
await expect(
await hre.ethers.provider.getStorageAt(safe.address, "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"),
).to.be.eq("0x" + "".padStart(64, "0"));
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", handler.address, AddressZero, 0, AddressZero);
// Check fallback handler
await expect(
await hre.ethers.provider.getStorageAt(safe.address, "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"),
).to.be.eq("0x" + handler.address.toLowerCase().slice(2).padStart(64, "0"));
});
it("is correctly set", async () => {
const { safe } = await setupWithTemplate();
const handler = await defaultTokenCallbackHandlerDeployment();
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", AddressZero, AddressZero, 0, AddressZero);
// Check fallback handler
await expect(
await hre.ethers.provider.getStorageAt(safe.address, "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"),
).to.be.eq("0x" + "".padStart(64, "0"));
await expect(executeContractCallWithSigners(safe, safe, "setFallbackHandler", [handler.address], [user1]))
.to.emit(safe, "ChangedFallbackHandler")
.withArgs(handler.address);
// Check fallback handler
await expect(
await hre.ethers.provider.getStorageAt(safe.address, "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"),
).to.be.eq("0x" + handler.address.toLowerCase().slice(2).padStart(64, "0"));
});
it("emits event when is set", async () => {
const { safe } = await setupWithTemplate();
const handler = await defaultTokenCallbackHandlerDeployment();
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", AddressZero, AddressZero, 0, AddressZero);
// Check event
await expect(executeContractCallWithSigners(safe, safe, "setFallbackHandler", [handler.address], [user1]))
.to.emit(safe, "ChangedFallbackHandler")
.withArgs(handler.address);
});
it("is called when set", async () => {
const { safe } = await setupWithTemplate();
const handler = await defaultTokenCallbackHandlerDeployment();
const safeHandler = (await defaultTokenCallbackHandlerContract()).attach(safe.address);
// Check that Safe is NOT setup
await expect(await safe.getThreshold()).to.be.deep.eq(BigNumber.from(0));
// Check unset callbacks
await expect(safeHandler.callStatic.onERC1155Received(AddressZero, AddressZero, 0, 0, "0x")).to.be.reverted;
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", handler.address, AddressZero, 0, AddressZero);
// Check callbacks
await expect(await safeHandler.callStatic.onERC1155Received(AddressZero, AddressZero, 0, 0, "0x")).to.be.eq("0xf23a6e61");
});
it("sends along msg.sender on simple call", async () => {
const { safe, mirror } = await setupWithTemplate();
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", mirror.address, AddressZero, 0, AddressZero);
const tx = {
to: safe.address,
data: mirror.interface.encodeFunctionData("lookAtMe"),
};
// Check that mock works as handler
const response = await user1.call(tx);
expect(response).to.be.eq(
"0x" +
"0000000000000000000000000000000000000000000000000000000000000020" +
"0000000000000000000000000000000000000000000000000000000000000018" +
"7f8dc53c" +
user1.address.slice(2).toLowerCase() +
"0000000000000000",
);
});
it("sends along msg.sender on more complex call", async () => {
const { safe, mirror } = await setupWithTemplate();
// Setup Safe
await safe.setup([user1.address, user2.address], 1, AddressZero, "0x", mirror.address, AddressZero, 0, AddressZero);
const tx = {
to: safe.address,
data: mirror.interface.encodeFunctionData("nowLookAtYou", [user2.address, "pink<>black"]),
};
// Check that mock works as handler
const response = await user1.call(tx);
expect(response).to.be.eq(
"0x" +
"0000000000000000000000000000000000000000000000000000000000000020" +
"0000000000000000000000000000000000000000000000000000000000000098" +
// Function call
"b2a88d99" +
"000000000000000000000000" +
user2.address.slice(2).toLowerCase() +
"0000000000000000000000000000000000000000000000000000000000000040" +
"000000000000000000000000000000000000000000000000000000000000000b" +
"70696e6b3c3e626c61636b000000000000000000000000000000000000000000" +
user1.address.slice(2).toLowerCase() +
"0000000000000000",
);
});
it("cannot be set to self", async () => {
const { safe } = await setupWithTemplate();
// Setup Safe
await safe.setup([user1.address], 1, AddressZero, "0x", AddressZero, AddressZero, 0, AddressZero);
// The transaction execution function doesn't bubble up revert messages so we check for a generic transaction fail code GS013
await expect(executeContractCallWithSigners(safe, safe, "setFallbackHandler", [safe.address], [user1])).to.be.revertedWith(
"GS013",
);
});
});
});