// Contract: Kaiba_IVC
// Target function: function save_status_ivc() public safe onlyTeam
 0; i < plugins_methods_id[plugin].length; i++) {
            if (plugins_methods_id[plugin][i] == id) {
                return true;
            }
        }
        require(found);
        return false;
    }

    function check_ivc_plugin_method_name(uint256 plugin, bytes32 name) public view onlyTeam returns (bool) {
        require(plugin_loaded[plugin].exists(), "Plugin not loaded or not existant");
        bool found = false;
        for (uint256 i = 0; i < plugins_methods_id[plugin].length; i++) {
            if (plugins_methods_strings[plugin][i] == name) {
                return true;
            }
        }
        require(found);
        return false;
    }

    /// @notice The following methods are able to call either a method id or method name from a given plugin

    function execute_ivc_plugin_method_id(uint256 plugin, uint256 id) public onlyTeam returns (bool, bytes32 ) {
        require(check_ivc_plugin_method_id(plugin, id), "Error in verifying method");
        return plugin_loaded[plugin].svt_method_call_id(id);
    }

    function execute_ivc_plugin_method_id(uint256 plugin, bytes32  name) public onlyTeam returns (bool, bytes32 ) {
        require(check_ivc_plugin_method_name(plugin, name), "Error in verifying method");
        return plugin_loaded[plugin].svt_method_call_name(name);
    }


    /////////////////////////////////////////////////////// Utility functions

    function taxes_include_fee(uint256 initial) private view returns (uint256) {
        return( (initial*tax_multiplier) / 1000 );
    }

    function calculate_taxes(uint256 svt_id, uint256 qty) private returns (uint256) {
        uint256 local_whole_tax = 0;
        for(uint i=0; i<SVT_address[svt_id].fees.length; i++) {
            SVT_address[svt_id].fees_storage[i] += (qty * SVT_address[svt_id].fees[i])/100;
            local_whole_tax += (qty * SVT_address[svt_id].fees[i])/100;
        }
        return local_whole_tax;
    }

    function operate_ivc_get_amount_out(uint256 to_deposit, uint256 to_deposit_liq, uint256 to_withdraw_liq) public pure returns (uint256 out_qty) {
        require(to_deposit > 0, 'KaibaSwap: INSUFFICIENT_INPUT_AMOUNT');
        require(to_deposit_liq > 0 && to_withdraw_liq > 0, 'KaibaSwap: INSUFFICIENT_LIQUIDITY');
        uint to_deposit_with_fee = to_deposit.mul(997);
        uint numerator = to_deposit_with_fee.mul(to_withdraw_liq);
        uint denominator = to_deposit_liq.mul(1000).add(to_deposit_with_fee);
        out_qty = numerator / denominator;
        return out_qty;
    }

    /// @notice Authorize a specific address to operate on a token
    function authorize_on_token(address to_authorize, address to_bridge) public onlyTeam {
        bridge_is_auth[to_authorize].is_auth[to_bridge] = true;
    }


    /// @notice This function allows issuance of native coins
    function issue_native_svt(
        bytes32  name,
        bytes32  ticker,
        uint256 max_supply,
        uint256[] calldata fees) 
    public Unlocked(1553) safe {
        uint256 thisAddress = svt_last_id+1;
        SVT_address[thisAddress].deployed = true;
        SVT_address[thisAddress].circulatingSupply = max_supply;
        SVT_address[thisAddress].totalSupply = max_supply;
        SVT_address[thisAddress].fees = fees;
        SVT_address[thisAddress].name = name;
        SVT_address[thisAddress].ticker = ticker;
        SVT_address[thisAddress].isBridged = true;
        SVT_address[thisAddress].original_token = ZERO;
        SVT_address[thisAddress].balance[msg.sender] = taxes_include_fee(max_supply);
        SVT_address[thisAddress].SVT_Liquidity_storage = svt_liquidity_last_id + 1;
        svt_liquidity_last_id += 1;
        taxes_native_total[thisAddress] += max_supply - taxes_include_fee(max_supply);
    }

    function native_add_liq(uint256 svt_id, uint256 qty) payable public safe { 
        require(msg.value > 10000000000000000, "Too low");
        require(SVT_address[svt_id].deployed, "SVT does not exists");
        uint256 thisLiquidity = SVT_address[svt_id].SVT_Liquidity_storage;
        require(!SVT_Liquidity_index[thisLiquidity].active, "Pair is already alive");
        SVT_Liquidity_index[thisLiquidity].active = true;
        SVT_Liquidity_index[thisLiquidity].deployed = true;
        SVT_Liquidity_index[thisLiquidity].token_1 = ERC20(WETH);
        SVT_Liquidity_index[thisLiquidity].token_2 = ERC20(ZERO);
        SVT_Liquidity_index[thisLiquidity].token_1_qty += msg.value;
        SVT_Liquidity_index[thisLiquidity].token_2_qty += qty;
        SVT_Liquidity_index[thisLiquidity].SVT_token_id = svt_id;
        SVT_Liquidity_index[thisLiquidity].liq_mode = 1;
    }

    /// @notice this returns the url with synthetic balances

    function get_synthetic_svt_url(uint256 svt_id) public returns (string memory, address, address) {
        require(SVT_address[svt_id].deployed=true, "No token");
        require(SVT_address[svt_id].is_synthetic=true, "No synthesis");
        return(SVT_address[svt_id].balance_url, SVT_address[svt_id].pair_address, SVT_address[svt_id].original_token);
    }

    /// @notice struct and methods to assign liquidity

    struct liquidity_owner_stats {
        mapping(bytes32 => bool) owned;
        mapping(bytes32 => uint256) qty_1;
        mapping(bytes32 => uint256) qty_2;
    }

    mapping(address => liquidity_owner_stats) liquidity_owned; 

    /// @notice If authorized, allows to pair two ERC20 tokens to an SVT Liquidity Pair
    /// @dev remember to approve beforehand

     function create_svt_synthetic(address to_bridge, address pair, string calldata url) public Unlocked(5147) onlyAuth(to_bridge) {
        require(!imported[to_bridge], "Already synthetized");
        svt_last_id +=1;
        uint256 thisAddress = svt_last_id;
        imported[to_bridge] = true;
        imported_id[to_bridge] = thisAddress;
        SVT_address[thisAddress].deployed = true;
        SVT_address[thisAddress].totalSupply = ERC20(to_bridge).totalSupply();
        SVT_address[thisAddress].circulatingSupply = ERC20(to_bridge).totalSupply();
        SVT_address[thisAddress].is_synthetic = true;
        SVT_address[thisAddress].balance_url = url;
        SVT_address[thisAddress].original_token = to_bridge;
        SVT_address[thisAddress].pair_address = pair;
        SVT_address[thisAddress].name = ERC20(to_bridge).name();
        SVT_address[thisAddress].ticker = ERC20(to_bridge).symbol();        
     }

    function create_svt_native(bytes32[] calldata strings, uint256[] calldata params, uint256[] calldata fees) public Unlocked(471)  returns(uint256, uint256){
        svt_last_id += 1;
        svt_liquidity_last_id += 1;
        SVT_address[svt_last_id].deployed = true;
        SVT_address[svt_last_id].is_svt_native = true;
        SVT_address[svt_last_id].tokenOwner = msg.sender;
        SVT_address[svt_last_id].totalSupply = params[0];
        SVT_address[svt_last_id].circulatingSupply = params[1];
        SVT_address[svt_last_id].balance[msg.sender] = params[0];
        SVT_address[svt_last_id].fees = fees;
        SVT_address[svt_last_id].name = strings[0];
        SVT_address[svt_last_id].ticker = strings[1];
        SVT_address[svt_last_id].SVT_Liquidity_storage = svt_liquidity_last_id;
        return (svt_last_id, svt_liquidity_last_id);
    }

    function create_svt_native_pair(uint256 to_add, uint256 to_bridge, uint256 qty_1, uint256 qty_2) public Unlocked(471) {
        require(SVT_address[to_add].deployed && SVT_address[to_bridge].deployed, "Missing tokens");
        require((SVT_address[to_add].balance[msg.sender] >= qty_1) && (SVT_address[to_bridge].balance[msg.sender] >= qty_2), "Not enough tokens");
        uint256 to_add_liq = SVT_address[to_add].SVT_Liquidity_storage;
        if(!SVT_Liquidity_index[to_add_liq].active) {
            SVT_Liquidity_index[to_add_liq].active = true;
        } else {
            uint256 ratio = (SVT_Liquidity_index[to_add_liq].token_1_qty / SVT_Liquidity_index[to_add_liq].token_2_qty);
            require((qty_1/qty_2) == ratio, "Wrong ratio");
        }
            SVT_address[to_add].balance[msg.sender] -= qty_1;
            SVT_address[to_bridge].balance[msg.sender] -= qty_2;
            SVT_Liquidity_index[to_add_liq].token_1_qty += taxes_include_fee(qty_1);
            SVT_Liquidity_index[to_add_liq].token_2_qty += taxes_include_fee(qty_2);
            SVT_address[to_add].balance[owner] += (qty_1 - taxes_include_fee(qty_1));
            SVT_address[to_bridge].balance[owner] += (qty_2 - taxes_include_fee(qty_2));
            SVT_Liquidity_index[to_add_liq].SVT_token_id = to_add;
            SVT_Liquidity_index[to_add_liq].token_2_native = to_bridge;
            SVT_Liquidity_index[to_add_liq].liq_mode = 3;

    }

    function create_svt_pair(address to_bridge, address to_pair,  uint256 qty_1, uint256 qty_2) public Unlocked(841) onlyAuth(to_bridge) {
        ERC20 from_token = ERC20(to_bridge);
        ERC20 from_pair = ERC20(to_pair);
        bytes32 pool_name = keccak256(abi.encodePacked(from_token.name() , from_pair.name()));
        delete to_bridge;
        delete to_pair;
        require(from_token.balanceOf(msg.sender) >= qty_1, "You don't have enough tokens (1)");
        require(from_pair.balanceOf(msg.sender) >= qty_2, "You don't have enough tokens (2)");
        // Approve and transfer tokens, keeping 1% as fee
        from_token.transferFrom(msg.sender, address(this), qty_1);
        from_pair.transferFrom(msg.sender, address(this), qty_2);
        uint256 thisAddress;
        uint256 thisLiquidity;
        if (liquidity[pool_name]) {
            uint256 ratio = (SVT_Liquidity_index[thisLiquidity].token_1_qty / SVT_Liquidity_index[thisLiquidity].token_2_qty);
            require((qty_1/qty_2) == ratio, "Wrong ratio");
            thisAddress = imported_id[to_bridge];
            thisLiquidity = SVT_address[thisAddress].SVT_Liquidity_storage;

        } else {
            svt_last_id +=1;
            svt_liquidity_last_id += 1;
            thisAddress = svt_last_id;
            thisLiquidity = svt_liquidity_last_id;
            imported[to_bridge] = true;
            imported_id[to_bridge] = thisAddress;
            liquidity[pool_name] = true;
        }
        // Liquidity add
        if (to_pair == WETH) { 
            SVT_Liquidity_index[thisLiquidity].native_pair = true;
        }
        SVT_Liquidity_index[thisLiquidity].active = true;
        SVT_Liquidity_index[thisLiquidity].deployed = true;
        SVT_Liquidity_index[thisLiquidity].token_1 = ERC20(to_bridge);
        SVT_Liquidity_index[thisLiquidity].token_2 = ERC20(to_pair);
        delete to_pair;
        SVT_Liquidity_index[thisLiquidity].token_1_qty += taxes_include_fee(qty_1);
        SVT_Liquidity_index[thisLiquidity].token_2_qty += taxes_include_fee(qty_2);
        SVT_Liquidity_index[thisLiquidity].SVT_token_id = thisAddress;
        SVT_Liquidity_index[thisLiquidity].liq_mode = 1;
        // Token definition
        SVT_address[thisAddress].deployed = true;
        SVT_address[thisAddress].circulatingSupply += qty_1;
        liquidity_owned[msg.sender].owned[pool_name] = true;
        liquidity_owned[msg.sender].qty_1[pool_name] += qty_1;
        liquidity_owned[msg.sender].qty_2[pool_name] += qty_2;
        SVT_address[thisAddress].totalSupply = from_token.totalSupply();
        SVT_address[thisAddress].name = from_token.name();
        SVT_address[thisAddress].ticker = from_token.symbol();
        SVT_address[thisAddress].isBridged = true;
        SVT_address[thisAddress].original_token = to_bridge;
        //SVT_address[thisAddress].balance[msg.sender] = (qty_1*99)/100;
        SVT_address[thisAddress].SVT_Liquidity_storage = thisLiquidity;
        taxes_token_total[to_bridge] += ( qty_1 - taxes_include_fee(qty_1) );
        taxes_token_total[to_pair] += ( qty_2 - taxes_include_fee(qty_2) );

    }

    function create_svt_pair_from_eth(address to_bridge, uint256 qty_1) public payable onlyAuth(to_bridge) {
        ERC20 from_token = ERC20(to_bridge);
        ERC20 from_pair = ERC20(WETH);
        bytes32 pool_name = keccak256(abi.encodePacked(from_token.name() , from_pair.name()));
        require(from_token.balanceOf(msg.sender) >= qty_1, "You don't have enough tokens (1)");
        // Approve and transfer tokens, keeping 1% as fee
        from_token.transferFrom(msg.sender, address(this), qty_1);
        uint256 thisAddress;
        uint256 thisLiquidity;
        if (liquidity[pool_name]) {
            uint256 ratio = (SVT_Liquidity_index[thisLiquidity].token_1_qty / SVT_Liquidity_index[thisLiquidity].token_2_qty);
            require((qty_1/msg.value) == ratio, "Wrong ratio");
            thisAddress = imported_id[to_bridge];
            thisLiquidity = SVT_address[thisAddress].SVT_Liquidity_storage;

        }  else {
            svt_last_id +=1;
            svt_liquidity_last_id += 1;
            thisAddress = svt_last_id;
            thisLiquidity = svt_liquidity_last_id;
        }
        imported[to_bridge] = true;
        imported_id[to_bridge] = thisAddress;

        // Liquidity add
        SVT_Liquidity_index[thisLiquidity].native_pair = true;
        SVT_Liquidity_index[thisLiquidity].active = true;
        SVT_Liquidity_index[thisLiquidity].deployed = true;
        SVT_Liquidity_index[thisLiquidity].token_1 = from_token;
        SVT_Liquidity_index[thisLiquidity].token_2 = from_pair;
        SVT_Liquidity_index[thisLiquidity].token_1_qty += taxes_include_fee(qty_1);
        SVT_Liquidity_index[thisLiquidity].token_2_qty += taxes_include_fee(msg.value);
        liquidity_owned[msg.sender].owned[pool_name] = true;
        liquidity_owned[msg.sender].qty_1[pool_name] += qty_1;
        liquidity_owned[msg.sender].qty_2[pool_name] += msg.value;
        SVT_Liquidity_index[thisLiquidity].SVT_token_id = thisAddress;
        SVT_Liquidity_index[thisLiquidity].liq_mode = 1;

        // Token definition
        SVT_address[thisAddress].deployed = true;
        SVT_address[thisAddress].circulatingSupply += qty_1;
        SVT_address[thisAddress].totalSupply = from_token.totalSupply();
        SVT_address[thisAddress].name = from_token.name();
        SVT_address[thisAddress].ticker = from_token.symbol();
        SVT_address[thisAddress].isBridged = true;
        SVT_address[thisAddress].original_token = to_bridge;
        //SVT_address[thisAddress].balance[msg.sender] = (qty_1*995)/1000;
        SVT_address[thisAddress].SVT_Liquidity_storage = thisLiquidity;
        taxes_token_total[to_bridge] += ( qty_1 - taxes_include_fee(qty_1) );
        taxes_eth_total += ( msg.value - taxes_include_fee(msg.value) );
        liquidity[pool_name] = true;

    }

    function collect_taxes_eth() public onlyTeam {
        if (address(this).balance < taxes_eth_total) {
            payable(owner).transfer(address(this).balance);
        } else {
            payable(owner).transfer(taxes_eth_total);
        }
        taxes_eth_total = 0;
    }

    function collect_taxes_token(address addy) public onlyTeam {
        ERC20 token_erc = ERC20(addy);
        if (token_erc.balanceOf(address(this)) < taxes_token_total[addy]) {
            token_erc.transfer(owner, token_erc.balanceOf(address(this)));
        } else {
            token_erc.transfer(owner, taxes_token_total[addy]);
        }
        taxes_token_total[addy] = 0;
    }