diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a6df9fd..05e51b96 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,7 @@ jobs: run: | forge --version if [ "${{ matrix.profile }}" == "solc-0.7.6" ]; then - forge build --sizes --use 0.7.6 --skip 'test/*' + forge build --sizes --use 0.7.6 --skip 'test/*' --skip 'script/*' else forge build --sizes fi diff --git a/.gitignore b/.gitignore index d482968b..e8fa9d16 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ build/ coverage/ deployments/*/solcInputs deployments/localhost/ -lib/ +/lib/ node_modules/ coverage.json yarn-error.log @@ -15,9 +15,10 @@ cache/ out/ # Ignores development broadcast logs -!/broadcast /broadcast/*/31337/ /broadcast/**/dry-run/ +# Ignores logs of some scripts +/broadcast/TransferOwnership.s.sol/ # Dotenv file -.env \ No newline at end of file +.env diff --git a/README.md b/README.md index eb3ea62d..a71557d3 100644 --- a/README.md +++ b/README.md @@ -138,10 +138,24 @@ We recommend to never sign orders of this form and, if developing a contract tha There is a dedicated script to change the owner of the authenticator proxy. -Usage and parameters can be seen by running: +The following parameters can be set: ```sh -yarn hardhat transfer-ownership --help +export ETH_RPC_URL='https://rpc.url.example.com' +export NEW_OWNER=0x1111111111111111111111111111111111111111 +export RESET_MANAGER=true # true if the new owner should also become the manager, false otherwise +``` + +To test run the script from a specific owner (sender): + +```sh +forge script script/TransferOwnership.s.sol:TransferOwnership --rpc-url "$ETH_RPC_URL" --sender 0xcA771eda0c70aA7d053aB1B25004559B918FE662 +``` + +To actually execute the transaction: + +```sh +forge script script/TransferOwnership.s.sol:TransferOwnership --rpc-url "$ETH_RPC_URL" --private-key 0x0000000000000000000000000000000000000000000000000000000000000001 --broadcast ``` ## Releases diff --git a/foundry.toml b/foundry.toml index 7a628779..b0d9f155 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,6 +8,8 @@ via_ir = false optimizer = true optimizer_runs = 1000000 +fs_permissions = [{ access = "read", path = "./networks.json"}] + [fmt] ignore = [ # We don't want to change the formatting of our main contracts until the diff --git a/hardhat.config.ts b/hardhat.config.ts index a7a23379..04e5953c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -10,8 +10,6 @@ import type { HttpNetworkUserConfig } from "hardhat/types"; import type { MochaOptions } from "mocha"; import yargs from "yargs"; -import { setupTasks } from "./src/tasks"; - const argv = yargs .option("network", { type: "string", @@ -65,25 +63,23 @@ switch (MOCHA_CONF) { case undefined: break; case "coverage": - // End to end and task tests are skipped because: + // End to end tests are skipped because: // - coverage tool does not play well with proxy deployment with // hardhat-deploy // - coverage compiles without optimizer and, unlike Waffle, hardhat-deploy // strictly enforces the contract size limits from EIP-170 - mocha.grep = /^(?!E2E|Task)/; + mocha.grep = /^(?!E2E)/; // Note: unit is Wei, not GWei. This is a workaround to make the coverage // tool work with the London hardfork. initialBaseFeePerGas = 1; break; case "ignored in coverage": - mocha.grep = /^E2E|Task/; + mocha.grep = /^E2E/; break; default: throw new Error("Invalid MOCHA_CONF"); } -setupTasks(); - export default { mocha, paths: { diff --git a/script/TransferOwnership.s.sol b/script/TransferOwnership.s.sol new file mode 100644 index 00000000..5b4e6d82 --- /dev/null +++ b/script/TransferOwnership.s.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8.26; + +import {console} from "forge-std/Script.sol"; + +import {GPv2AllowListAuthentication} from "../src/contracts/GPv2AllowListAuthentication.sol"; + +import {ERC173, ERC165} from "./interfaces/ERC173.sol"; +import {NetworksJson} from "./lib/NetworksJson.sol"; + +contract TransferOwnership is NetworksJson { + // Required input + string private constant INPUT_ENV_NEW_OWNER = "NEW_OWNER"; + string private constant INPUT_ENV_RESET_MANAGER = "RESET_MANAGER"; + // Optional input + string private constant INPUT_ENV_AUTHENTICATOR_PROXY = "AUTHENTICATOR_PROXY"; + + NetworksJson internal networksJson; + + struct ScriptParams { + address newOwner; + bool resetManager; + ERC173 authenticatorProxy; + } + + constructor() { + networksJson = new NetworksJson(); + } + + function run() public virtual { + ScriptParams memory params = paramsFromEnv(); + runWith(params); + } + + function runWith(ScriptParams memory params) public { + console.log(string.concat("Using account ", vm.toString(msg.sender))); + + checkIsProxy(address(params.authenticatorProxy)); + + address owner = params.authenticatorProxy.owner(); + if (owner != msg.sender) { + revert(string.concat("Account does NOT match current owner ", vm.toString(owner))); + } + + GPv2AllowListAuthentication authenticator = GPv2AllowListAuthentication(address(params.authenticatorProxy)); + + // Make sure to reset the manager BEFORE transferring ownership, or else + // we will not be able to do it once we lose permissions. + if (params.resetManager) { + console.log( + string.concat( + "Setting new solver manager from ", + vm.toString(authenticator.manager()), + " to ", + vm.toString(params.newOwner) + ) + ); + vm.broadcast(msg.sender); + authenticator.setManager(params.newOwner); + console.log("Set new solver manager account."); + } + + console.log( + string.concat( + "Setting new authenticator proxy owner from ", vm.toString(owner), " to ", vm.toString(params.newOwner) + ) + ); + vm.broadcast(msg.sender); + params.authenticatorProxy.transferOwnership(params.newOwner); + console.log("Set new owner of the authenticator proxy."); + } + + function paramsFromEnv() internal view returns (ScriptParams memory) { + address newOwner = vm.envAddress(INPUT_ENV_NEW_OWNER); + bool resetManager = vm.envBool(INPUT_ENV_RESET_MANAGER); + + address authenticatorProxy; + try vm.envAddress(INPUT_ENV_AUTHENTICATOR_PROXY) returns (address env) { + authenticatorProxy = env; + } catch { + try networksJson.addressOf("GPv2AllowListAuthentication_Proxy") returns (address addr) { + authenticatorProxy = addr; + } catch { + revert( + string.concat( + "Could not find default authenticator address in file ", + networksJson.PATH(), + " for network with chain id ", + vm.toString(block.chainid), + ". Export variable ", + INPUT_ENV_AUTHENTICATOR_PROXY, + " to manually specify a non-standard address for the authenticator." + ) + ); + } + } + + return ScriptParams({ + newOwner: newOwner, + resetManager: resetManager, + authenticatorProxy: ERC173(authenticatorProxy) + }); + } + + function checkIsProxy(address candidate) internal view { + if (address(candidate).code.length == 0) { + revert(string.concat("No code at target authenticator proxy ", vm.toString(address(candidate)), ".")); + } + + bool isERC173; + try ERC165(candidate).supportsInterface(type(ERC173).interfaceId) returns (bool isERC173_) { + isERC173 = isERC173_; + } catch { + isERC173 = false; + } + if (!isERC173) { + revert( + string.concat( + "Not a valid proxy contract: target address ", + vm.toString(address(candidate)), + " does not support the ERC173 interface." + ) + ); + } + } +} diff --git a/script/interfaces/ERC173.sol b/script/interfaces/ERC173.sol new file mode 100644 index 00000000..0024e0d4 --- /dev/null +++ b/script/interfaces/ERC173.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0 +pragma solidity >=0.7.6 <0.9.0; + +// Copied from: +// + +/// @title ERC-173 Contract Ownership Standard +/// Note: the ERC-165 identifier for this interface is 0x7f5828d0 +interface ERC173 { + /// @dev This emits when ownership of a contract changes. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Get the address of the owner + /// @return The address of the owner. + function owner() external view returns (address); + + /// @notice Set the address of the new owner of the contract + /// @dev Set _newOwner to address(0) to renounce any ownership. + /// @param _newOwner The address of the new owner of the contract + function transferOwnership(address _newOwner) external; +} + +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} diff --git a/script/lib/NetworksJson.sol b/script/lib/NetworksJson.sol new file mode 100644 index 00000000..44db2de0 --- /dev/null +++ b/script/lib/NetworksJson.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8.26; + +import {Script} from "forge-std/Script.sol"; + +contract NetworksJson is Script { + string public constant PATH = "./networks.json"; + + function addressOf(string memory contractName) public view returns (address) { + return addressByChainId(contractName, block.chainid); + } + + function addressByChainId(string memory contractName, uint256 chainId) public view returns (address) { + string memory networksJson = vm.readFile(PATH); + return + vm.parseJsonAddress(networksJson, string.concat(".", contractName, ".", vm.toString(chainId), ".address")); + } +} diff --git a/src/tasks/index.ts b/src/tasks/index.ts deleted file mode 100644 index b97bc255..00000000 --- a/src/tasks/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { setupTransferOwnershipTask } from "./transferOwnership"; - -export function setupTasks(): void { - setupTransferOwnershipTask(); -} diff --git a/src/tasks/transferOwnership.ts b/src/tasks/transferOwnership.ts deleted file mode 100644 index caa5dcc3..00000000 --- a/src/tasks/transferOwnership.ts +++ /dev/null @@ -1,91 +0,0 @@ -import "hardhat-deploy"; -import "@nomiclabs/hardhat-ethers"; - -import { task } from "hardhat/config"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; - -import { proxyInterface } from "../ts"; - -import { getDeployedContract } from "./ts/deployment"; -import { getNamedSigner } from "./ts/signers"; -import { prompt, transactionUrl } from "./ts/tui"; - -interface Args { - newOwner: string; - resetManager: boolean; - dryRun: boolean; -} - -async function transferOwnership( - { newOwner, resetManager, dryRun }: Args, - hre: HardhatRuntimeEnvironment, -) { - const owner = await getNamedSigner(hre, "owner"); - const authenticator = await getDeployedContract( - "GPv2AllowListAuthentication", - hre, - ); - const proxy = proxyInterface(authenticator); - - console.log(`Using account ${owner.address}`); - const currentOwner = await proxy.owner(); - if (owner.address !== currentOwner) { - console.warn(`Account does NOT match current owner ${currentOwner}`); - return; - } - - if (resetManager) { - console.log( - `Setting new solver manager from ${await authenticator.manager()} to ${newOwner}`, - ); - } - console.log(`Transfering ownership from ${currentOwner} to ${newOwner}`); - - if (dryRun) { - if (resetManager) { - await authenticator.connect(owner).callStatic.setManager(newOwner); - } - await proxy.connect(owner).callStatic.transferOwnership(newOwner); - console.log("Successfully simulated ownership transfer."); - } else if (await prompt(hre, "Execute?")) { - // Make sure to reset the manager BEFORE transferring ownership, or else - // we will not be able to do it once we lose permissions. - if (resetManager) { - const setManager = await authenticator - .connect(owner) - .setManager(newOwner); - console.log(transactionUrl(hre, setManager)); - await setManager.wait(); - console.log("Set new solver manager account."); - } - - const setOwner = await proxy.connect(owner).transferOwnership(newOwner); - console.log(transactionUrl(hre, setOwner)); - await setOwner.wait(); - console.log("Set new proxy owner account."); - } else { - console.log("Operation aborted."); - } -} - -const setupTransferOwnershipTask: () => void = () => { - task( - "transfer-ownership", - "Transfer ownership of the GPv2 authenticator contract", - ) - .addPositionalParam( - "newOwner", - `The account to transfer ownership of the GPv2 authenticator to`, - ) - .addFlag( - "resetManager", - "Additionally reset the manager account to the new owner.", - ) - .addFlag( - "dryRun", - "Just simulate the transaction instead of executing on the blockchain.", - ) - .setAction(transferOwnership); -}; - -export { setupTransferOwnershipTask }; diff --git a/src/tasks/ts/deployment.ts b/src/tasks/ts/deployment.ts deleted file mode 100644 index cf9fc017..00000000 --- a/src/tasks/ts/deployment.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Contract } from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; - -import { ContractName } from "../../ts"; - -const supportedNetworks = [ - "rinkeby", - "goerli", - "xdai", - "mainnet", - "sepolia", -] as const; -export type SupportedNetwork = (typeof supportedNetworks)[number]; -export function isSupportedNetwork( - network: string, -): network is SupportedNetwork { - return (supportedNetworks as readonly string[]).includes(network); -} - -export async function getDeployedContract( - name: ContractName, - { ethers, deployments }: HardhatRuntimeEnvironment, -): Promise { - const deployment = await deployments.get(name); - - return new Contract(deployment.address, deployment.abi).connect( - ethers.provider, - ); -} diff --git a/src/tasks/ts/signers.ts b/src/tasks/ts/signers.ts deleted file mode 100644 index 5b534c97..00000000 --- a/src/tasks/ts/signers.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import type { HardhatRuntimeEnvironment } from "hardhat/types"; - -export async function getNamedSigner( - { ethers, getNamedAccounts }: HardhatRuntimeEnvironment, - name: string, -): Promise { - const accounts = await getNamedAccounts(); - const account = accounts[name]; - if (account === undefined) { - throw new Error(`No account named ${name}`); - } - - const signer = (await ethers.getSigners()).find( - (signer) => signer.address == account, - ); - if (signer === undefined) { - throw new Error( - `No account '${name}' found among the signers. Did you export its private key with 'export PK='?`, - ); - } - return signer; -} diff --git a/src/tasks/ts/tui.ts b/src/tasks/ts/tui.ts deleted file mode 100644 index b8ffe45f..00000000 --- a/src/tasks/ts/tui.ts +++ /dev/null @@ -1,48 +0,0 @@ -import readline from "readline"; - -import { HardhatRuntimeEnvironment } from "hardhat/types"; - -// Only a single readline interface should be available at each point in time. -// If more than one is created, then any input to stdin will be printed more -// than once to stdout. -export const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -export async function prompt( - { network }: HardhatRuntimeEnvironment, - message: string, -): Promise { - if (network.name === "hardhat") { - // shortcut prompts in tests. - return true; - } - - const response = await new Promise((resolve) => - rl.question(`${message} (y/N) `, (response) => resolve(response)), - ); - return "y" === response.toLowerCase(); -} - -export interface TransactionLike { - hash: string; -} - -export function transactionUrl( - { network }: HardhatRuntimeEnvironment, - { hash }: TransactionLike, -): string { - switch (network.name) { - case "mainnet": - return `https://etherscan.io/tx/${hash}`; - case "rinkeby": - return `https://rinkeby.etherscan.io/tx/${hash}`; - case "goerli": - return `https://goerli.etherscan.io/tx/${hash}`; - case "xdai": - return `https://blockscout.com/xdai/mainnet/tx/${hash}`; - default: - return hash; - } -} diff --git a/test/script/TransferOwnership.t.sol b/test/script/TransferOwnership.t.sol new file mode 100644 index 00000000..d804bfb2 --- /dev/null +++ b/test/script/TransferOwnership.t.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8.26; + +import {Test} from "forge-std/Test.sol"; + +import {TransferOwnership, ERC173} from "script/TransferOwnership.s.sol"; +import {ERC165} from "script/interfaces/ERC173.sol"; +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; + +contract TestTransferOwnership is Test { + TransferOwnership private script; + ERC173 private proxy; + GPv2AllowListAuthentication private proxyAsAuthenticator; + address private owner; + + function setUp() public { + // It's not possible to use `prank` with a script: "you have an active + // prank; broadcasting and pranks are not compatible". + // Since this contract will be the executor of the script, as a + // workaround we'll make it the owner. + // This workaround also requires to make `msg.sender` an input to + // vm.broadcast(), otherwise the broadcaster in the test changes + // compared to when running the script. + owner = address(this); + + GPv2AllowListAuthentication impl = new GPv2AllowListAuthentication(); + script = new TransferOwnership(); + address deployed = deployAuthenticatorProxy(owner, address(impl)); + proxy = ERC173(deployed); + proxyAsAuthenticator = GPv2AllowListAuthentication(deployed); + } + + function test_transfers_proxy_ownership_and_resets_manager() public { + address newOwner = makeAddr("TestTransferOwnership: new proxy owner"); + assertEq(proxy.owner(), owner); + assertEq(proxyAsAuthenticator.manager(), owner); + + TransferOwnership.ScriptParams memory params = + TransferOwnership.ScriptParams({newOwner: newOwner, authenticatorProxy: proxy, resetManager: true}); + + script.runWith(params); + + assertEq(proxy.owner(), newOwner, "did not change the owner"); + assertEq(proxyAsAuthenticator.manager(), newOwner, "did not change the manager"); + } + + function test_only_transfers_proxy_ownership() public { + address newOwner = makeAddr("TestTransferOwnership: new proxy owner"); + assertEq(proxy.owner(), owner); + assertEq(proxyAsAuthenticator.manager(), owner); + + TransferOwnership.ScriptParams memory params = + TransferOwnership.ScriptParams({newOwner: newOwner, authenticatorProxy: proxy, resetManager: false}); + + script.runWith(params); + + assertEq(proxy.owner(), newOwner, "did not change the owner"); + assertEq(proxyAsAuthenticator.manager(), owner, "changed the manager"); + } + + function test_reverts_if_no_proxy_at_target() public { + address notAProxy = makeAddr("not a proxy"); + TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ + newOwner: makeAddr("some owner"), + authenticatorProxy: ERC173(notAProxy), + resetManager: false + }); + + vm.expectRevert(bytes(string.concat("No code at target authenticator proxy ", vm.toString(notAProxy), "."))); + script.runWith(params); + } + + function test_reverts_if_proxy_does_not_support_ERC173() public { + address noERC173Proxy = makeAddr("proxy not supporting ERC173"); + TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ + newOwner: makeAddr("some owner"), + authenticatorProxy: ERC173(noERC173Proxy), + resetManager: false + }); + vm.etch(noERC173Proxy, hex"1337"); + vm.mockCall( + noERC173Proxy, abi.encodeCall(ERC165.supportsInterface, type(ERC173).interfaceId), abi.encode(false) + ); + + vm.expectRevert( + bytes( + string.concat( + "Not a valid proxy contract: target address ", + vm.toString(noERC173Proxy), + " does not support the ERC173 interface." + ) + ) + ); + script.runWith(params); + } + + function test_reverts_if_proxy_reverts_on_supportsInterface() public { + address revertingProxy = makeAddr("proxy reverting on calls to supportsInterface"); + TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ + newOwner: makeAddr("some owner"), + authenticatorProxy: ERC173(revertingProxy), + resetManager: false + }); + vm.etch(revertingProxy, hex"1337"); + vm.mockCallRevert( + revertingProxy, + abi.encodeCall(ERC165.supportsInterface, type(ERC173).interfaceId), + abi.encode("some revert error") + ); + + vm.expectRevert( + bytes( + string.concat( + "Not a valid proxy contract: target address ", + vm.toString(revertingProxy), + " does not support the ERC173 interface." + ) + ) + ); + script.runWith(params); + } + + function deployAuthenticatorProxy(address targetOwner, address implementation) internal returns (address) { + // We deploy the proxy from bytecode to ensure compatibility with the + // existing contract setup, currently built with Solidity v0.7. + // See contract code and constructor arguments at: + // + + bytes memory deploymentBytecodeWithArgs = abi.encodePacked( + AUTHENTICATOR_PROXY_DEPLOYMENT_BYTECODE, + abi.encode( + implementation, + targetOwner, + abi.encodeCall(GPv2AllowListAuthentication.initializeManager, (targetOwner)) + ) + ); + + address deployed; + vm.startPrank(makeAddr("TestTransferOwnership: proxy deployer")); + assembly { + deployed := create(0, add(deploymentBytecodeWithArgs, 0x20), mload(deploymentBytecodeWithArgs)) + } + vm.stopPrank(); + + if (deployed == address(0)) { + revert("Error on proxy deployment"); + } + return deployed; + } +} + +// From +bytes constant AUTHENTICATOR_PROXY_DEPLOYMENT_BYTECODE = + hex"6080604052604051610bed380380610bed8339818101604052606081101561002657600080fd5b8151602083015160408085018051915193959294830192918464010000000082111561005157600080fd5b90830190602082018581111561006657600080fd5b825164010000000081118282018810171561008057600080fd5b82525081516020918201929091019080838360005b838110156100ad578181015183820152602001610095565b50505050905090810190601f1680156100da5780820380516001836020036101000a031916815260200191505b506040525050506100f1838261010260201b60201c565b6100fa82610225565b505050610299565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8054908390556040516001600160a01b0380851691908316907f5570d70a002632a7b0b3c9304cc89efb62d8da9eca0dbd7752c83b737906829690600090a3815115610220576000836001600160a01b0316836040518082805190602001908083835b602083106101a55780518252601f199092019160209182019101610186565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114610205576040519150601f19603f3d011682016040523d82523d6000602084013e61020a565b606091505b505090508061021e573d806000803e806000fd5b505b505050565b600061022f610286565b905081600080516020610bcd83398151915255816001600160a01b0316816001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080516020610bcd8339815191525490565b610925806102a86000396000f3fe60806040526004361061005e5760003560e01c80634f1ef286116100435780634f1ef286146101745780638da5cb5b14610201578063f2fde38b1461023f576100ca565b806301ffc9a7146100d45780633659cfe614610134576100ca565b366100ca57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f45544845525f52454a4543544544000000000000000000000000000000000000604482015290519081900360640190fd5b6100d261027f565b005b3480156100e057600080fd5b50610120600480360360208110156100f757600080fd5b50357fffffffff00000000000000000000000000000000000000000000000000000000166102ca565b604080519115158252519081900360200190f35b34801561014057600080fd5b506100d26004803603602081101561015757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661048d565b6100d26004803603604081101561018a57600080fd5b73ffffffffffffffffffffffffffffffffffffffff82351691908101906040810160208201356401000000008111156101c257600080fd5b8201836020820111156101d457600080fd5b803590602001918460018302840111640100000000831117156101f657600080fd5b50909250905061054a565b34801561020d57600080fd5b50610216610630565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561024b57600080fd5b506100d26004803603602081101561026257600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661063f565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5460003681823780813683855af491503d8082833e8280156102c0578183f35b8183fd5b50505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316148061035d57507f7f5828d0000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b1561036a57506001610488565b7fffffffff00000000000000000000000000000000000000000000000000000000808316141561039c57506000610488565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54604080517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000085166004820152905173ffffffffffffffffffffffffffffffffffffffff8316916301ffc9a7916024808301926020929190829003018186803b15801561044c57600080fd5b505afa92505050801561047157506040513d602081101561046c57600080fd5b505160015b61047f576000915050610488565b91506104889050565b919050565b6104956106e9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461052e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4e4f545f415554484f52495a4544000000000000000000000000000000000000604482015290519081900360640190fd5b610547816040518060200160405280600081525061070e565b50565b6105526106e9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105eb57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4e4f545f415554484f52495a4544000000000000000000000000000000000000604482015290519081900360640190fd5b61062b8383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061070e92505050565b505050565b600061063a6106e9565b905090565b6106476106e9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106e057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4e4f545f415554484f52495a4544000000000000000000000000000000000000604482015290519081900360640190fd5b61054781610862565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80549083905560405173ffffffffffffffffffffffffffffffffffffffff80851691908316907f5570d70a002632a7b0b3c9304cc89efb62d8da9eca0dbd7752c83b737906829690600090a381511561062b5760008373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106107e957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107ac565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d8060008114610849576040519150601f19603f3d011682016040523d82523d6000602084013e61084e565b606091505b50509050806102c4573d806000803e806000fd5b600061086c6106e9565b9050817fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103558173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3505056fea26469706673582212209a21f7e39c677b08222e0075630be7fe375e2d2b64ed95bb001fbeae13a76b5a64736f6c63430007060033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; diff --git a/test/script/lib/NetworksJson.t.sol b/test/script/lib/NetworksJson.t.sol new file mode 100644 index 00000000..b3c46c2a --- /dev/null +++ b/test/script/lib/NetworksJson.t.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8.26; + +import {Test} from "forge-std/Test.sol"; + +import {NetworksJson} from "script/lib/NetworksJson.sol"; + +contract TestTransferOwnership is Test { + NetworksJson private networksJson; + + function setUp() public { + networksJson = new NetworksJson(); + } + + function test_reads_address_json_on_existing_chain_id() public { + uint256 chainId = 1; + assertEq( + networksJson.addressByChainId("GPv2Settlement", chainId), + address(0x9008D19f58AAbD9eD0D60971565AA8510560ab41) + ); + vm.chainId(chainId); + assertEq(networksJson.addressOf("GPv2Settlement"), address(0x9008D19f58AAbD9eD0D60971565AA8510560ab41)); + } + + function test_reverts_reads_address_json_on_unsupported_chain_id() public { + uint256 chainId = 31333333333333337; + vm.expectRevert(); + networksJson.addressByChainId("GPv2Settlement", chainId); + vm.chainId(chainId); + vm.expectRevert(); + networksJson.addressOf("GPv2Settlement"); + } +} diff --git a/test/tasks/transferOwnership.test.ts b/test/tasks/transferOwnership.test.ts deleted file mode 100644 index 6e0940e7..00000000 --- a/test/tasks/transferOwnership.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { expect } from "chai"; -import { Contract } from "ethers"; -import { getNamedAccounts, run } from "hardhat"; - -import { proxyInterface } from "../../src/ts"; -import { deployTestContracts } from "../e2e/fixture"; - -let authenticator: Contract; -let proxy: Contract; - -describe("Task: transfer ownership", () => { - beforeEach(async () => { - ({ authenticator } = await deployTestContracts()); - proxy = proxyInterface(authenticator); - }); - - describe("transfers ownership", () => { - it("transfers proxy ownership and resets the manager", async () => { - const newOwner = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; - await run("transfer-ownership", { - newOwner, - resetManager: true, - dryRun: false, - }); - - expect(await authenticator.manager()).to.equal(newOwner); - expect(await proxy.owner()).to.equal(newOwner); - }); - - it("only transfers proxy ownership", async () => { - const { manager } = await getNamedAccounts(); - const newOwner = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; - await run("transfer-ownership", { - newOwner, - resetManager: false, - dryRun: false, - }); - - expect(await authenticator.manager()).to.equal(manager); - expect(await proxy.owner()).to.equal(newOwner); - }); - - it("does nothing when executing dry run", async () => { - const { owner, manager } = await getNamedAccounts(); - await run("transfer-ownership", { - newOwner: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - resetManager: true, - dryRun: true, - }); - - expect(await authenticator.manager()).to.equal(manager); - expect(await proxy.owner()).to.equal(owner); - }); - }); -});