// File: Context.sol



pragma solidity ^0.5.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}
// File: DualityConsole.sol



pragma solidity >=0.5.0;

import "./Ownable.sol";
import "./SafeMath.sol";


/**
 * @title DualityConsole
 * Duality is a simple smart contract that allows two parties to open up a Duality contract
 * together where each party has their own currency, but this currency can only be
 * redeemed back to the party that minted them. This allows one party to mint
 * tokens as a gift to the other party, and the other party may then come back in the
 * future to redeem this currency for goods or services.
 */
contract DualityConsole is Ownable {
    using SafeMath for uint256;

    uint256 DUALITY_COST_WEI = 10**16; // 0.01 ETH

    /// @notice Event to fire when a new duality conract is created
    event DualityCreated(uint256 indexed dualityId, string name);
    /// @notice Event to fire when one party mints tokens
    event Minted(
        uint256 indexed dualityId,
        address indexed by,
        uint256 indexed amount
    );
    /// @notice Event to fire when one party burns tokens
    event Burned(
        uint256 indexed dualityId,
        address indexed by,
        uint256 indexed amount
    );

    struct Duality {
        string name; // Name of Duality
        address aOne; // Address of party one
        string nOne; // Name of party one
        uint256 bOne; // Balance of party one
        address aTwo; // Address of party two
        string nTwo; // Name of party two
        uint256 bTwo; // Balance of part two
    }

    Duality[] public dualities;

    /// @notice Modifier used to ensure the correct payment has been made
    /// @param _weiCost -The amount in wei that the payment needs to be
    /// @dev Required that msg.value is greater than or equal to _weiCost. Also,
    ///  _splitPayment is NOT used when a crest is purchased. The majority
    ///  of that payment goes to the previous owner obviously.
    modifier sufficientPayment(uint256 _weiCost) {
        require(
            (msg.value >= _weiCost) || isOwner(),
            "There is not a sufficient payment to perform this action."
        );
        _;
        if (msg.value > _weiCost) msg.sender.transfer(msg.value - _weiCost);
    }

    function getDualityCount() public returns (uint256) {
        return dualities.length;
    }

    /// @param dualityName Name given to the created crest
    /// @param partyOneName Address that receives the fees for this crest.
    function createDuality(
        string calldata dualityName,
        string calldata partyOneName,
        string calldata partyTwoName,
        address partyTwoAddress
    ) external payable sufficientPayment(DUALITY_COST_WEI) returns (uint256) {
        // TEST
        require(
            msg.sender != partyTwoAddress && partyTwoAddress != address(0),
            "Party two address cannot be the same as senders or be address zero."
        );

        uint256 tokenId = dualities.push(
            Duality(
                dualityName,
                msg.sender, // One Address
                partyOneName, // One Name
                0, // One Balance
                partyTwoAddress, // Two Address
                partyTwoName, // Two Name
                0 // Two Balance
            )
        ) - 1; // push() returns the new length of the array

        emit DualityCreated(tokenId, dualityName);
        return tokenId;
    }

    function withdrawalFunds() external payable onlyOwner() {
        msg.sender.transfer(address(this).balance);
    }

    function setPartyName(uint256 dualityId, string calldata name) external {
        require(dualityId < dualities.length, "This is an invalid dualityId");
        Duality storage duality = dualities[dualityId];
        if (duality.aOne == msg.sender) {
            duality.nOne = name;
            return;
        }
        require(
            duality.aTwo == msg.sender,
            "Sender address is not included in this duality."
        );
        duality.nTwo = name;
    }

    function mintDualityDollars(uint256 dualityId, uint256 amount) external {
        require(dualityId < dualities.length, "This is an invalid dualityId");
        Duality storage duality = dualities[dualityId];
        if (duality.aOne == msg.sender) {
            // Here we are minting tokens for the other party
            duality.bTwo = duality.bTwo.add(amount);
            return;
        }
        require(
            duality.aTwo == msg.sender,
            "Sender address is not included in this duality."
        );
        // Here we are minting tokens for the other party
        duality.bOne = duality.bOne.add(amount);
    }

    function burnDualityDollars(uint256 dualityId, uint256 amount) external {
        require(dualityId < dualities.length, "This is an invalid dualityId");
        Duality storage duality = dualities[dualityId];
        if (duality.aOne == msg.sender) {
            require(
                duality.bOne >= amount,
                "Sender does not have anout duality dollars to perform this action."
            );
            // Here we are burning tokens from the senders own balance
            duality.bOne -= amount;
            return;
        }
        require(
            duality.aTwo == msg.sender,
            "Sender address is not included in this duality."
        );
        require(
            duality.bTwo >= amount,
            "Sender does not have anout duality dollars to perform this action."
        );
        // Here we are burning tokens from the senders own balance
        duality.bTwo -= amount;
    }
}

// File: Migrations.sol



pragma solidity >=0.4.21 <0.7.0;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }
}

// File: Ownable.sol



pragma solidity ^0.5.0;

import "./Context.sol";


/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(
            newOwner != address(0),
            "Ownable: new owner is the zero address"
        );
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: SafeMath.sol



pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "SafeMath: modulo by zero");
        return a % b;
    }
}