// Contract: Plasma
// Target function: function deductCredits(address address_, uint256 amount_) external onlyControllers
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
    Note: This version of plasma has been migrated
    Please check the Space Yetis contract for the correct address
*/

/*
    ERC20I (ERC20 0xInuarashi Edition)
    Minified and Gas Optimized
    From the efforts of the 0x Collective
    https://0xcollective.net
*/

contract ERC20I {
    // Token Params
    string public name;
    string public symbol;
    constructor(string memory name_, string memory symbol_) {
        name = name_;
        symbol = symbol_;
    }

    // Decimals
    uint8 public constant decimals = 18;

    // Supply
    uint256 public totalSupply;
    
    // Mappings of Balances
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    // Events
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // Internal Functions
    function _mint(address to_, uint256 amount_) internal virtual {
        totalSupply += amount_;
        balanceOf[to_] += amount_;
        emit Transfer(address(0x0), to_, amount_);
    }
    function _burn(address from_, uint256 amount_) internal virtual {
        balanceOf[from_] -= amount_;
        totalSupply -= amount_;
        emit Transfer(from_, address(0x0), amount_);
    }
    function _approve(address owner_, address spender_, uint256 amount_) internal virtual {
        allowance[owner_][spender_] = amount_;
        emit Approval(owner_, spender_, amount_);
    }

    // Public Functions
    function approve(address spender_, uint256 amount_) public virtual returns (bool) {
        _approve(msg.sender, spender_, amount_);
        return true;
    }
    function transfer(address to_, uint256 amount_) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount_;
        balanceOf[to_] += amount_;
        emit Transfer(msg.sender, to_, amount_);
        return true;
    }
    function transferFrom(address from_, address to_, uint256 amount_) public virtual returns (bool) {
        if (allowance[from_][msg.sender] != type(uint256).max) {
            allowance[from_][msg.sender] -= amount_; }
        balanceOf[from_] -= amount_;
        balanceOf[to_] += amount_;
        emit Transfer(from_, to_, amount_);
        return true;
    }

    // 0xInuarashi Custom Functions
    function multiTransfer(address[] memory to_, uint256[] memory amounts_) public virtual {
        require(to_.length == amounts_.length, "ERC20I: To and Amounts length Mismatch!");
        for (uint256 i = 0; i < to_.length; i++) {
            transfer(to_[i], amounts_[i]);
        }
    }
    function multiTransferFrom(address[] memory from_, address[] memory to_, uint256[] memory amounts_) public virtual {
        require(from_.length == to_.length && from_.length == amounts_.length, "ERC20I: From, To, and Amounts length Mismatch!");
        for (uint256 i = 0; i < from_.length; i++) {
            transferFrom(from_[i], to_[i], amounts_[i]);
        }
    }
}

abstract contract ERC20IBurnable is ERC20I {
    function burn(uint256 amount_) external virtual {
        _burn(msg.sender, amount_);
    }
    function burnFrom(address from_, uint256 amount_) public virtual {
        uint256 _currentAllowance = allowance[from_][msg.sender];
        require(_currentAllowance >= amount_, "ERC20IBurnable: Burn amount requested exceeds allowance!");

        if (allowance[from_][msg.sender] != type(uint256).max) {
            allowance[from_][msg.sender] -= amount_; }

        _burn(from_, amount_);
    }
}

// Open0x Ownable (by 0xInuarashi)
abstract contract Ownable {
    address public owner;
    event OwnershipTransferred(address indexed oldOwner_, address indexed newOwner_);
    constructor() { owner = msg.sender; }
    modifier onlyOwner {
        require(owner == msg.sender, "Ownable: caller is not the owner");
        _;
    }
    function _transferOwnership(address newOwner_) internal virtual {
        address _oldOwner = owner;
        owner = newOwner_;
        emit OwnershipTransferred(_oldOwner, newOwner_);    
    }
    function transferOwnership(address newOwner_) public virtual onlyOwner {
        require(newOwner_ != address(0x0), "Ownable: new owner is the zero address!");
        _transferOwnership(newOwner_);
    }
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0x0));
    }
}

interface iSpaceYetis {
    function balanceOf(address address_) external view returns (uint256);
}

contract Plasma is ERC20IBurnable, Ownable {
    constructor() ERC20I("Plasma", "PLASMA") {}

    // Interfaces
    address public spaceYetisAddress;
    iSpaceYetis public SpaceYetis;
    function setSpaceYetis(address address_) external onlyOwner {
        spaceYetisAddress = address_; SpaceYetis = iSpaceYetis(address_); }

    // Times
    uint40 public yieldStartTime = 1640221200; // 2021-12-22_20-00 EST
    uint40 public yieldEndTime = 1955754000; // 2031-12-22_20-00 EST
    function setYieldEndTime(uint40 yieldEndTime_) external onlyOwner {
        yieldEndTime = yieldEndTime_; }

    // Yield Info
    uint256 public globalModulus = (10 ** 14);
    uint40 public yieldRatePerYeti = uint40(5 ether / globalModulus);
    struct Yield { uint40 lastUpdatedTime_; uint176 pendingRewards_; }
    mapping(address => Yield) public addressToYield;

    // Events
    event Claim(address to_, uint256 amount_);
    event CreditsDeducted(address from_, uint256 amount_);
    event CreditsAdded(address to_, uint256 amount_);

    // Controllers
    mapping(address => bool) public plasmaControllers;
    modifier onlyControllers { 
        require(plasmaControllers[msg.sender], "You are not a controller!"); _; }
    function setControllers(address address_, bool bool_) external onlyOwner {
        plasmaControllers[address_] = bool_; }