UNPKG

@safe-global/safe-contracts

Version:
168 lines (134 loc) 8.88 kB
import { expect } from "chai"; import hre, { deployments, waffle, ethers } from "hardhat"; import "@nomiclabs/hardhat-ethers"; import { compile, getCreateCall, getSafeWithOwners } from "../utils/setup"; import { buildContractCall, executeTx, safeApproveHash } from "../../src/utils/execution"; import { parseEther } from "@ethersproject/units"; const CONTRACT_SOURCE = ` contract Test { address public creator; constructor() payable { creator = msg.sender; } function x() public pure returns (uint) { return 21; } }`; describe("CreateCall", async () => { const [user1] = waffle.provider.getWallets(); const setupTests = deployments.createFixture(async ({ deployments }) => { await deployments.fixture(); const testContract = await compile(CONTRACT_SOURCE); return { safe: await getSafeWithOwners([user1.address]), createCall: await getCreateCall(), testContract, }; }); describe("performCreate", async () => { it("should revert if called directly and no value is on the factory", async () => { const { createCall, testContract } = await setupTests(); await expect(createCall.performCreate(1, testContract.data)).to.be.revertedWith("Could not deploy contract"); }); it("can call factory directly", async () => { const { createCall, testContract } = await setupTests(); const createCallNonce = await ethers.provider.getTransactionCount(createCall.address); const address = ethers.utils.getContractAddress({ from: createCall.address, nonce: createCallNonce }); await expect(createCall.performCreate(0, testContract.data)).to.emit(createCall, "ContractCreation").withArgs(address); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(createCall.address); }); it("should fail if Safe does not have value to send along", async () => { const { safe, createCall, testContract } = await setupTests(); const tx = await buildContractCall(createCall, "performCreate", [1, testContract.data], await safe.nonce(), true); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])).to.revertedWith("GS013"); }); it("should successfully create contract and emit event", async () => { const { safe, createCall, testContract } = await setupTests(); const safeEthereumNonce = await ethers.provider.getTransactionCount(safe.address); const address = ethers.utils.getContractAddress({ from: safe.address, nonce: safeEthereumNonce }); // We require this as 'emit' check the address of the event const safeCreateCall = createCall.attach(safe.address); const tx = await buildContractCall(createCall, "performCreate", [0, testContract.data], await safe.nonce(), true); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])) .to.emit(safe, "ExecutionSuccess") .and.to.emit(safeCreateCall, "ContractCreation") .withArgs(address); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(safe.address); }); it("should successfully create contract and send along ether", async () => { const { safe, createCall, testContract } = await setupTests(); await user1.sendTransaction({ to: safe.address, value: parseEther("1") }); await expect(await hre.ethers.provider.getBalance(safe.address)).to.be.deep.eq(parseEther("1")); const safeEthereumNonce = await ethers.provider.getTransactionCount(safe.address); const address = ethers.utils.getContractAddress({ from: safe.address, nonce: safeEthereumNonce }); // We require this as 'emit' check the address of the event const safeCreateCall = createCall.attach(safe.address); const tx = await buildContractCall(createCall, "performCreate", [parseEther("1"), testContract.data], await safe.nonce(), true); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])) .to.emit(safe, "ExecutionSuccess") .and.to.emit(safeCreateCall, "ContractCreation") .withArgs(address); await expect(await hre.ethers.provider.getBalance(safe.address)).to.be.deep.eq(parseEther("0")); await expect(await hre.ethers.provider.getBalance(address)).to.be.deep.eq(parseEther("1")); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(safe.address); }); }); describe("performCreate2", async () => { const salt = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("createCall")); it("should revert if called directly and no value is on the factory", async () => { const { createCall, testContract } = await setupTests(); await expect(createCall.performCreate2(1, testContract.data, salt)).to.be.revertedWith("Could not deploy contract"); }); it("can call factory directly", async () => { const { createCall, testContract } = await setupTests(); const address = ethers.utils.getCreate2Address(createCall.address, salt, ethers.utils.keccak256(testContract.data)); await expect(createCall.performCreate2(0, testContract.data, salt)).to.emit(createCall, "ContractCreation").withArgs(address); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(createCall.address); }); it("should fail if Safe does not have value to send along", async () => { const { safe, createCall, testContract } = await setupTests(); const tx = await buildContractCall(createCall, "performCreate2", [1, testContract.data, salt], await safe.nonce(), true); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])).to.revertedWith("GS013"); }); it("should successfully create contract and emit event", async () => { const { safe, createCall, testContract } = await setupTests(); const address = ethers.utils.getCreate2Address(safe.address, salt, ethers.utils.keccak256(testContract.data)); // We require this as 'emit' check the address of the event const safeCreateCall = createCall.attach(safe.address); const tx = await buildContractCall(createCall, "performCreate2", [0, testContract.data, salt], await safe.nonce(), true); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])) .to.emit(safe, "ExecutionSuccess") .and.to.emit(safeCreateCall, "ContractCreation") .withArgs(address); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(safe.address); }); it("should successfully create contract and send along ether", async () => { const { safe, createCall, testContract } = await setupTests(); await user1.sendTransaction({ to: safe.address, value: parseEther("1") }); await expect(await hre.ethers.provider.getBalance(safe.address)).to.be.deep.eq(parseEther("1")); const address = ethers.utils.getCreate2Address(safe.address, salt, ethers.utils.keccak256(testContract.data)); // We require this as 'emit' check the address of the event const safeCreateCall = createCall.attach(safe.address); const tx = await buildContractCall( createCall, "performCreate2", [parseEther("1"), testContract.data, salt], await safe.nonce(), true, ); await expect(executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)])) .to.emit(safe, "ExecutionSuccess") .and.to.emit(safeCreateCall, "ContractCreation") .withArgs(address); await expect(await hre.ethers.provider.getBalance(safe.address)).to.be.deep.eq(parseEther("0")); await expect(await hre.ethers.provider.getBalance(address)).to.be.deep.eq(parseEther("1")); const newContract = new ethers.Contract(address, testContract.interface, user1); expect(await newContract.creator()).to.be.eq(safe.address); }); }); });