diff --git a/package.json b/package.json index de5baaa5..0023942a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gnosis.pm/gp-v2-contracts", - "version": "0.0.1-alpha.12", + "version": "0.0.1-alpha.13", "license": "LGPL-3.0-or-later", "scripts": { "build": "tsc && hardhat compile", diff --git a/src/ts/proxy.ts b/src/ts/proxy.ts index 54cb4057..55c1875e 100644 --- a/src/ts/proxy.ts +++ b/src/ts/proxy.ts @@ -1,17 +1,18 @@ -// defined in https://eips.ethereum.org/EIPS/eip-1967 +import { BigNumber, BytesLike, Contract, ethers } from "ethers"; -import { BigNumber, Contract } from "ethers"; -import { ethers } from "hardhat"; -import Proxy from "hardhat-deploy/extendedArtifacts/EIP173Proxy.json"; - -// The proxy contract used by hardhat-deploy implements EIP-1967 (Standard Proxy -// Storage Slot). See . -function slot(string: string) { +/** + * Compute an EIP-1967 slot for the specified name. The proxy contract used by + * `hardhat-deploy` implements EIP-1967 (Standard Proxy Storage Slot). + * + * . + */ +function slot(name: string): BytesLike { return ethers.utils.defaultAbiCoder.encode( ["bytes32"], - [BigNumber.from(ethers.utils.id(string)).sub(1)], + [BigNumber.from(ethers.utils.id(name)).sub(1)], ); } + const IMPLEMENTATION_STORAGE_SLOT = slot("eip1967.proxy.implementation"); const OWNER_STORAGE_SLOT = slot("eip1967.proxy.admin"); @@ -22,10 +23,13 @@ const OWNER_STORAGE_SLOT = slot("eip1967.proxy.admin"); * @param proxy Address of the proxy contract. * @returns The address of the contract storing the proxy implementation. */ -export async function implementationAddress(proxy: string): Promise { +export async function implementationAddress( + provider: ethers.providers.Provider, + proxy: string, +): Promise { const [implementation] = ethers.utils.defaultAbiCoder.decode( ["address"], - await ethers.provider.getStorageAt(proxy, IMPLEMENTATION_STORAGE_SLOT), + await provider.getStorageAt(proxy, IMPLEMENTATION_STORAGE_SLOT), ); return implementation; } @@ -37,14 +41,31 @@ export async function implementationAddress(proxy: string): Promise { * @param proxy Address of the proxy contract. * @returns The address of the administrator of the proxy. */ -export async function ownerAddress(proxy: string): Promise { +export async function ownerAddress( + provider: ethers.providers.Provider, + proxy: string, +): Promise { const [owner] = ethers.utils.defaultAbiCoder.decode( ["address"], - await ethers.provider.getStorageAt(proxy, OWNER_STORAGE_SLOT), + await provider.getStorageAt(proxy, OWNER_STORAGE_SLOT), ); return owner; } +/** + * EIP-173 proxy ABI in "human-readable ABI" format. The proxy used by the + * deployment plugin implements this interface, and copying it here avoids + * pulling in `hardhat` as a dependency for just this ABI. + * + * + */ +export const EIP173_PROXY_ABI = [ + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function owner() view external returns(address)", + "function transferOwnership(address newOwner) external", + "function supportsInterface(bytes4 interfaceID) external view returns (bool)", +]; + /** * Returns the proxy interface for the specified address. * @@ -52,10 +73,9 @@ export async function ownerAddress(proxy: string): Promise { * @returns A Ethers.js contract instance for interacting with the proxy. */ export function proxyInterface(contract: Contract): Contract { - const { abi } = Proxy; return new Contract( contract.address, - abi, + EIP173_PROXY_ABI, contract.signer ?? contract.provider, ); } diff --git a/test/e2e/deployment.test.ts b/test/e2e/deployment.test.ts index b1f9a550..6aa45435 100644 --- a/test/e2e/deployment.test.ts +++ b/test/e2e/deployment.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; import { Contract, Wallet } from "ethers"; -import { artifacts } from "hardhat"; +import { artifacts, ethers } from "hardhat"; import Proxy from "hardhat-deploy/extendedArtifacts/EIP173Proxy.json"; import { @@ -50,7 +50,7 @@ describe("E2E: Deployment", () => { it("authenticator", async () => { expect( await builtAndDeployedMetadataCoincide( - await implementationAddress(authenticator.address), + await implementationAddress(ethers.provider, authenticator.address), "GPv2AllowListAuthentication", ), ).to.be.true; @@ -80,7 +80,7 @@ describe("E2E: Deployment", () => { it("proxy", async () => { expect( deterministicDeploymentAddress(Proxy, [ - await implementationAddress(authenticator.address), + await implementationAddress(ethers.provider, authenticator.address), authenticator.interface.encodeFunctionData("initializeManager", [ manager.address, ]), @@ -91,7 +91,7 @@ describe("E2E: Deployment", () => { it("implementation", async () => { expect(await contractAddress("GPv2AllowListAuthentication")).to.equal( - await implementationAddress(authenticator.address), + await implementationAddress(ethers.provider, authenticator.address), ); }); });