import { expect } from "chai"; import hre, { deployments, waffle } from "hardhat"; import "@nomiclabs/hardhat-ethers"; import { deployContract, getSimulateTxAccessor, getSafeWithOwners, getCompatFallbackHandler } from "../utils/setup"; import { buildContractCall } from "../../src/utils/execution"; import { parseEther } from "ethers/lib/utils"; describe("SimulateTxAccessor", async () => { const [user1, user2] = waffle.provider.getWallets(); const setupTests = deployments.createFixture(async ({ deployments }) => { await deployments.fixture(); const accessor = await getSimulateTxAccessor() const source = ` contract Test { function sendAndReturnBalance(address payable target, uint256 amount) public returns (uint256) { (bool success,) = target.call{ value: amount }(""); require(success, "Transfer failed"); return target.balance; } }` const interactor = await deployContract(user1, source); const handler = await getCompatFallbackHandler() const safe = await getSafeWithOwners([user1.address], 1, handler.address) const simulator = handler.attach(safe.address) return { safe, accessor, interactor, simulator } }) describe("estimate", async () => { it('should enforce delegatecall', async () => { const { accessor } = await setupTests() const source = ` contract Test { function killme() public { selfdestruct(payable(msg.sender)); } }` const killLib = await deployContract(user1, source); const tx = buildContractCall(killLib, "killme", [], 0) let code = await hre.ethers.provider.getCode(accessor.address) await expect( accessor.simulate(tx.to, tx.value, tx.data, tx.operation) ).to.be.revertedWith("SimulateTxAccessor should only be called via delegatecall") expect(await hre.ethers.provider.getCode(accessor.address)).to.be.eq(code) }) it('simulate call', async () => { const { safe, accessor, simulator } = await setupTests() const tx = buildContractCall(safe, "getOwners", [], 0) const simulationData = accessor.interface.encodeFunctionData("simulate", [tx.to, tx.value, tx.data, tx.operation]) const acccessibleData = await simulator.callStatic.simulate(accessor.address, simulationData) const simulation = accessor.interface.decodeFunctionResult("simulate", acccessibleData) expect( safe.interface.decodeFunctionResult("getOwners", simulation.returnData)[0] ).to.be.deep.eq([user1.address]) expect( simulation.success ).to.be.true expect( simulation.estimate.toNumber() ).to.be.lte(10000) }) it('simulate delegatecall', async () => { const { safe, accessor, interactor, simulator } = await setupTests() await user1.sendTransaction({to: safe.address, value: parseEther("1")}) const userBalance = await hre.ethers.provider.getBalance(user2.address) const tx = buildContractCall(interactor, "sendAndReturnBalance", [user2.address, parseEther("1")], 0, true) const simulationData = accessor.interface.encodeFunctionData("simulate", [tx.to, tx.value, tx.data, tx.operation]) const acccessibleData = await simulator.callStatic.simulate(accessor.address, simulationData) const simulation = accessor.interface.decodeFunctionResult("simulate", acccessibleData) expect( interactor.interface.decodeFunctionResult("sendAndReturnBalance", simulation.returnData)[0] ).to.be.deep.eq(userBalance.add(parseEther("1"))) expect( simulation.success ).to.be.true expect( simulation.estimate.toNumber() ).to.be.lte(15000) }) it('simulate revert', async () => { const { safe, accessor, interactor, simulator } = await setupTests() const tx = buildContractCall(interactor, "sendAndReturnBalance", [user2.address, parseEther("1")], 0, true) const simulationData = accessor.interface.encodeFunctionData("simulate", [tx.to, tx.value, tx.data, tx.operation]) const acccessibleData = await simulator.callStatic.simulate(accessor.address, simulationData) const simulation = accessor.interface.decodeFunctionResult("simulate", acccessibleData) expect(simulation.returnData).to.be.deep.eq("0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f5472616e73666572206661696c65640000000000000000000000000000000000") expect( simulation.success ).to.be.false expect( simulation.estimate.toNumber() ).to.be.lte(20000) }) }) })