From dba38235367c8c6492ad70f01a867c65e7bfe9fe Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Sun, 15 Sep 2024 19:35:05 +0530 Subject: [PATCH 1/5] chore: migrate E2E deployment test to Foundry --- test/e2e/Deployment.t.sol | 125 ++++++++++++++++++++++++++++++++ test/e2e/deployment.test.ts | 141 ------------------------------------ 2 files changed, 125 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..54925238 --- /dev/null +++ b/test/e2e/Deployment.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; + +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; + +import {GPv2Settlement} from "src/contracts/GPv2Settlement.sol"; +import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {Eip712} from "../libraries/Eip712.sol"; + +import {Sign} from "../libraries/Sign.sol"; +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {SwapEncoder} from "../libraries/encoders/SwapEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +interface IEIP173Proxy { + function owner() external view returns (address); +} + +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_hash_dedicated_owner() external view { + assertEq(IEIP173Proxy(address(allowList)).owner(), owner, "owner not as expected"); + } + + function test__authorization__authenticator_hash_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), address(this)); + } + + function _implementationAddress(address proxy) internal view returns (address) { + return address( + uint160(uint256(vm.load(proxy, 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc))) + ); + } +} 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); - }); - }); -}); From 36c3eee00b2ff27f2b4e306ab7126d39a40370de Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Sun, 15 Sep 2024 20:07:06 +0530 Subject: [PATCH 2/5] chore: fix solhint --- test/e2e/Deployment.t.sol | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol index 54925238..edec4205 100644 --- a/test/e2e/Deployment.t.sol +++ b/test/e2e/Deployment.t.sol @@ -1,25 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8; -import {Vm} from "forge-std/Vm.sol"; - -import {IERC20} from "src/contracts/interfaces/IERC20.sol"; -import {IVault} from "src/contracts/interfaces/IVault.sol"; - import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; -import {GPv2Settlement} from "src/contracts/GPv2Settlement.sol"; -import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; -import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; -import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; - -import {Eip712} from "../libraries/Eip712.sol"; - -import {Sign} from "../libraries/Sign.sol"; -import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; -import {SwapEncoder} from "../libraries/encoders/SwapEncoder.sol"; -import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; -import {Helper, IERC20Mintable} from "./Helper.sol"; +import {Helper} from "./Helper.sol"; interface IEIP173Proxy { function owner() external view returns (address); From 25450646d9693959bd44f2981a412bef7f345b6e Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Sun, 15 Sep 2024 23:22:33 +0530 Subject: [PATCH 3/5] chore: fix test --- test/e2e/Deployment.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol index edec4205..32985159 100644 --- a/test/e2e/Deployment.t.sol +++ b/test/e2e/Deployment.t.sol @@ -98,7 +98,7 @@ contract DeploymentTest is Helper(false) { } function _computeCreate2Addr(bytes memory initCode) internal view returns (address) { - return vm.computeCreate2Address(SALT, hashInitCode(initCode), address(this)); + return vm.computeCreate2Address(SALT, hashInitCode(initCode), deployer); } function _implementationAddress(address proxy) internal view returns (address) { From 7bbe2dbe4835140c925c3728ee8900721c7fda9e Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 16:53:26 +0530 Subject: [PATCH 4/5] chore: review comments --- test/e2e/Deployment.t.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol index 32985159..18c5e31e 100644 --- a/test/e2e/Deployment.t.sol +++ b/test/e2e/Deployment.t.sol @@ -9,6 +9,9 @@ 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); @@ -59,11 +62,11 @@ contract DeploymentTest is Helper(false) { ); } - function test__authorization__authenticator_hash_dedicated_owner() external view { + function test__authorization__authenticator_has_dedicated_owner() external view { assertEq(IEIP173Proxy(address(allowList)).owner(), owner, "owner not as expected"); } - function test__authorization__authenticator_hash_dedicated_manager() external view { + function test__authorization__authenticator_has_dedicated_manager() external view { assertEq(allowList.manager(), owner, "manager not as expected"); } @@ -103,7 +106,7 @@ contract DeploymentTest is Helper(false) { function _implementationAddress(address proxy) internal view returns (address) { return address( - uint160(uint256(vm.load(proxy, 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc))) + uint160(uint256(vm.load(proxy, EIP173_IMPLEMENTATION_SLOT))) ); } } From c1e9ce20efbd76935cb2216060e4893cb617de3f Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 16:54:28 +0530 Subject: [PATCH 5/5] chore: fmt --- test/e2e/Deployment.t.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol index 18c5e31e..456549e5 100644 --- a/test/e2e/Deployment.t.sol +++ b/test/e2e/Deployment.t.sol @@ -105,8 +105,6 @@ contract DeploymentTest is Helper(false) { } function _implementationAddress(address proxy) internal view returns (address) { - return address( - uint160(uint256(vm.load(proxy, EIP173_IMPLEMENTATION_SLOT))) - ); + return address(uint160(uint256(vm.load(proxy, EIP173_IMPLEMENTATION_SLOT)))); } }