contract KMCDDelegator is KMCDStorage, KDelegatorInterface {
    /**
    * @notice Construct a new Kine MCD market
    * @param controller_ The address of the Controller
    * @param name_ Name of this MCD token
    * @param symbol_ Symbol of this MCD token
    * @param decimals_ Decimal precision of this MCD token
    * @param admin_ Address of the administrator of this token
    * @param minter_ Address of KUSDMinter
    * @param implementation_ The address of the implementation the contract delegates to
    * @param becomeImplementationData The encoded args for becomeImplementation
    */
    constructor(KineControllerInterface controller_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address payable admin_,
        address minter_,
        address implementation_,
        bytes memory becomeImplementationData) public {
        // Creator of the contract is admin during initialization
        admin = msg.sender;
        initialized = false;

        // First delegate gets to initialize the delegator (i.e. storage contract)
        delegateTo(implementation_, abi.encodeWithSignature("initialize(address,string,string,uint8,address)",
            controller_, name_, symbol_, decimals_, minter_));

        // New implementations always get set via the settor (post-initialize)
        _setImplementation(implementation_, false, becomeImplementationData);

        // Set the proper admin now that initialization is done
        admin = admin_;
    }

    /**
     * @notice Internal method to delegate execution to another contract
     * @dev It returns to the external caller whatever the implementation returns or forwards reverts
     * @param callee The contract to delegatecall
     * @param data The raw data to delegatecall
     * @return The returned bytes from the delegatecall
     */
    function delegateTo(address callee, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returnData) = callee.delegatecall(data);
        assembly {
            if eq(success, 0) {
                revert(add(returnData, 0x20), returndatasize)
            }
        }
        return returnData;
    }

    /**
     * @notice Delegates execution to the implementation contract
     * @dev It returns to the external caller whatever the implementation returns or forwards reverts
     * @param data The raw data to delegatecall
     * @return The returned bytes from the delegatecall
     */
    function delegateToImplementation(bytes memory data) public returns (bytes memory) {
        return delegateTo(implementation, data);
    }

    /**
     * @notice Called by the admin to update the implementation of the delegator
     * @param implementation_ The address of the new implementation for delegation
     * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
     * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
     */
    function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public {
        require(msg.sender == admin, "KMCDDelegator::_setImplementation: Caller must be admin");

        if (allowResign) {
            delegateToImplementation(abi.encodeWithSignature("_resignImplementation()"));
        }

        address oldImplementation = implementation;
        implementation = implementation_;

        delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData));

        emit NewImplementation(oldImplementation, implementation);
    }

    /**
     * @notice Delegates execution to an implementation contract
     * @dev It returns to the external caller whatever the implementation returns or forwards reverts
     */
    function() external payable {
        require(msg.value == 0, "KMCDDelegator:fallback: cannot send value to fallback");

        // delegate all other functions to current implementation
        (bool success,) = implementation.delegatecall(msg.data);

        assembly {
            let free_mem_ptr := mload(0x40)
            returndatacopy(free_mem_ptr, 0, returndatasize)

            switch success
            case 0 {revert(free_mem_ptr, returndatasize)}
            default {return (free_mem_ptr, returndatasize)}
        }
    }
}
/**
 * @title Kine's KMCDDelegator Contract
 * @notice KMCD which delegate to an implementation
 * @author Kine
 */
function() external payable {
    require(msg.value == 0, "KMCDDelegator:fallback: cannot send value to fallback");

    // delegate all other functions to current implementation
    (bool success,) = implementation.delegatecall(msg.data);

    assembly {
        let free_mem_ptr := mload(0x40)
        returndatacopy(free_mem_ptr, 0, returndatasize)

        switch success
        case 0 {revert(free_mem_ptr, returndatasize)}
        default {return (free_mem_ptr, returndatasize)}
    }
}

/**
 * @notice Delegates execution to an implementation contract
 * @dev It returns to the external caller whatever the implementation returns or forwards reverts
 */