// File: KnightSafe/contracts/proxies/KnightSafeProxyFactory.sol



// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "./KnightSafeProxy.sol";

/**
 * @title KnightSafeProxyFactory
 * @notice A factory contract that allow user to create new KnightSafeProxy contract
 */
contract KnightSafeProxyFactory {
    event ProxyCreation(KnightSafeProxy proxy, address indexed implementation);

    /**
     * @notice Create a new KnightSafeProxy contract
     * @param implementation Address of implementation contract
     * @param data Payload for a message call to be sent to a new proxy contract
     */
    function createProxy(address implementation, bytes memory data) public returns (KnightSafeProxy proxy) {
        proxy = new KnightSafeProxy(implementation);
        if (data.length > 0)
            // solhint-disable-next-line no-inline-assembly
            assembly {
                if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                    revert(0, 0)
                }
            }
        emit ProxyCreation(proxy, implementation);
    }
}

// File: KnightSafe/contracts/proxies/KnightSafeProxy.sol



// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

/**
 * @title KnightSafeProxy
 * @notice Using proxy design pattern to save gas consumption for distribution  
 */
contract KnightSafeProxy {
    
    // implementation address needs to be the first declared variable. 
    address internal implementation;

    /**
     * @notice Constructor function sets address of implementation contract.
     * @param _implementation Implementation address
     */
    constructor(address _implementation) {
        require(_implementation != address(0), "Implementation address should not be null.");
        implementation = _implementation;
    }

    /**
     * @notice Fallback function forwards all transactions and returns all received return data.
     */
    fallback() external payable {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let _implementation := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)

            // copy msg.data.
            calldatacopy(0, 0, calldatasize())

            // call the implementation.
            let result  := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)

            // copy the returned data.
            returndatacopy(0, 0, returndatasize())
            
            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}