// Contract: InstaIndex
// Target function: function isClone(uint version, address query) external view returns (bool result)
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

/**
 * @title InstaIndex
 * @dev Main Contract For DeFi Smart Accounts. This is also a factory contract, Which deploys new Smart Account.
 * Also Registry for DeFi Smart Accounts.
 */

interface AccountInterface {
    function version() external view returns (uint);
    function enable(address authority) external;
    function cast(address[] calldata _targets, bytes[] calldata _datas, address _origin) external payable returns (bytes32[] memory responses);
}

interface ListInterface {
    function init(address _account) external;
}

contract AddressIndex {

    event LogNewMaster(address indexed master);
    event LogUpdateMaster(address indexed master);
    event LogNewCheck(uint indexed accountVersion, address indexed check);
    event LogNewAccount(address indexed _newAccount, address indexed _connectors, address indexed _check);

    // New Master Address.
    address private newMaster;
    // Master Address.
    address public master;
    // List Registry Address.
    address public list;

    // Connectors Modules(Account Module Version => Connectors Registry Module Address).
    mapping (uint => address) public connectors;
    // Check Modules(Account Module Version => Check Module Address).
    mapping (uint => address) public check;
    // Account Modules(Account Module Version => Account Module Address).
    mapping (uint => address) public account;
    // Version Count of Account Modules.
    uint public versionCount;

    /**
    * @dev Throws if the sender not is Master Address.
    */
    modifier isMaster() {
        require(msg.sender == master, "not-master");
        _;
    }

    /**
     * @dev Change the Master Address.
     * @param _newMaster New Master Address.
     */
    function changeMaster(address _newMaster) external isMaster {
        require(_newMaster != master, "already-a-master");
        require(_newMaster != address(0), "not-valid-address");
        require(newMaster != _newMaster, "already-a-new-master");
        newMaster = _newMaster;
        emit LogNewMaster(_newMaster);
    }

    function updateMaster() external {
        require(newMaster != address(0), "not-valid-address");
        require(msg.sender == newMaster, "not-master");
        master = newMaster;
        newMaster = address(0);
        emit LogUpdateMaster(master);
    }

    /**
     * @dev Change the Check Address of a specific Account Module version.
     * @param accountVersion Account Module version.
     * @param _newCheck The New Check Address.
     */
    function changeCheck(uint accountVersion, address _newCheck) external isMaster {
        require(_newCheck != check[accountVersion], "already-a-check");
        check[accountVersion] = _newCheck;
        emit LogNewCheck(accountVersion, _newCheck);
    }

    /**
     * @dev Add New Account Module.
     * @param _newAccount The New Account Module Address.
     * @param _connectors Connectors Registry Module Address.
     * @param _check Check Module Address.
     */
    function addNewAccount(address _newAccount, address _connectors, address _check) external isMaster {
        require(_newAccount != address(0), "not-valid-address");
        versionCount++;
        require(AccountInterface(_newAccount).version() == versionCount, "not-valid-version");
        account[versionCount] = _newAccount;
        if (_connectors != address(0)) connectors[versionCount] = _connectors;
        if (_check != address(0)) check[versionCount] = _check;
        emit LogNewAccount(_newAccount, _connectors, _check);
    }

}

contract CloneFactory is AddressIndex {
    /**
     * @dev Clone a new Account Module.
     * @param version Account Module version to clone.
     */
    function createClone(uint version) internal returns (address result) {
        bytes20 targetBytes = bytes20(account[version]);
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let clone := mload(0x40)
            mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone, 0x14), targetBytes)
            mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            result := create(0, clone, 0x37)
        }
    }