diff --git a/benchmark/GnosisSafe.Creation.spec.ts b/benchmark/GnosisSafe.Creation.spec.ts new file mode 100644 index 000000000..8a9e2e4d1 --- /dev/null +++ b/benchmark/GnosisSafe.Creation.spec.ts @@ -0,0 +1,9 @@ +import "@nomiclabs/hardhat-ethers"; +import { setupBenchmarkContracts } from "./utils/setup" + +const contractSetup = setupBenchmarkContracts(undefined, true) +describe("GnosisSafe", async () => { + it("creation", async () => { + await contractSetup() + }) +}) \ No newline at end of file diff --git a/benchmark/utils/setup.ts b/benchmark/utils/setup.ts index bf7a9a71d..68370027d 100644 --- a/benchmark/utils/setup.ts +++ b/benchmark/utils/setup.ts @@ -13,9 +13,9 @@ export interface Contracts { additions: any | undefined } -const generateTarget = async (owners: Wallet[], threshold: number, guardAddress: string) => { +const generateTarget = async (owners: Wallet[], threshold: number, guardAddress: string, logGasUsage?: boolean) => { const fallbackHandler = await getDefaultCallbackHandler() - const safe = await getSafeWithOwners(owners.map((owner) => owner.address), threshold, fallbackHandler.address) + const safe = await getSafeWithOwners(owners.map((owner) => owner.address), threshold, fallbackHandler.address, logGasUsage) await executeContractCallWithSigners(safe, safe, "setGuard", [guardAddress], owners) return safe } @@ -28,14 +28,14 @@ export const configs = [ { name: "3 out of 5", signers: [user1, user2, user3, user4, user5], threshold: 3 }, ] -const setupBenchmarkContracts = async (benchmarkFixture?: () => Promise) => { - return await deployments.createFixture(async ({ deployments }) => { +export const setupBenchmarkContracts = (benchmarkFixture?: () => Promise, logGasUsage?: boolean) => { + return deployments.createFixture(async ({ deployments }) => { await deployments.fixture(); const guardFactory = await hre.ethers.getContractFactory("DelegateCallTransactionGuard"); const guard = await guardFactory.deploy(AddressZero) const targets = [] for (const config of configs) { - targets.push(await generateTarget(config.signers, config.threshold, config.useGuard ? guard.address : AddressZero)) + targets.push(await generateTarget(config.signers, config.threshold, config.useGuard ? guard.address : AddressZero, logGasUsage)) } return { targets, @@ -54,7 +54,7 @@ export interface Benchmark { export const benchmark = async (topic: string, benchmarks: Benchmark[]) => { for (const benchmark of benchmarks) { const { name, prepare, after, fixture } = benchmark - const contractSetup = await setupBenchmarkContracts(fixture) + const contractSetup = setupBenchmarkContracts(fixture) describe(`${topic} - ${name}`, async () => { it("with an EOA", async () => { const contracts = await contractSetup() diff --git a/contracts/GnosisSafe.sol b/contracts/GnosisSafe.sol index 1af9b00f9..12445a6c3 100644 --- a/contracts/GnosisSafe.sol +++ b/contracts/GnosisSafe.sol @@ -38,6 +38,13 @@ contract GnosisSafe //); bytes32 private constant SAFE_MSG_TYPEHASH = 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca; + event SafeSetup( + address indexed initiator, + address[] owners, + uint256 threshold, + address initializer, + address fallbackHandler + ); event ApproveHash( bytes32 indexed approvedHash, address indexed owner @@ -99,6 +106,7 @@ contract GnosisSafe // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment handlePayment(payment, 0, 1, paymentToken, paymentReceiver); } + emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler); } /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction. diff --git a/test/core/GnosisSafe.Execution.spec.ts b/test/core/GnosisSafe.Execution.spec.ts index c4332aa1b..1c4d60178 100644 --- a/test/core/GnosisSafe.Execution.spec.ts +++ b/test/core/GnosisSafe.Execution.spec.ts @@ -120,7 +120,6 @@ describe("GnosisSafe", async () => { executeTx(safe, tx, [await safeApproveHash(user1, safe, tx, true)]).then((tx) => { executedTx = tx; return tx }) ).to.emit(safe, "ExecutionSuccess") const receipt = await hre.ethers.provider.getTransactionReceipt(executedTx!!.hash) - console.log(receipt.logs) const successEvent = safe.interface.decodeEventLog("ExecutionSuccess", receipt.logs[0].data, receipt.logs[0].topics) expect(successEvent.txHash).to.be.eq(calculateSafeTransactionHash(safe, tx, await chainId())) // Gas costs are around 3000, so even if we specified a safeTxGas from 100000 we should not use more diff --git a/test/core/GnosisSafe.Setup.spec.ts b/test/core/GnosisSafe.Setup.spec.ts index 47c3f8988..0c253225e 100644 --- a/test/core/GnosisSafe.Setup.spec.ts +++ b/test/core/GnosisSafe.Setup.spec.ts @@ -22,7 +22,7 @@ describe("GnosisSafe", async () => { } }) - describe("Setup", async () => { + describe("setup", async () => { it('should not allow to call setup on singleton', async () => { await deployments.fixture(); const singleton = await getSafeSingleton() @@ -45,7 +45,9 @@ describe("GnosisSafe", async () => { it('should set domain hash', async () => { const { template } = await setupTests() - await template.setup([user1.address, user2.address, user3.address], 2, AddressZero, "0x", AddressZero, AddressZero, 0, AddressZero) + await expect( + template.setup([user1.address, user2.address, user3.address], 2, AddressZero, "0x", AddressZero, AddressZero, 0, AddressZero) + ).to.emit(template, "SafeSetup").withArgs(user1.address, [user1.address, user2.address, user3.address], 2, AddressZero, AddressZero) await expect(await template.domainSeparator()).to.be.eq(calculateSafeDomainSeparator(template, await chainId())) await expect(await template.getOwners()).to.be.deep.eq([user1.address, user2.address, user3.address]) await expect(await template.getThreshold()).to.be.deep.eq(BigNumber.from(2)) @@ -129,7 +131,9 @@ describe("GnosisSafe", async () => { }` const testIntializer = await deployContract(user1, source); const initData = testIntializer.interface.encodeFunctionData("init", ["0x42baddad"]) - await template.setup([user1.address, user2.address, user3.address], 2, testIntializer.address, initData, AddressOne, AddressZero, 0, AddressZero) + await expect( + template.setup([user1.address, user2.address, user3.address], 2, testIntializer.address, initData, AddressOne, AddressZero, 0, AddressZero) + ).to.emit(template, "SafeSetup").withArgs(user1.address, [user1.address, user2.address, user3.address], 2, testIntializer.address, AddressOne) await expect(await template.domainSeparator()).to.be.eq(calculateSafeDomainSeparator(template, await chainId())) await expect(await template.getOwners()).to.be.deep.eq([user1.address, user2.address, user3.address]) await expect(await template.getThreshold()).to.be.deep.eq(BigNumber.from(2)) diff --git a/test/utils/execution.ts b/test/utils/execution.ts index dcfa13a32..fed64b531 100644 --- a/test/utils/execution.ts +++ b/test/utils/execution.ts @@ -111,10 +111,10 @@ export const buildSignatureBytes = (signatures: SafeSignature[]): string => { return signatureBytes } -export const logGas = async (message: string, tx: Promise): Promise => { +export const logGas = async (message: string, tx: Promise, skip?: boolean): Promise => { return tx.then(async (result) => { const receipt = await result.wait() - console.log(" Used", receipt.gasUsed.toNumber(), `gas for >${message}<`) + if (!skip) console.log(" Used", receipt.gasUsed.toNumber(), `gas for >${message}<`) return result }) } diff --git a/test/utils/setup.ts b/test/utils/setup.ts index bbca8dd00..62c6c0ac9 100644 --- a/test/utils/setup.ts +++ b/test/utils/setup.ts @@ -2,6 +2,7 @@ import hre, { deployments } from "hardhat" import { Wallet, Contract } from "ethers" import { AddressZero } from "@ethersproject/constants"; import solc from "solc" +import { logGas } from "./execution"; export const defaultCallbackHandlerDeployment = async () => { return await deployments.get("DefaultCallbackHandler"); @@ -68,9 +69,13 @@ export const getSafeTemplate = async () => { return Safe.attach(template); } -export const getSafeWithOwners = async (owners: string[], threshold?: number, fallbackHandler?: string) => { +export const getSafeWithOwners = async (owners: string[], threshold?: number, fallbackHandler?: string, logGasUsage?: boolean) => { const template = await getSafeTemplate() - await template.setup(owners, threshold || owners.length, AddressZero, "0x", fallbackHandler || AddressZero, AddressZero, 0, AddressZero) + await logGas( + `Setup Safe with ${owners.length} owner(s)${fallbackHandler && fallbackHandler !== AddressZero ? " and fallback handler" : ""}`, + template.setup(owners, threshold || owners.length, AddressZero, "0x", fallbackHandler || AddressZero, AddressZero, 0, AddressZero), + !logGasUsage + ) return template }