contract CoveredCall {

	// Enum of the different states the contract can be in
	enum ContractStates {
		NONE,
		STATUS_INITIALIZED, // In the very beginning after contract creation
		STATUS_OPEN, // Buyer has paid premium and has the option of redeeming
		STATUS_REDEEMED, // Buyer has redeemed tokens - end of life
		STATUS_CLOSED // Seller has decided to close contract - end of life
	}

	// The current state of the contract as it flows through different actions.
	ContractStates public currentState;

	// Events triggered when users take action
	event PremiumUpdated(address indexed _seller, uint256 _oldPremiumAmount, uint256 _newPremiumAmount);
	event Initialized(address indexed _seller, uint256 _underlyingAssetAmount);
	event Opened(address indexed _buyer, uint256 _purchaseAmount);
	event Redeemed(address indexed _buyer, uint256 _paymentAmount);
	event Closed(address indexed _seller);

	address public buyer; // The address that created the contract
	address public seller; // The address that purchased the option

	IERC20 public underlyingAssetToken; // ERC20 being used as the underlying asset
	IERC20 public purchasingToken; // ERC20 being used for premium and redeem payments

	uint256 public underlyingAssetAmount; // Amount of the underlying asset being offered in the contract

	uint256 public strikePrice; // This is the strike price required to redeem. Redeem amount = strike * asset amount.
	uint256 public premiumAmount; // Amount of payment tokens required to open the contract

	uint256 public expirationDate; // Date after which the seller can close out and buyer can no longer can redeem

	/**
	 * The Seller initiates the contract by setting all parameters.
	 * The Seller should also have already "allowed" the ERC20 to be transferred in by the contract
	 * in the amount specified by premiumAmount.  This will be held in escrow.
	 */
	constructor(
		IERC20 _underlyingAssetToken,
		uint256 _underlyingAssetAmount,
		IERC20 _purchasingToken,
		uint256 _strikePrice,
		uint256 _premiumAmount,
		uint _expirationDate
	) public {
		// Validate inputs
		require(address(_underlyingAssetToken) != address(0), "The asset token must not be 0x0");
		require(_underlyingAssetAmount > 0, "The asset amount must be valid");
		require(address(_purchasingToken) != address(0), "The purchasing token must not be 0x0");
		require(_strikePrice > 0, "The strike price must be valid");
		require(_premiumAmount > 0, "The premium amount price must be valid");
		require(_expirationDate > now, "The expiration must be in the future");

		// Save off the inputs
		seller = msg.sender;
		underlyingAssetToken = _underlyingAssetToken;
		underlyingAssetAmount = _underlyingAssetAmount;
		purchasingToken = _purchasingToken;
		strikePrice = _strikePrice;
		premiumAmount = _premiumAmount;
		expirationDate = _expirationDate;

		// Make the contract be in a dead state
		currentState = ContractStates.NONE;
	}

	function initialize() public {
		require(
			currentState == ContractStates.NONE,
			"Contract must be in NONE state to allow initialization"
		);

		require(msg.sender == seller, "Only the original seller can initialize the contract");

		// Transfer in the underlying asset to escrow
		require(underlyingAssetToken.transferFrom(seller, address(this), underlyingAssetAmount), "Must provide initial escrow token");

		// Update the state to initialized
		currentState = ContractStates.STATUS_INITIALIZED;

		// Emit the event
		emit Initialized(seller, underlyingAssetAmount);
	}

	/**
	 * When the contract is in an initialized state, the seller can updated the premium amount to reflect real world price changes.
	 */
	function updatePremium(uint256 _premiumAmount) public {
		// Validate contract state
		require(
			currentState == ContractStates.STATUS_INITIALIZED,
			"Contract must be in initialized state to allow updates of the premium"
		);
		require(msg.sender == seller, "Only the original seller can update the premium");

		// Save off the old value
		uint256 oldPremiumAmount = premiumAmount;

		// Update to the new value
		premiumAmount = _premiumAmount;

		// Emit the event
		emit PremiumUpdated(seller, oldPremiumAmount, premiumAmount);
	}

	/**
	 * The buyer can call this to open the contract.
	 * THe buyer must have already "allowed" the contract to transfer premiumAmount purchasingTokens
	 */
	function open() public {
		// Validate contract state
		require(now < expirationDate, "Cannot open an expired contract");
		require(currentState == ContractStates.STATUS_INITIALIZED, "Contract must be in initialized state to open");

		// Save off the buyer
		buyer = msg.sender;

		// Transfer the tokens over to the seller
		require(purchasingToken.transferFrom(buyer, seller, premiumAmount), "Must pay premium to open contract");

		// Set the status to open
		currentState = ContractStates.STATUS_OPEN;

		// Emit the event
		emit Opened(buyer, premiumAmount);
	}

	/**
	 * If the contract was never opened by a buyer or the contract was never redeemed and it expired,
	 * the seller can close it out and get back the initial underlying asset token.
	 */
	function close() public {
		// Validate contract state
		require(
			currentState == ContractStates.STATUS_INITIALIZED ||
				(currentState == ContractStates.STATUS_OPEN && now > expirationDate),
			"Contract must be in initialized state or expired in order to close it"
		);
		require(msg.sender == seller, "Only the original seller can close a contract");

		// Transfer out the tokens from escrow
		underlyingAssetToken.transfer(seller, underlyingAssetAmount);

		// Set the status to closed
		currentState = ContractStates.STATUS_CLOSED;

		// Emit the event
		emit Closed(seller);
	}

	/**
	 * After a buyer has opened the contract they can redeem their right to the underlying asset.
	 * They can only do this if the contract is in the open state and it has not expired.
	 * They must pay the purchase price times the asset amount and they will get the underlying asset in return.
	 * The buyer must have already "allowed" the contract to transfer the payment amount of purchasingTokens
	 */
	function redeem() public {
		// Validate contract state
		require(
			currentState == ContractStates.STATUS_OPEN && now < expirationDate,
			"Contract must be in open state and not expired to redeem it"
		);
		require(msg.sender == buyer, "Only the original buyer can redeem a contract");

		// Calculate the amount of tokens that should be paid
		uint256 paymentAmount = underlyingAssetAmount * strikePrice;

		// Move the payment from the buyer to the seller
		require(purchasingToken.transferFrom(buyer, seller, paymentAmount), "Must pay amount * strike to redeem contract");

		// Pay out the buyer from escrow
		underlyingAssetToken.transfer(buyer, underlyingAssetAmount);

		// Set the status to closed
		currentState = ContractStates.STATUS_REDEEMED;

		// Emit the event
		emit Redeemed(buyer, paymentAmount);
	}

}
function redeem() public {
	// Validate contract state
	require(
		currentState == ContractStates.STATUS_OPEN && now < expirationDate,
		"Contract must be in open state and not expired to redeem it"
	);
	require(msg.sender == buyer, "Only the original buyer can redeem a contract");

	// Calculate the amount of tokens that should be paid
	uint256 paymentAmount = underlyingAssetAmount * strikePrice;

	// Move the payment from the buyer to the seller
	require(purchasingToken.transferFrom(buyer, seller, paymentAmount), "Must pay amount * strike to redeem contract");

	// Pay out the buyer from escrow
	underlyingAssetToken.transfer(buyer, underlyingAssetAmount);

	// Set the status to closed
	currentState = ContractStates.STATUS_REDEEMED;

	// Emit the event
	emit Redeemed(buyer, paymentAmount);
}

/**
 	 * After a buyer has opened the contract they can redeem their right to the underlying asset.
 	 * They can only do this if the contract is in the open state and it has not expired.
 	 * They must pay the purchase price times the asset amount and they will get the underlying asset in return.
 	 * The buyer must have already "allowed" the contract to transfer the payment amount of purchasingTokens
 	 */