From 5e0abf88a8a578c6b6e4b20d8ca0dfdf938648b5 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Wed, 21 Apr 2021 16:27:39 -0400 Subject: [PATCH 1/3] Port OVM_ProxyEOA to use ovm-solc --- .../OVM/accounts/OVM_ProxyEOA.sol | 50 +++++++------------ .../OVM/execution/OVM_ExecutionManager.sol | 23 +++++++-- .../src/contract-deployment/config.ts | 3 ++ packages/contracts/src/contract-dumps.ts | 1 + packages/contracts/src/predeploys.ts | 1 + .../OVM/accounts/OVM_ProxyEOA.spec.ts | 5 +- .../OVM_ExecutionManager/ovmCREATEEOA.spec.ts | 2 +- .../test/helpers/test-runner/test-runner.ts | 15 ++++++ 8 files changed, 62 insertions(+), 38 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol b/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol index 0f376f821fee0..434b80ed2c44e 100644 --- a/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol +++ b/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT +// @unsupported: evm pragma solidity >0.5.0 <0.8.0; /* Library Imports */ import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol"; -import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; -import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; -import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; /** * @title OVM_ProxyEOA @@ -13,7 +11,7 @@ import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_Sa * In combination with the logic implemented in the ECDSA Contract Account, this enables a form of upgradable * 'account abstraction' on layer 2. * - * Compiler used: solc + * Compiler used: optimistic-solc * Runtime target: OVM */ contract OVM_ProxyEOA { @@ -29,14 +27,8 @@ contract OVM_ProxyEOA { * Constructor * ***************/ - /** - * @param _implementation Address of the initial implementation contract. - */ - constructor( - address _implementation - ) - { - _setImplementation(_implementation); + constructor() { + _setImplementation(0x4200000000000000000000000000000000000003); } @@ -47,20 +39,16 @@ contract OVM_ProxyEOA { fallback() external { - (bool success, bytes memory returndata) = Lib_SafeExecutionManagerWrapper.safeDELEGATECALL( - gasleft(), - getImplementation(), - msg.data - ); + (bool success, bytes memory returndata) = getImplementation().delegatecall(msg.data); if (success) { assembly { return(add(returndata, 0x20), mload(returndata)) } } else { - Lib_SafeExecutionManagerWrapper.safeREVERT( - string(returndata) - ); + assembly { + revert(add(returndata, 0x20), mload(returndata)) + } } } @@ -78,8 +66,8 @@ contract OVM_ProxyEOA { ) external { - Lib_SafeExecutionManagerWrapper.safeREQUIRE( - Lib_SafeExecutionManagerWrapper.safeADDRESS() == Lib_SafeExecutionManagerWrapper.safeCALLER(), + require( + msg.sender == address(this), "EOAs can only upgrade their own EOA implementation" ); @@ -96,11 +84,11 @@ contract OVM_ProxyEOA { address ) { - return Lib_Bytes32Utils.toAddress( - Lib_SafeExecutionManagerWrapper.safeSLOAD( - IMPLEMENTATION_KEY - ) - ); + bytes32 addr32; + assembly { + addr32 := sload(IMPLEMENTATION_KEY) + } + return Lib_Bytes32Utils.toAddress(addr32); } @@ -113,9 +101,9 @@ contract OVM_ProxyEOA { ) internal { - Lib_SafeExecutionManagerWrapper.safeSSTORE( - IMPLEMENTATION_KEY, - Lib_Bytes32Utils.fromAddress(_implementation) - ); + bytes32 addr32 = Lib_Bytes32Utils.fromAddress(_implementation); + assembly { + sstore(IMPLEMENTATION_KEY, addr32) + } } } diff --git a/packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index dba6fa1db2ff0..0c0437a1ee4e9 100644 --- a/packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -16,7 +16,6 @@ import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol" /* Contract Imports */ import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol"; -import { OVM_ProxyEOA } from "../accounts/OVM_ProxyEOA.sol"; import { OVM_DeployerWhitelist } from "../predeploys/OVM_DeployerWhitelist.sol"; /** @@ -534,9 +533,25 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { address prevADDRESS = messageContext.ovmADDRESS; messageContext.ovmADDRESS = eoa; - // Now actually create the account and get its bytecode. We're not worried about reverts - // (other than out of gas, which we can't capture anyway) because this contract is trusted. - OVM_ProxyEOA proxyEOA = new OVM_ProxyEOA(0x4200000000000000000000000000000000000003); + // Creates a duplicate of the OVM_ProxyEOA located at 0x42....09. Uses the following + // "magic" prefix to deploy an exact copy of the code: + // PUSH1 0x0D # size of this prefix in bytes + // CODESIZE + // SUB # subtract prefix size from codesize + // DUP1 + // PUSH1 0x0D + // PUSH1 0x00 + // CODECOPY # copy everything after prefix into memory at pos 0 + // PUSH1 0x00 + // RETURN # return the copied code + address proxyEOA = Lib_EthUtils.createContract(abi.encodePacked( + hex"600D380380600D6000396000f3", + ovmEXTCODECOPY( + 0x4200000000000000000000000000000000000009, + 0, + ovmEXTCODESIZE(0x4200000000000000000000000000000000000009) + ) + )); // Reset the address now that we're done deploying. messageContext.ovmADDRESS = prevADDRESS; diff --git a/packages/contracts/src/contract-deployment/config.ts b/packages/contracts/src/contract-deployment/config.ts index db5c10b6b4d34..d62dec7f69458 100644 --- a/packages/contracts/src/contract-deployment/config.ts +++ b/packages/contracts/src/contract-deployment/config.ts @@ -254,5 +254,8 @@ export const makeContractDeployConfig = async ( ERC1820Registry: { factory: getContractFactory('ERC1820Registry'), }, + OVM_ProxyEOA: { + factory: getContractFactory('OVM_ProxyEOA', undefined, true), + }, } } diff --git a/packages/contracts/src/contract-dumps.ts b/packages/contracts/src/contract-dumps.ts index baffa384cffd9..6ee260f03967a 100644 --- a/packages/contracts/src/contract-dumps.ts +++ b/packages/contracts/src/contract-dumps.ts @@ -167,6 +167,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise => { 'OVM_SequencerEntrypoint', 'Lib_AddressManager', 'OVM_ETH', + 'OVM_ProxyEOA', ] const deploymentResult = await deploy(config) diff --git a/packages/contracts/src/predeploys.ts b/packages/contracts/src/predeploys.ts index a442cf02c54fe..a3de85dc58fb4 100644 --- a/packages/contracts/src/predeploys.ts +++ b/packages/contracts/src/predeploys.ts @@ -8,5 +8,6 @@ export const predeploys = { OVM_ETH: '0x4200000000000000000000000000000000000006', OVM_L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007', Lib_AddressManager: '0x4200000000000000000000000000000000000008', + OVM_ProxyEOA: '0x4200000000000000000000000000000000000009', ERC1820Registry: '0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24', } diff --git a/packages/contracts/test/contracts/OVM/accounts/OVM_ProxyEOA.spec.ts b/packages/contracts/test/contracts/OVM/accounts/OVM_ProxyEOA.spec.ts index ce576e23f7695..588068d0ae8ce 100644 --- a/packages/contracts/test/contracts/OVM/accounts/OVM_ProxyEOA.spec.ts +++ b/packages/contracts/test/contracts/OVM/accounts/OVM_ProxyEOA.spec.ts @@ -8,6 +8,7 @@ import { remove0x } from '@eth-optimism/core-utils' /* Internal Imports */ import { decodeSolidityError } from '../../../helpers' +import { getContractFactory } from '../../../../src' const callPredeploy = async ( Helper_PredeployCaller: Contract, @@ -60,12 +61,12 @@ describe('OVM_ProxyEOA', () => { let OVM_ProxyEOAFactory: ContractFactory before(async () => { - OVM_ProxyEOAFactory = await ethers.getContractFactory('OVM_ProxyEOA') + OVM_ProxyEOAFactory = getContractFactory('OVM_ProxyEOA', wallet, true) }) let OVM_ProxyEOA: Contract beforeEach(async () => { - OVM_ProxyEOA = await OVM_ProxyEOAFactory.deploy(eoaDefaultAddr) + OVM_ProxyEOA = await OVM_ProxyEOAFactory.deploy() Mock__OVM_ExecutionManager.smocked.ovmADDRESS.will.return.with( OVM_ProxyEOA.address diff --git a/packages/contracts/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATEEOA.spec.ts b/packages/contracts/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATEEOA.spec.ts index 665479e61867b..fac05477a0718 100644 --- a/packages/contracts/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATEEOA.spec.ts +++ b/packages/contracts/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATEEOA.spec.ts @@ -73,7 +73,7 @@ const test_ovmCREATEEOA: TestDefinition = { }, expectedReturnStatus: true, expectedReturnValue: fromHexString( - getContractDefinition('OVM_ProxyEOA').deployedBytecode + getContractDefinition('OVM_ProxyEOA', true).deployedBytecode ).length, }, ], diff --git a/packages/contracts/test/helpers/test-runner/test-runner.ts b/packages/contracts/test/helpers/test-runner/test-runner.ts index 91eb6f7e61ba0..de919626faafe 100644 --- a/packages/contracts/test/helpers/test-runner/test-runner.ts +++ b/packages/contracts/test/helpers/test-runner/test-runner.ts @@ -37,6 +37,7 @@ import { } from '../constants' import { getStorageXOR } from '../' import { UNSAFE_BYTECODE } from '../dummy' +import { getContractFactory } from '../../../src' export class ExecutionManagerTestRunner { private snapshot: string @@ -47,6 +48,7 @@ export class ExecutionManagerTestRunner { Helper_TestRunner: Contract Factory__Helper_TestRunner_CREATE: ContractFactory OVM_DeployerWhitelist: Contract + OVM_ProxyEOA: Contract } = { OVM_SafetyChecker: undefined, OVM_StateManager: undefined, @@ -54,6 +56,7 @@ export class ExecutionManagerTestRunner { Helper_TestRunner: undefined, Factory__Helper_TestRunner_CREATE: undefined, OVM_DeployerWhitelist: undefined, + OVM_ProxyEOA: undefined, } // Default pre-state with contract deployer whitelist NOT initialized. @@ -65,6 +68,10 @@ export class ExecutionManagerTestRunner { codeHash: NON_NULL_BYTES32, ethAddress: '$OVM_DEPLOYER_WHITELIST', }, + ['0x4200000000000000000000000000000000000009']: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_PROXY_EOA', + }, }, contractStorage: { ['0x4200000000000000000000000000000000000002']: { @@ -217,6 +224,12 @@ export class ExecutionManagerTestRunner { this.contracts.OVM_DeployerWhitelist = DeployerWhitelist + this.contracts.OVM_ProxyEOA = await getContractFactory( + 'OVM_ProxyEOA', + AddressManager.signer, + true + ).deploy() + this.contracts.OVM_ExecutionManager = await ( await smoddit('OVM_ExecutionManager') ).deploy( @@ -266,6 +279,8 @@ export class ExecutionManagerTestRunner { return this.contracts.Helper_TestRunner.address } else if (kv === '$OVM_DEPLOYER_WHITELIST') { return this.contracts.OVM_DeployerWhitelist.address + } else if (kv === '$OVM_PROXY_EOA') { + return this.contracts.OVM_ProxyEOA.address } else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) { return ExecutionManagerTestRunner.getDummyAddress(kv) } else { From 09029cd40e6694deb966e23020504fde70794b81 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Wed, 21 Apr 2021 17:11:34 -0400 Subject: [PATCH 2/3] Remove all constructor logic from ProxyEOA --- .../OVM/accounts/OVM_ProxyEOA.sol | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol b/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol index 434b80ed2c44e..a74e3084693ea 100644 --- a/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol +++ b/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ProxyEOA.sol @@ -20,18 +20,10 @@ contract OVM_ProxyEOA { * Constants * *************/ + address constant DEFAULT_IMPLEMENTATION = 0x4200000000000000000000000000000000000003; bytes32 constant IMPLEMENTATION_KEY = 0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead; - /*************** - * Constructor * - ***************/ - - constructor() { - _setImplementation(0x4200000000000000000000000000000000000003); - } - - /********************* * Fallback Function * *********************/ @@ -52,6 +44,10 @@ contract OVM_ProxyEOA { } } + // WARNING: We use the deployed bytecode of this contract as a template to create ProxyEOA + // contracts. As a result, we must *not* perform any constructor logic. Use initialization + // functions if necessary. + /******************** * Public Functions * @@ -88,7 +84,13 @@ contract OVM_ProxyEOA { assembly { addr32 := sload(IMPLEMENTATION_KEY) } - return Lib_Bytes32Utils.toAddress(addr32); + + address implementation = Lib_Bytes32Utils.toAddress(addr32); + if (implementation == address(0)) { + return DEFAULT_IMPLEMENTATION; + } else { + return implementation; + } } From 2e405df1bcee32af362cc175f95487d4f4f84503 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Wed, 21 Apr 2021 19:47:58 -0400 Subject: [PATCH 3/3] chore[contracts]: add changeset --- .changeset/twenty-cows-walk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twenty-cows-walk.md diff --git a/.changeset/twenty-cows-walk.md b/.changeset/twenty-cows-walk.md new file mode 100644 index 0000000000000..12126ede2b23e --- /dev/null +++ b/.changeset/twenty-cows-walk.md @@ -0,0 +1,5 @@ +--- +"@eth-optimism/contracts": patch +--- + +Ports OVM_ProxyEOA to use optimistic-solc instead of the standard solc compiler.