Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/twenty-cows-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@eth-optimism/contracts": patch
---

Ports OVM_ProxyEOA to use optimistic-solc instead of the standard solc compiler.
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// 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
* @dev The Proxy EOA contract uses a delegate call to execute the logic in an implementation contract.
* 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 {
Expand All @@ -22,48 +20,34 @@ contract OVM_ProxyEOA {
* Constants *
*************/

address constant DEFAULT_IMPLEMENTATION = 0x4200000000000000000000000000000000000003;
bytes32 constant IMPLEMENTATION_KEY = 0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for a separate PR, but harping again that this should really use this EIP here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we should use that EIP, note that this will also break our regenesis script
https://github.com/ethereum-optimism/scripts/blob/main/scripts/state-surgery.js
Will add an issue over there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue here: #550



/***************
* Constructor *
***************/

/**
* @param _implementation Address of the initial implementation contract.
*/
constructor(
address _implementation
)
{
_setImplementation(_implementation);
}


/*********************
* Fallback Function *
*********************/

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))
}
}
}

// 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 *
Expand All @@ -78,8 +62,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"
);

Expand All @@ -96,11 +80,17 @@ contract OVM_ProxyEOA {
address
)
{
return Lib_Bytes32Utils.toAddress(
Lib_SafeExecutionManagerWrapper.safeSLOAD(
IMPLEMENTATION_KEY
)
);
bytes32 addr32;
assembly {
addr32 := sload(IMPLEMENTATION_KEY)
}

address implementation = Lib_Bytes32Utils.toAddress(addr32);
if (implementation == address(0)) {
return DEFAULT_IMPLEMENTATION;
} else {
return implementation;
}
}


Expand All @@ -113,9 +103,9 @@ contract OVM_ProxyEOA {
)
internal
{
Lib_SafeExecutionManagerWrapper.safeSSTORE(
IMPLEMENTATION_KEY,
Lib_Bytes32Utils.fromAddress(_implementation)
);
bytes32 addr32 = Lib_Bytes32Utils.fromAddress(_implementation);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double checked the test files to make sure these are aligned correctly, looks good.

assembly {
sstore(IMPLEMENTATION_KEY, addr32)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ben-chain would love to have your eyes here

address proxyEOA = Lib_EthUtils.createContract(abi.encodePacked(
hex"600D380380600D6000396000f3",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed that this does indeed decompile to the opcodes in the comment above

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bytestring looks good; it would also be super obvious from integration tests if it weren't looking. Since we do not have constructors, then this code could obviously also be put in place without a constructor. I assume we are going this way for ease of implementation in geth, right @smartcontracts? if so, we could use the ovmSETCODE upgrade path in the future.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call on using SETCODE for this in the future

ovmEXTCODECOPY(
0x4200000000000000000000000000000000000009,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend to resolve these addresses

0,
ovmEXTCODESIZE(0x4200000000000000000000000000000000000009)
)
));

// Reset the address now that we're done deploying.
messageContext.ovmADDRESS = prevADDRESS;
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts/src/contract-deployment/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,8 @@ export const makeContractDeployConfig = async (
ERC1820Registry: {
factory: getContractFactory('ERC1820Registry'),
},
OVM_ProxyEOA: {
factory: getContractFactory('OVM_ProxyEOA', undefined, true),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these additional arguments? undefined is probably a provider, what is true? imo Python kwargs are handy for this kind of thing which can be replicated by making the second argument an object

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that we should make these additional arguments an object. Although probably deserves its own issue.

},
}
}
1 change: 1 addition & 0 deletions packages/contracts/src/contract-dumps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
'OVM_SequencerEntrypoint',
'Lib_AddressManager',
'OVM_ETH',
'OVM_ProxyEOA',
]

const deploymentResult = await deploy(config)
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/predeploys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export const predeploys = {
OVM_ETH: '0x4200000000000000000000000000000000000006',
OVM_L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007',
Lib_AddressManager: '0x4200000000000000000000000000000000000008',
OVM_ProxyEOA: '0x4200000000000000000000000000000000000009',
ERC1820Registry: '0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24',
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const test_ovmCREATEEOA: TestDefinition = {
},
expectedReturnStatus: true,
expectedReturnValue: fromHexString(
getContractDefinition('OVM_ProxyEOA').deployedBytecode
getContractDefinition('OVM_ProxyEOA', true).deployedBytecode
).length,
},
],
Expand Down
15 changes: 15 additions & 0 deletions packages/contracts/test/helpers/test-runner/test-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -47,13 +48,15 @@ export class ExecutionManagerTestRunner {
Helper_TestRunner: Contract
Factory__Helper_TestRunner_CREATE: ContractFactory
OVM_DeployerWhitelist: Contract
OVM_ProxyEOA: Contract
} = {
OVM_SafetyChecker: undefined,
OVM_StateManager: undefined,
OVM_ExecutionManager: undefined,
Helper_TestRunner: undefined,
Factory__Helper_TestRunner_CREATE: undefined,
OVM_DeployerWhitelist: undefined,
OVM_ProxyEOA: undefined,
}

// Default pre-state with contract deployer whitelist NOT initialized.
Expand All @@ -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']: {
Expand Down Expand Up @@ -217,6 +224,12 @@ export class ExecutionManagerTestRunner {

this.contracts.OVM_DeployerWhitelist = DeployerWhitelist

this.contracts.OVM_ProxyEOA = await getContractFactory(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we deploy the reference version of the OVM_ProxyEOA which is duplicated by the OVM_ExecutionManager.

'OVM_ProxyEOA',
AddressManager.signer,
true
).deploy()

this.contracts.OVM_ExecutionManager = await (
await smoddit('OVM_ExecutionManager')
).deploy(
Expand Down Expand Up @@ -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 {
Expand Down