Blockchain / Global / Address

Contract

0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67

Balance
0 SOMI
Transactions sent
97,738
Type
Contract · 3,054 bytes of code

SafeProxyFactory Verified

Compiler
solc 0.7.6+commit.7338295f
Language
Solidity
Optimization
No
EVM version
—
License
—
Verified
2026-10-12

contracts/proxies/SafeProxyFactory.sol

and 2 more files

Solidity
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

import "./SafeProxy.sol";
import "./IProxyCreationCallback.sol";

/**
 * @title Proxy Factory - Allows to create a new proxy contract and execute a message call to the new proxy within one transaction.
 * @author Stefan George - @Georgi87
 */
contract SafeProxyFactory {
    event ProxyCreation(SafeProxy indexed proxy, address singleton);

    /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
    function proxyCreationCode() public pure returns (bytes memory) {
        return type(SafeProxy).creationCode;
    }

    /**
     * @notice Internal method to create a new proxy contract using CREATE2. Optionally executes an initializer call to a new proxy.
     * @param _singleton Address of singleton contract. Must be deployed at the time of execution.
     * @param initializer (Optional) Payload for a message call to be sent to a new proxy contract.
     * @param salt Create2 salt to use for calculating the address of the new proxy contract.
     * @return proxy Address of the new proxy contract.
     */
    function deployProxy(address _singleton, bytes memory initializer, bytes32 salt) internal returns (SafeProxy proxy) {
        require(isContract(_singleton), "Singleton contract not deployed");

        bytes memory deploymentData = abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
        // solhint-disable-next-line no-inline-assembly
        assembly {
            proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
        }
        require(address(proxy) != address(0), "Create2 call failed");

        if (initializer.length > 0) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                    revert(0, 0)
                }
            }
        }
    }

    /**
     * @notice Deploys a new proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
     * @param _singleton Address of singleton contract. Must be deployed at the time of execution.
     * @param initializer Payload for a message call to be sent to a new proxy contract.
     * @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
     */
    function createProxyWithNonce(address _singleton, bytes memory initializer, uint256 saltNonce) public returns (SafeProxy proxy) {
        // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
        bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
        proxy = deployProxy(_singleton, initializer, salt);
        emit ProxyCreation(proxy, _singleton);
    }

    /**
     * @notice Deploys a new chain-specific proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
     * @dev Allows to create a new proxy contract that should exist only on 1 network (e.g. specific governance or admin accounts)
     *      by including the chain id in the create2 salt. Such proxies cannot be created on other networks by replaying the transaction.
     * @param _singleton Address of singleton contract. Must be deployed at the time of execution.
     * @param initializer Payload for a message call to be sent to a new proxy contract.
     * @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
     */
    function createChainSpecificProxyWithNonce(
        address _singleton,
        bytes memory initializer,
        uint256 saltNonce
    ) public returns (SafeProxy proxy) {
        // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
        bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce, getChainId()));
        proxy = deployProxy(_singleton, initializer, salt);
        emit ProxyCreation(proxy, _singleton);
    }

    /**
     * @notice Deploy a new proxy with `_singleton` singleton and `saltNonce` salt.
     *         Optionally executes an initializer call to a new proxy and calls a specified callback address `callback`.
     * @param _singleton Address of singleton contract. Must be deployed at the time of execution.
     * @param initializer Payload for a message call to be sent to a new proxy contract.
     * @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
     * @param callback Callback that will be invoked after the new proxy contract has been successfully deployed and initialized.
     */
    function createProxyWithCallback(
        address _singleton,
        bytes memory initializer,
        uint256 saltNonce,
        IProxyCreationCallback callback
    ) public returns (SafeProxy proxy) {
        uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
        proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
        if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
    }

    /**
     * @notice Returns true if `account` is a contract.
     * @dev This function will return false if invoked during the constructor of a contract,
     *      as the code is not actually created until after the constructor finishes.
     * @param account The address being queried
     * @return True if `account` is a contract
     */
    function isContract(address account) internal view returns (bool) {
        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @notice Returns the ID of the chain the contract is currently deployed on.
     * @return The ID of the current chain as a uint256.
     */
    function getChainId() public view returns (uint256) {
        uint256 id;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            id := chainid()
        }
        return id;
    }
}

Other files

  • contracts/proxies/IProxyCreationCallback.sol
    // SPDX-License-Identifier: LGPL-3.0-only
    pragma solidity >=0.7.0 <0.9.0;
    import "./SafeProxy.sol";
    
    /**
     * @title IProxyCreationCallback
     * @dev An interface for a contract that implements a callback function to be executed after the creation of a proxy instance.
     */
    interface IProxyCreationCallback {
        /**
         * @dev Function to be called after the creation of a SafeProxy instance.
         * @param proxy The newly created SafeProxy instance.
         * @param _singleton The address of the singleton contract used to create the proxy.
         * @param initializer The initializer function call data.
         * @param saltNonce The nonce used to generate the salt for the proxy deployment.
         */
        function proxyCreated(SafeProxy proxy, address _singleton, bytes calldata initializer, uint256 saltNonce) external;
    }
    
  • contracts/proxies/SafeProxy.sol
    // SPDX-License-Identifier: LGPL-3.0-only
    pragma solidity >=0.7.0 <0.9.0;
    
    /**
     * @title IProxy - Helper interface to access the singleton address of the Proxy on-chain.
     * @author Richard Meissner - @rmeissner
     */
    interface IProxy {
        function masterCopy() external view returns (address);
    }
    
    /**
     * @title SafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
     * @author Stefan George - <stefan@gnosis.io>
     * @author Richard Meissner - <richard@gnosis.io>
     */
    contract SafeProxy {
        // Singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
        // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
        address internal singleton;
    
        /**
         * @notice Constructor function sets address of singleton contract.
         * @param _singleton Singleton address.
         */
        constructor(address _singleton) {
            require(_singleton != address(0), "Invalid singleton address provided");
            singleton = _singleton;
        }
    
        /// @dev Fallback function forwards all transactions and returns all received return data.
        fallback() external payable {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                    mstore(0, _singleton)
                    return(0, 0x20)
                }
                calldatacopy(0, 0, calldatasize())
                let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                returndatacopy(0, 0, returndatasize())
                if eq(success, 0) {
                    revert(0, returndatasize())
                }
                return(0, returndatasize())
            }
        }
    }
    
ABI · 6 entries
json
[
  {
    "name": "ProxyCreation",
    "type": "event",
    "inputs": [
      {
        "name": "proxy",
        "type": "address",
        "indexed": true,
        "internalType": "contract SafeProxy"
      },
      {
        "name": "singleton",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "createChainSpecificProxyWithNonce",
    "type": "function",
    "inputs": [
      {
        "name": "_singleton",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "initializer",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "saltNonce",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "proxy",
        "type": "address",
        "internalType": "contract SafeProxy"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "createProxyWithCallback",
    "type": "function",
    "inputs": [
      {
        "name": "_singleton",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "initializer",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "saltNonce",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "callback",
        "type": "address",
        "internalType": "contract IProxyCreationCallback"
      }
    ],
    "outputs": [
      {
        "name": "proxy",
        "type": "address",
        "internalType": "contract SafeProxy"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "createProxyWithNonce",
    "type": "function",
    "inputs": [
      {
        "name": "_singleton",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "initializer",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "saltNonce",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "proxy",
        "type": "address",
        "internalType": "contract SafeProxy"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "getChainId",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "proxyCreationCode",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "stateMutability": "pure"
  }
]