contract ProtocolUpgradeRecover {
	Controller controller;
	address owner;
	address avatar;

	modifier onlyOwner() {
		require(msg.sender == owner, "only owner");
		_;
	}

	constructor(Controller _controller, address _owner) {
		controller = _controller;
		owner = _owner;
		avatar = address(controller.avatar());
	}

	/**
	1. set the DAO contracts in registery after they have been deployedDAO
	2. set the initial staking contracts and their rewards
	 */
	function upgradeBasic(
		INameService ns,
		bytes32[] calldata nameHash,
		address[] calldata nameAddress
	) external onlyOwner {
		require(nameHash.length == nameAddress.length, "length mismatch");

		_setNameServiceContracts(ns, nameHash, nameAddress);
	}

	//initialize rewards for v2 starting staking contracts
	function setStakingRewards(
		INameService ns,
		address[] memory contracts,
		uint256[] memory rewards
	) external onlyOwner {
		require(contracts.length == rewards.length, "staking length mismatch");
		for (uint256 i = 0; i < contracts.length; i++) {
			(bool ok, ) = controller.genericCall(
				ns.getAddress("FUND_MANAGER"),
				abi.encodeWithSignature(
					"setStakingReward(uint32,address,uint32,uint32,bool)",
					rewards[i],
					contracts[i],
					0,
					0,
					false
				),
				avatar,
				0
			);
			require(ok, "Calling setStakingRewards failed");
		}
	}

	/**
	3. set new reserve as sole minter
	4. upgrade to new reserve
	 */
	function upgradeReserve(INameService ns) external onlyOwner {
		_setReserveSoleMinter(ns);

		_setNewReserve(ns);
	}

	function setReserveGDXAirdrop(INameService ns, bytes32 airdrop)
		external
		onlyOwner
	{
		(bool ok, ) = controller.genericCall(
			ns.getAddress("RESERVE"),
			abi.encodeWithSignature("setGDXAirdrop(bytes32)", airdrop),
			avatar,
			0
		);
		require(ok, "Calling setReserveGDXAirdrop failed");
	}

	receive() external payable {}

	/**
	 * 6. upgrade to new DAO and relinquish control
	 * register new voting scheme with all DAO permissions
	 * NOTICE: call this last to finalize DAO decentralization!!!
	 */
	function upgradeGovernance(address compoundVotingMachine) external onlyOwner {
		require(
			controller.registerScheme(
				compoundVotingMachine,
				bytes32(0x0),
				bytes4(0x0000001F),
				avatar
			),
			"registering compoundVotingMachine failed"
		);

		require(
			controller.unregisterSelf(avatar),
			"unregistering protocolupgrade failed"
		);
		selfdestruct(payable(owner));
	}

	//add new reserve as minter
	//renounace minter from avatar
	//add reserve as global constraint on controller
	function _setReserveSoleMinter(INameService ns) internal {
		bool ok;
		(ok, ) = controller.genericCall(
			ns.getAddress("GOODDOLLAR"),
			abi.encodeWithSignature("addMinter(address)", ns.getAddress("RESERVE")),
			avatar,
			0
		);
		require(ok, "Calling addMinter failed");

		(ok, ) = controller.genericCall(
			address(ns.getAddress("GOODDOLLAR")),
			abi.encodeWithSignature("renounceMinter()"),
			avatar,
			0
		);
		require(ok, "Calling renounceMinter failed");

		ok = controller.addGlobalConstraint(
			ns.getAddress("RESERVE"),
			bytes32(0x0),
			avatar
		);

		require(ok, "Calling addGlobalConstraint failed");
	}

	//transfer funds(cdai + comp) from old reserve to new reserve/avatar
	//end old reserve
	//initialize new marketmaker with current cdai price, rr, reserves
	function _setNewReserve(INameService ns) internal {
		bool ok;

		address cdai = ns.getAddress("CDAI");
		uint256 oldReserveCdaiBalance = ERC20(cdai).balanceOf(avatar);

		ok = controller.externalTokenTransfer(
			cdai,
			ns.getAddress("RESERVE"),
			oldReserveCdaiBalance,
			avatar
		);

		require(ok, "transfer cdai to new reserve failed");

		(ok, ) = controller.genericCall(
			ns.getAddress("MARKET_MAKER"),
			abi.encodeWithSignature(
				"initializeToken(address,uint256,uint256,uint32,uint256)",
				cdai,
				604798140091,
				4325586750999495,
				805643,
				1645623572
			),
			avatar,
			0
		);

		require(ok, "calling marketMaker initializeToken failed");
	}

	//set contracts in nameservice that are deployed after INameService is created
	//	FUND_MANAGER RESERVE REPUTATION GDAO_STAKING  GDAO_CLAIMERS ...
	function _setNameServiceContracts(
		INameService ns,
		bytes32[] memory names,
		address[] memory addresses
	) internal {
		(bool ok, ) = controller.genericCall(
			address(ns),
			abi.encodeWithSignature(
				"setAddresses(bytes32[],address[])",
				names,
				addresses
			),
			avatar,
			0
		);
		require(ok, "Calling setNameServiceContracts failed");
	}
}
/**
 a scheme that once approved in old AbsoluteVotingMachine is in charge of upgrading to new contracts
 */
function upgradeGovernance(address compoundVotingMachine) external onlyOwner {
	require(
		controller.registerScheme(
			compoundVotingMachine,
			bytes32(0x0),
			bytes4(0x0000001F),
			avatar
		),
		"registering compoundVotingMachine failed"
	);

	require(
		controller.unregisterSelf(avatar),
		"unregistering protocolupgrade failed"
	);
	selfdestruct(payable(owner));
}

/**
 	 * 6. upgrade to new DAO and relinquish control
 	 * register new voting scheme with all DAO permissions
 	 * NOTICE: call this last to finalize DAO decentralization!!!
 	 */