OpsProxyFactoryZkSync.sol 4.7 KB
Newer Older
vicotor's avatar
vicotor committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;

import {
    Initializable
} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {EIP173OpsProxy} from "../vendor/proxy/EIP173/EIP173OpsProxy.sol";
import {Proxied} from "../vendor/proxy/EIP173/Proxied.sol";
import {IOpsProxyFactory} from "../interfaces/IOpsProxyFactory.sol";

// solhint-disable max-states-count
contract OpsProxyFactoryZkSync is Initializable, Proxied, IOpsProxyFactory {
    address public immutable ops;
    bytes32 public immutable eip173OpsProxyByteCodeHash;

    address public implementation;
    mapping(address => bool) public override whitelistedImplementations;

    ///@dev track proxy of user
    mapping(address => address) internal _proxyOf;

    ///@dev track owner of proxy
    mapping(address => address) internal _ownerOf;

    modifier onlyOneProxy(address _account) {
        require(_proxyOf[_account] == address(0), "OpsProxyFactory: One proxy");
        _;
    }

    modifier notProxy(address _account) {
        require(_ownerOf[_account] == address(0), "OpsProxyFactory: No proxy");
        _;
    }

    constructor(address _ops, bytes32 _eip173OpsProxyByteCodeHash) {
        ops = _ops;
        eip173OpsProxyByteCodeHash = _eip173OpsProxyByteCodeHash;
    }

    function initialize(address _implementation) external initializer {
        implementation = _implementation;
        whitelistedImplementations[_implementation] = true;
    }

    ///@inheritdoc IOpsProxyFactory
    function deploy() external override returns (address payable proxy) {
        proxy = deployFor(msg.sender);
    }

    function setImplementation(address _newImplementation)
        external
        onlyProxyAdmin
    {
        address oldImplementation = implementation;
        require(
            oldImplementation != _newImplementation &&
                whitelistedImplementations[_newImplementation],
            "OpsProxyFactory: Invalid implementation"
        );

        implementation = _newImplementation;

        emit SetImplementation(oldImplementation, _newImplementation);
    }

    function updateWhitelistedImplementations(
        address _implementation,
        bool _whitelist
    ) external onlyProxyAdmin {
        whitelistedImplementations[_implementation] = _whitelist;

        emit UpdateWhitelistedImplementation(_implementation, _whitelist);
    }

    ///@inheritdoc IOpsProxyFactory
    function getProxyOf(address _account)
        external
        view
        override
        returns (address, bool)
    {
        address proxyAddress = _proxyOf[_account];

        if (proxyAddress != address(0)) return (proxyAddress, true);

        proxyAddress = determineProxyAddress(_account);
        return (proxyAddress, false);
    }

    ///@inheritdoc IOpsProxyFactory
    function ownerOf(address _proxy) external view override returns (address) {
        return _ownerOf[_proxy];
    }

    ///@inheritdoc IOpsProxyFactory
    function deployFor(address owner)
        public
        override
        onlyOneProxy(owner)
        notProxy(owner)
        returns (address payable proxy)
    {
        proxy = _deploy(bytes32(0), _getBytecode(owner));

        _proxyOf[owner] = proxy;
        _ownerOf[proxy] = owner;

        emit DeployProxy(msg.sender, owner, address(proxy));
    }

    ///@inheritdoc IOpsProxyFactory
    function determineProxyAddress(address _account)
        public
        view
        override
        returns (address)
    {
        bytes32 constructorInputHash = keccak256(
            _encodeConstructorInput(_account)
        );

        bytes32 create2Hash = keccak256(
            bytes.concat(
                keccak256("zksyncCreate2"),
                bytes32(uint256(uint160(address(this)))),
                bytes32(0),
                eip173OpsProxyByteCodeHash,
                constructorInputHash
            )
        );

        return address(uint160(uint256(create2Hash)));
    }

    function _deploy(bytes32 _salt, bytes memory _bytecode)
        internal
        returns (address payable proxy)
    {
        assembly {
            let endowment := 0
            let bytecodeStart := add(_bytecode, 0x20)
            let bytecodeLength := mload(_bytecode)
            proxy := create2(endowment, bytecodeStart, bytecodeLength, _salt)
        }
    }

    function _getBytecode(address _owner) internal view returns (bytes memory) {
        return
            abi.encodePacked(
                type(EIP173OpsProxy).creationCode,
                _encodeConstructorInput(_owner)
            );
    }

    function _encodeConstructorInput(address _account)
        internal
        view
        returns (bytes memory)
    {
        return abi.encode(address(this), implementation, _account, bytes(""));
    }
}