From b6871bc87e9ff58b87439a871c7a2e5c7b3858f9 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 04:55:53 -0700 Subject: [PATCH] chore: migrate E2E deployment test to Foundry (#225) ## Description Migrate the deployment e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #149 --- test/e2e/Deployment.t.sol | 110 ++++++++++++++++++++++++++++ test/e2e/deployment.test.ts | 141 ------------------------------------ 2 files changed, 110 insertions(+), 141 deletions(-) create mode 100644 test/e2e/Deployment.t.sol delete mode 100644 test/e2e/deployment.test.ts diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol new file mode 100644 index 00000000..456549e5 --- /dev/null +++ b/test/e2e/Deployment.t.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; + +import {Helper} from "./Helper.sol"; + +interface IEIP173Proxy { + function owner() external view returns (address); +} + +// ref: https://github.com/wighawag/hardhat-deploy/blob/e0ffcf9e7dc92b246e832c4d175f9dbd8b6df14d/solc_0.8/proxy/EIP173Proxy.sol +bytes32 constant EIP173_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + +contract DeploymentTest is Helper(false) { + event Metadata(string, bytes); + + function test__same_built_and_deployed_bytecode_metadata__authenticator() external { + _assertBuiltAndDeployedMetadataCoincide(address(allowListImpl), "GPv2AllowListAuthentication"); + } + + function test__same_built_and_deployed_bytecode_metadata__settlement() external { + _assertBuiltAndDeployedMetadataCoincide(address(settlement), "GPv2Settlement"); + } + + function test__same_built_and_deployed_bytecode_metadata__vault_relayer() external { + _assertBuiltAndDeployedMetadataCoincide(address(vaultRelayer), "GPv2VaultRelayer"); + } + + function test__determininstic_addresses__authenticator__proxy() external view { + assertEq( + _computeCreate2Addr( + abi.encodePacked( + vm.getCode("EIP173Proxy"), + abi.encode( + _implementationAddress(address(allowList)), + owner, + abi.encodeCall(GPv2AllowListAuthentication.initializeManager, (owner)) + ) + ) + ), + address(authenticator), + "authenticator address not as expected" + ); + } + + function test__determininstic_addresses__authenticator__implementation() external view { + assertEq( + _computeCreate2Addr(vm.getCode("GPv2AllowListAuthentication")), + _implementationAddress(address(allowList)), + "authenticator impl address not as expected" + ); + } + + function test__determininstic_addresses__settlement() external view { + assertEq( + _computeCreate2Addr( + abi.encodePacked(vm.getCode("GPv2Settlement"), abi.encode(address(authenticator), address(vault))) + ), + address(settlement), + "settlement address not as expected" + ); + } + + function test__authorization__authenticator_has_dedicated_owner() external view { + assertEq(IEIP173Proxy(address(allowList)).owner(), owner, "owner not as expected"); + } + + function test__authorization__authenticator_has_dedicated_manager() external view { + assertEq(allowList.manager(), owner, "manager not as expected"); + } + + function _assertBuiltAndDeployedMetadataCoincide(address addr, string memory artifactName) internal { + bytes memory deployedCode = vm.getDeployedCode(artifactName); + assertEq( + keccak256(_getMetadata(string(abi.encodePacked("deployed ", artifactName)), addr.code)), + keccak256(_getMetadata(artifactName, deployedCode)), + "metadata doesnt match" + ); + } + + function _getMetadata(string memory hint, bytes memory bytecode) internal returns (bytes memory metadata) { + assembly ("memory-safe") { + // the last two bytes encode the size of the cbor encoded metadata + let bytecodeSize := mload(bytecode) + let bytecodeStart := add(bytecode, 0x20) + let cborSizeOffset := add(bytecodeStart, sub(bytecodeSize, 0x20)) + let cborSize := and(mload(cborSizeOffset), 0xffff) + + // copy the metadata out + metadata := mload(0x40) + let metadataSize := add(cborSize, 0x02) + mstore(metadata, metadataSize) + let metadataOffset := add(bytecodeStart, sub(bytecodeSize, metadataSize)) + mcopy(add(metadata, 0x20), metadataOffset, metadataSize) + + // update free memory ptr + mstore(0x40, add(metadata, add(metadataSize, 0x20))) + } + emit Metadata(hint, metadata); + } + + function _computeCreate2Addr(bytes memory initCode) internal view returns (address) { + return vm.computeCreate2Address(SALT, hashInitCode(initCode), deployer); + } + + function _implementationAddress(address proxy) internal view returns (address) { + return address(uint160(uint256(vm.load(proxy, EIP173_IMPLEMENTATION_SLOT)))); + } +} diff --git a/test/e2e/deployment.test.ts b/test/e2e/deployment.test.ts deleted file mode 100644 index 394c2c53..00000000 --- a/test/e2e/deployment.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { artifacts, ethers } from "hardhat"; -import Proxy from "hardhat-deploy/extendedArtifacts/EIP173Proxy.json"; - -import { - ContractName, - DeploymentArguments, - deterministicDeploymentAddress, - implementationAddress, - proxyInterface, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -async function contractAddress( - contractName: C, - ...deploymentArguments: DeploymentArguments -): Promise { - const artifact = await artifacts.readArtifact(contractName); - return deterministicDeploymentAddress(artifact, deploymentArguments); -} - -async function builtAndDeployedMetadataCoincide( - contractAddress: string, - contractName: string, -): Promise { - const contractArtifacts = await artifacts.readArtifact(contractName); - - const code = await ethers.provider.send("eth_getCode", [ - contractAddress, - "latest", - ]); - - // NOTE: The last 53 bytes in a deployed contract's bytecode contains the - // contract metadata. Compare the deployed contract's metadata with the - // compiled contract's metadata. - // - const metadata = (bytecode: string) => bytecode.slice(-106); - - return metadata(code) === metadata(contractArtifacts.deployedBytecode); -} - -describe("E2E: Deployment", () => { - let owner: Wallet; - let manager: Wallet; - let user: Wallet; - - let authenticator: Contract; - let vault: Contract; - let settlement: Contract; - let vaultRelayer: Contract; - - beforeEach(async () => { - ({ - owner, - manager, - wallets: [user], - authenticator, - vault, - settlement, - vaultRelayer, - } = await deployTestContracts()); - - authenticator.connect(user); - settlement.connect(user); - vaultRelayer.connect(user); - }); - - describe("same built and deployed bytecode metadata", () => { - it("authenticator", async () => { - expect( - await builtAndDeployedMetadataCoincide( - await implementationAddress(ethers.provider, authenticator.address), - "GPv2AllowListAuthentication", - ), - ).to.be.true; - }); - - it("settlement", async () => { - expect( - await builtAndDeployedMetadataCoincide( - settlement.address, - "GPv2Settlement", - ), - ).to.be.true; - }); - - it("vaultRelayer", async () => { - expect( - await builtAndDeployedMetadataCoincide( - vaultRelayer.address, - "GPv2VaultRelayer", - ), - ).to.be.true; - }); - }); - - describe("deterministic addresses", () => { - describe("authenticator", () => { - it("proxy", async () => { - expect( - deterministicDeploymentAddress(Proxy, [ - await implementationAddress(ethers.provider, authenticator.address), - owner.address, - authenticator.interface.encodeFunctionData("initializeManager", [ - manager.address, - ]), - ]), - ).to.equal(authenticator.address); - }); - - it("implementation", async () => { - expect(await contractAddress("GPv2AllowListAuthentication")).to.equal( - await implementationAddress(ethers.provider, authenticator.address), - ); - }); - }); - - it("settlement", async () => { - expect( - await contractAddress( - "GPv2Settlement", - authenticator.address, - vault.address, - ), - ).to.equal(settlement.address); - }); - }); - - describe("authorization", () => { - it("authenticator has dedicated owner", async () => { - const proxy = proxyInterface(authenticator); - expect(await proxy.owner()).to.equal(owner.address); - }); - - it("authenticator has dedicated manager", async () => { - expect(await authenticator.manager()).to.equal(manager.address); - }); - }); -});