creation_sourcecode: /*
            ,/`.
          ,'/ __`.
        ,'_/__ _ _`.
      ,'__/__ _ _  _`.
    ,'_  /___ __ _ __ `.
   '-.._/___ _ __ __  __`.
  */
  pragma solidity ^0.8.18;
  
  interface IERC721Receiver {
      function onERC721Received(
          address operator,
          address from,
          uint256 tokenId,
          bytes calldata data
      ) external returns (bytes4);
  }
  
  interface IERC165 {
      function supportsInterface(bytes4 interfaceId) external view returns (bool);
  }
  
  abstract contract ERC165 is IERC165 {
      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
          return interfaceId == type(IERC165).interfaceId;
      }
  }
  
  interface IERC721 is IERC165 {
      event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
      event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
      event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
      function balanceOf(address owner) external view returns (uint256 balance);
      function ownerOf(uint256 tokenId) external view returns (address owner);
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId,
          bytes calldata data
      ) external;
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId
      ) external;
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) external;
      function approve(address to, uint256 tokenId) external;
      function setApprovalForAll(address operator, bool _approved) external;
      function getApproved(uint256 tokenId) external view returns (address operator);
      function isApprovedForAll(address owner, address operator) external view returns (bool);
  }
  
  interface IERC721Enumerable is IERC721 {
      function totalSupply() external view returns (uint256);
      function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
      function tokenByIndex(uint256 index) external view returns (uint256);
  }
  
  interface IERC721Metadata is IERC721 {
      function name() external view returns (string memory);
      function symbol() external view returns (string memory);
      function tokenURI(uint256 tokenId) external view returns (string memory);
  }
  
  contract ERC721 is ERC165, IERC721, IERC721Metadata {
  
      string private _name;
  
      string private _symbol;
  
      mapping(uint256 => address) private _owners;
  
      mapping(address => uint256) private _balances;
  
      mapping(uint256 => address) private _tokenApprovals;
  
      mapping(address => mapping(address => bool)) private _operatorApprovals;
  
      constructor(string memory name_, string memory symbol_) {
          _name = name_;
          _symbol = symbol_;
      }
  
      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
          return
              interfaceId == type(IERC721).interfaceId ||
              interfaceId == type(IERC721Metadata).interfaceId ||
              super.supportsInterface(interfaceId);
      }
  
      function balanceOf(address owner) public view virtual override returns (uint256) {
          require(owner != address(0), "ERC721: address zero is not a valid owner");
          return _balances[owner];
      }
  
      function ownerOf(uint256 tokenId) public view virtual override returns (address) {
          address owner = _ownerOf(tokenId);
          require(owner != address(0), "ERC721: invalid token ID");
          return owner;
      }
  
      function name() public view virtual override returns (string memory) {
          return _name;
      }
  
      function symbol() public view virtual override returns (string memory) {
          return _symbol;
      }
  
      function uintToString(uint256 value) internal pure returns (string memory) {
          if (value == 0) {
              return "0";
          }
          
          uint256 temp = value;
          uint256 digits;
          
          while (temp != 0) {
              digits++;
              temp /= 10;
          }
          
          bytes memory buffer = new bytes(digits);
          
          while (value != 0) {
              digits -= 1;
              buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
              value /= 10;
          }
      
          return string(buffer);
      }
  
      function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
          _requireMinted(tokenId);
  
          string memory baseURI = _baseURI();
          return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, uintToString(tokenId) )) : "";
      }
  
      function _baseURI() internal view virtual returns (string memory) {
          return "";
      }
  
      function approve(address to, uint256 tokenId) public virtual override {
          address owner = ERC721.ownerOf(tokenId);
          require(to != owner, "ERC721: approval to current owner");
  
          require(
              msg.sender == owner || isApprovedForAll(owner, msg.sender),
              "ERC721: approve caller is not token owner or approved for all"
          );
  
          _approve(to, tokenId);
      }
  
      function getApproved(uint256 tokenId) public view virtual override returns (address) {
          _requireMinted(tokenId);
  
          return _tokenApprovals[tokenId];
      }
  
      function setApprovalForAll(address operator, bool approved) public virtual override {
          _setApprovalForAll(msg.sender, operator, approved);
      }
  
      function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
          return _operatorApprovals[owner][operator];
      }
  
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public virtual override {
          //solhint-disable-next-line max-line-length
          require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: caller is not token owner or approved");
  
          _transfer(from, to, tokenId);
      }
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public virtual override {
          safeTransferFrom(from, to, tokenId, "");
      }
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) public virtual override {
          require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: caller is not token owner or approved");
          _safeTransfer(from, to, tokenId, data);
      }
  
      function _safeTransfer(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _transfer(from, to, tokenId);
          require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
      }
  
      function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
          return _owners[tokenId];
      }
  
      function _exists(uint256 tokenId) internal view virtual returns (bool) {
          return _ownerOf(tokenId) != address(0);
      }
  
  
      function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
          address owner = ERC721.ownerOf(tokenId);
          return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
      }
  
      function _safeMint(
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _mint(to, tokenId);
          require(
              _checkOnERC721Received(address(0), to, tokenId, data),
              "ERC721: transfer to non ERC721Receiver implementer"
          );
      }
  
  
      function _mint(address to, uint256 tokenId) internal virtual {
          require(to != address(0), "ERC721: mint to the zero address");
          require(!_exists(tokenId), "ERC721: token already minted");
  
          _beforeTokenTransfer(address(0), to, tokenId, 1);
  
          // Check that tokenId was not minted by `_beforeTokenTransfer` hook
          require(!_exists(tokenId), "ERC721: token already minted");
  
          unchecked {
              _balances[to] += 1;
          }
  
          _owners[tokenId] = to;
  
          emit Transfer(address(0), to, tokenId);
  
          _afterTokenTransfer(address(0), to, tokenId, 1);
      }
  
      function _burn(uint256 tokenId) internal virtual {
          address owner = ERC721.ownerOf(tokenId);
  
          _beforeTokenTransfer(owner, address(0), tokenId, 1);
  
          // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
          owner = ERC721.ownerOf(tokenId);
  
          // Clear approvals
          delete _tokenApprovals[tokenId];
  
          unchecked {
              _balances[owner] -= 1;
          }
          delete _owners[tokenId];
  
          emit Transfer(owner, address(0), tokenId);
  
          _afterTokenTransfer(owner, address(0), tokenId, 1);
      }
  
      function _transfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {
          require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
          //require(to != address(0), "ERC721: transfer to the zero address");
  
          _beforeTokenTransfer(from, to, tokenId, 1);
  
          // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
          require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
  
          // Clear approvals from the previous owner
          delete _tokenApprovals[tokenId];
  
          unchecked {
              _balances[from] -= 1;
              _balances[to] += 1;
          }
          _owners[tokenId] = to;
  
          emit Transfer(from, to, tokenId);
  
          _afterTokenTransfer(from, to, tokenId, 1);
      }
  
      function _approve(address to, uint256 tokenId) internal virtual {
          _tokenApprovals[tokenId] = to;
          emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
      }
  
      function _setApprovalForAll(
          address owner,
          address operator,
          bool approved
      ) internal virtual {
          require(owner != operator, "ERC721: approve to caller");
          _operatorApprovals[owner][operator] = approved;
          emit ApprovalForAll(owner, operator, approved);
      }
  
      function _requireMinted(uint256 tokenId) internal view virtual {
          require(_exists(tokenId), "ERC721: invalid token ID");
      }
  
      function isContract(address _addr) public view returns (bool is_contract) {
          return _addr.code.length > 0;
      }
  
      function _checkOnERC721Received(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) private returns (bool) {
          if (isContract(to)) {
              try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
                  return retval == IERC721Receiver.onERC721Received.selector;
              } catch (bytes memory reason) {
                  if (reason.length == 0) {
                      revert("ERC721: transfer to non ERC721Receiver implementer");
                  } else {
                      /// @solidity memory-safe-assembly
                      assembly {
                          revert(add(32, reason), mload(reason))
                      }
                  }
              }
          } else {
              return true;
          }
      }
  
      function _beforeTokenTransfer(
          address from,
          address to,
          uint256 firstTokenId,
          uint256 batchSize
      ) internal virtual {}
  
      function _afterTokenTransfer(
          address from,
          address to,
          uint256 firstTokenId,
          uint256 batchSize
      ) internal virtual {}
  }
  
  abstract contract ERC721URIStorage is ERC721 {
      // Optional mapping for token URIs
      mapping(uint256 => string) private _tokenURIs;
  
      function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
          _requireMinted(tokenId);
  
          string memory _tokenURI = _tokenURIs[tokenId];
          string memory base = _baseURI();
  
          // If there is no base URI, return the token URI.
          if (bytes(base).length == 0) {
              return _tokenURI;
          }
          // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
          if (bytes(_tokenURI).length > 0) {
              return string(abi.encodePacked(base, _tokenURI));
          }
  
          return super.tokenURI(tokenId);
      }
  }
  
  abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
      mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
      mapping(uint256 => uint256) private _ownedTokensIndex;
      uint256[] private _allTokens;
      mapping(uint256 => uint256) private _allTokensIndex;
  
      function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
          return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
      }
  
      function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
          require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
          return _ownedTokens[owner][index];
      }
  
      function totalSupply() public view virtual override returns (uint256) {
          return _allTokens.length;
      }
  
      function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
          require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
          return _allTokens[index];
      }
  
      function _beforeTokenTransfer(
          address from,
          address to,
          uint256 firstTokenId,
          uint256 batchSize
      ) internal virtual override{
          super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
  
          if (batchSize > 1) {
              // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
              revert("ERC721Enumerable: consecutive transfers not supported");
          }
  
          uint256 tokenId = firstTokenId;
  
          if (from == address(0)) {
              _addTokenToAllTokensEnumeration(tokenId);
          } else if (from != to) {
              _removeTokenFromOwnerEnumeration(from, tokenId);
          }
          if (to == address(0)) {
              _removeTokenFromAllTokensEnumeration(tokenId);
          } else if (to != from) {
              _addTokenToOwnerEnumeration(to, tokenId);
          }
      }
  
      function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
          uint256 length = ERC721.balanceOf(to);
          _ownedTokens[to][length] = tokenId;
          _ownedTokensIndex[tokenId] = length;
      }
  
      function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
          _allTokensIndex[tokenId] = _allTokens.length;
          _allTokens.push(tokenId);
      }
  
      function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
          // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
          // then delete the last slot (swap and pop).
  
          uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
          uint256 tokenIndex = _ownedTokensIndex[tokenId];
  
          // When the token to delete is the last token, the swap operation is unnecessary
          if (tokenIndex != lastTokenIndex) {
              uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
  
              _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
              _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
          }
  
          // This also deletes the contents at the last position of the array
          delete _ownedTokensIndex[tokenId];
          delete _ownedTokens[from][lastTokenIndex];
      }
  
      function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
          // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
          // then delete the last slot (swap and pop).
  
          uint256 lastTokenIndex = _allTokens.length - 1;
          uint256 tokenIndex = _allTokensIndex[tokenId];
  
          // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
          // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
          // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
          uint256 lastTokenId = _allTokens[lastTokenIndex];
  
          _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
          _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
  
          // This also deletes the contents at the last position of the array
          delete _allTokensIndex[tokenId];
          _allTokens.pop();
      }
  }
  
  contract $ObelusBonds is ERC721, ERC721Enumerable, ERC721URIStorage {
      address THIS = address(this);
      Renderer renderer;
      
      address public admin;
      address hypeToken;
      uint hypeRatio;
  
      constructor() ERC721("Obelus Bond", "OBLS"){
          resolveToken = new ResolveToken( THIS );
          renderer = Renderer(THIS);
          address sender = msg.sender;
          resolveToken.mint(sender, 100*1e18);
          admin = sender;
      }
  
      function setOwner(address addr) public {
          require(msg.sender == admin);
          admin = addr;
      }
      
      function setRenderer(address addr) public {
          require(msg.sender== admin);
          renderer = Renderer(addr);
      }
      
      function setHypeToken(address addr, uint ratio) public {
          require(msg.sender== admin && hypeToken == address(0) );
          hypeToken = addr;
          hypeRatio = ratio;
      }
      
      function hype(uint amount) public {
          address sender = msg.sender;
          require(ERC20TransferFrom(hypeToken).transferFrom(sender, THIS, amount));
          uint mintAmount = amount * hypeRatio / 1e18;
          resolveToken.mint(sender, mintAmount);
          resolveToken.mint(admin, mintAmount);
      }
  
      // scaleFactor is used to convert Ether into bonds and vice-versa: they're of different
      // orders of magnitude, hence the need to bridge between the two.
      uint256 constant scaleFactor = 0x10000000000000000;
      uint totalMass;
      address address0 = address(0);
  
      // Slope
      uint256 constant internal tokenPriceInitial_ = 0.0000001 ether;
      uint256 constant internal tokenPriceIncremental_ = 0.00000001 ether;
  
      struct Card{
          bool kind;//false = bond, true = stake
          uint average_ethSpent;//for bonds only
          uint average_buyInTimeSum;//for bonds only
          int payouts;//to be payed out or what has been payed out depending on kind
          uint mass;//dividend share weight or bond weight depending on kind
      }
  
      mapping(uint256 => Card) cards;
  
      // The total number of resolves being staked in this contract
      uint256 public dissolvingResolves;
  
      // For calculating the hodl multiplier. Weighted average release time
      uint public sumOfInputETH;
      uint public sumOfInputTime;
      uint public sumOfOutputETH;
      uint public sumOfOutputTime;
  
  
      // Variable tracking how much Ether each token is currently worth
      // Note that this is scaled by the scaleFactor variable.
      uint256 public earningsPerResolve;
  
      //The resolve token contract
      ResolveToken public resolveToken;
  
      function fluxFee(uint paidAmount) public view returns(uint fee) {
          //we're only going to count resolve tokens that haven't been burned.
          uint totalResolveSupply = resolveToken.totalSupply() - resolveToken.balanceOf( address(0) );
          if ( dissolvingResolves == 0 || sumOfInputETH==0)
              return 0;
  
          //the fee is the % of resolve tokens outside of the contract times easing function
          return paidAmount * ( totalResolveSupply - dissolvingResolves ) / totalResolveSupply * sumOfOutputETH / sumOfInputETH;
      }
  
      // Calculate the current resolveEarnings associated with the caller address. This is the net result
      // of multiplying the number of resolves held by their current value in Ether and subtracting the
      // Ether that has already been paid out.
      function resolveEarnings(uint stakeID) public view returns (uint256 amount) {
          Card memory stake = cards[stakeID];
          return uint( int(earningsPerResolve * stake.mass) - stake.payouts) / scaleFactor;
      }
  
      event Buy( address indexed addr, uint256 spent, uint256 mass, uint256 resolveTax);
      function buy(address addr) public payable returns(uint createdBonds){
          // Any transaction of less than 1 szabo is likely to be worth less than the gas used to send it.
          if ( msg.value < 0.000001 ether )
              revert();
  
          // Calculate the fee
          uint fee = fluxFee(msg.value);
  
          // The amount of Ether used to purchase new bonds for the caller.
          uint numEther = msg.value;
  
          numEther = numEther - fee;
  
          //resolve reward tracking stuff
          uint currentTime = block.timestamp;
          Card storage bond = cards[_mint(addr)];
          bond.average_ethSpent = numEther;
          bond.average_buyInTimeSum = currentTime * scaleFactor * numEther;
          sumOfInputETH += numEther;
          sumOfInputTime += currentTime * scaleFactor * numEther;
  
          // The number of bonds which can be purchased for numEther.
          createdBonds = ethereumToTokens_(numEther);
  
          // Add the createdBonds to the total supply.
          totalMass += createdBonds;
  
          // Assign the bonds to the balance of the buyer.
          bond.mass = createdBonds;
  
          // Check if we have resolves dissolving
          uint resolveFee;
          if (dissolvingResolves > 0){
              resolveFee = fee/2 * scaleFactor;
              bond.payouts = int(fee)/2;
  
              // Fee is distributed to all existing resolve holders before the new bonds are purchased.
              // rewardPerResolve is the amount gained per resolve token from this purchase.
              uint rewardPerResolve = resolveFee/dissolvingResolves;
  
              // The Ether value per resolve is increased proportionally.
              earningsPerResolve += rewardPerResolve;
          }
  
          emit Buy(addr, msg.value, createdBonds, resolveFee);
          return createdBonds;
      }
  
      function _mint(address _to) internal returns (uint256 _mintedTokenId) {
          uint bondID = totalMinted;
          totalMinted++;
  
          _safeMint(_to, bondID, '');
          emit Mint(bondID, msg.sender, _to);
          emit Transfer(address0, _to, bondID);
      
          return bondID;
      }
  
      function avgHodl() public view returns(uint hodlTime){
          return (sumOfInputTime - sumOfOutputTime) / (sumOfInputETH - sumOfOutputETH) / scaleFactor;
      }
  
      function getReturnsForBonds(uint bondID, uint bondsReleased) public view returns(uint etherValue, uint mintedResolves, uint new_releaseTimeSum, uint new_releaseAmount, uint initialInput_ETH){
          Card memory bond = cards[bondID];
          uint output_ETH = tokensToEthereum_(bondsReleased);
          uint input_ETH = bond.average_ethSpent * bondsReleased / bond.mass;
          // hodl multiplier. because if you don't hodl at all, you shouldn't be rewarded resolves.
          // and the multiplier you get for hodling needs to be relative to the average hodl
          uint buyInTime = bond.average_buyInTimeSum / bond.average_ethSpent;
          uint NOW = block.timestamp;
          uint cashoutTime = NOW * scaleFactor - buyInTime;
          uint new_sumOfOutputTime = sumOfOutputTime + bond.average_buyInTimeSum * bondsReleased / bond.mass;
          uint new_sumOfOutputETH = sumOfOutputETH + input_ETH;
          uint averageHoldingTime = NOW * scaleFactor - ( sumOfInputTime - sumOfOutputTime ) / ( sumOfInputETH - sumOfOutputETH );
          return (
              output_ETH, 
              averageHoldingTime>0?input_ETH * cashoutTime / averageHoldingTime * input_ETH / output_ETH:0,
              new_sumOfOutputTime,
              new_sumOfOutputETH,
              input_ETH
          );
      }
  
      // Sells your bonds for Ether
      function sellBonds(uint[] memory bondIDs, uint[] memory amounts) public returns(uint _returned_eth, uint _returned_resolves, uint _initialInput_ETH){
          address sender = msg.sender;
          uint L = bondIDs.length;
          uint returned_eth;
          uint returned_resolves;
          uint initialInput_ETH;
          uint bondIndex;
          Card storage bond;
          for(uint i;i<L;i++){
              bondIndex = bondIDs[i];
              bond = cards[bondIndex];
              require(bond.mass >= amounts[i] && ownerOf(bondIndex) == sender && !bond.kind);
              ( returned_eth, returned_resolves, initialInput_ETH ) = sell( bondIndex, amounts[i]);
  
              _returned_eth += returned_eth;
              _returned_resolves += returned_resolves;
              _initialInput_ETH += initialInput_ETH;
          }
          return (returned_eth, returned_resolves, initialInput_ETH);
      }
  
      event Sell( address indexed addr, uint indexed bondID, uint256 bondsSold, uint256 cashout, uint256 resolves, uint256 resolveTax, uint256 initialCash);
      function sell(uint bondID,uint256 amount) internal returns(uint eth, uint resolves, uint initialInput){
          Card storage bond = cards[bondID];
          address payable sender = payable(msg.sender);
          // Calculate the amount of Ether & Resolves that the holder's bonds sell for at the current sell price.
  
          uint _payouts = uint(bond.payouts) * amount / bond.mass;
          bond.payouts -= int(_payouts);
  
          uint[] memory UINTs = new uint[](5);
          (
          UINTs[0]/*ether out*/,
          UINTs[1]/*minted resolves*/,
          UINTs[2]/*new_sumOfOutputTime*/,
          UINTs[3]/*new_sumOfOutputETH*/,
          UINTs[4]/*initialInput_ETH*/) = getReturnsForBonds(bondID, amount);
  
          // magic distribution
          resolveToken.mint(sender, UINTs[1]/*minted resolves*/);
  
          // update weighted average cashout time
          sumOfOutputTime = UINTs[2] /*new_sumOfOutputTime*/;
          sumOfOutputETH = UINTs[3] /*new_sumOfOutputETH*/;
  
          // reduce the amount of "eth spent" based on the percentage of bonds being sold back into the contract
          bond.average_ethSpent = bond.average_ethSpent * ( bond.mass - amount ) / bond.mass;
          // reduce the "buyInTime" sum that's used for average buy in time
          bond.average_buyInTimeSum = bond.average_buyInTimeSum * (bond.mass - amount) / bond.mass;
  
          // Net Ether for the seller.
          uint numEthers = UINTs[0]/*ether out*/;
  
          // Burn the bonds which were just sold from the total supply.
          totalMass -= amount;
  
          // Remove the bonds from the balance of the buyer.
          bond.mass -= amount;
          if(bond.mass==0){
              _transfer(sender, address0, bondID);
          }
  
          // Check if we have resolves dissolving
          if ( dissolvingResolves > 0 ){
              // SellSideDivs are distributed to all remaining resolve holders.
              // rewardPerResolve is the amount gained per resolve thanks to this sell.
              uint rewardPerResolve = _payouts * scaleFactor/dissolvingResolves;
  
              // The Ether value per resolve is increased proportionally.
              earningsPerResolve += rewardPerResolve;
          }
          
          
          (bool success, ) = sender.call{value:numEthers}("");
          require(success, "Transfer failed.");
  
          emit Sell( sender, bondID, amount, numEthers, UINTs[1]/*minted resolves*/, _payouts, UINTs[4] /*initialInput_ETH*/);
          return (numEthers, UINTs[1]/*minted resolves*/, UINTs[4] /*initialInput_ETH*/);
      }
  
      // Allow contract to accept resolve tokens
      event StakeResolves( uint indexed stakeID, uint256 amountStaked );
      function tokenFallback(address from, uint value, bytes calldata _data) external{
          if(msg.sender == address(resolveToken) ){
              uint stakeID = uint(bytes32(_data));
              Card storage stake;
              if(stakeID>0){
                  stake = cards[stakeID];
                  require(stake.kind);//require that this is actually a staking position and not a bond. (safety)
              }else{
                  stakeID = _mint(from);
                  stake = cards[stakeID];
                  stake.kind = true;
              }
              stake.mass += value;
              dissolvingResolves += value;
  
              // Then we update the payouts array for the "resolve shareholder" with this amount
              stake.payouts += int(earningsPerResolve * value);
  
              emit StakeResolves(stakeID, value);
          }else if(msg.sender == 0xe324C8cF74899461Ef7aD2c3EB952DA7819aabc5/*ring hop old supply*/){
              resolveToken.mint(from, value);
          }else{
              revert("no want");
          }
      }
  
      // Withdraws resolveEarnings held by the caller sending the transaction, updates
      // the requisite global variables, and transfers Ether back to the caller.
      event Withdraw( address indexed addr, uint indexed stakeID, uint256 earnings, uint256 dissolve );
      function withdraw(uint stakeID, uint amount) public returns(uint){
          address payable sender = payable(msg.sender);
          Card storage stake = cards[stakeID];
          // Retrieve the resolveEarnings associated with the address the request came from.
          uint upScaleDivs = uint( int( earningsPerResolve * stake.mass ) - stake.payouts);
          uint totalEarnings = upScaleDivs / scaleFactor;
          require( amount <= totalEarnings && amount > 0 && stake.kind && ownerOf(stakeID)==sender );
          uint oldWeight = stake.mass;
          stake.mass = oldWeight * ( totalEarnings - amount ) / totalEarnings;
          uint weightDiff = oldWeight - stake.mass;
          
          if(oldWeight == weightDiff){
              _transfer(sender, address0, stakeID);
          }
  
          resolveToken.transfer( address0, weightDiff);
          dissolvingResolves -= weightDiff;
          
          stake.payouts += int(upScaleDivs * amount / totalEarnings) - int(weightDiff*earningsPerResolve);
  
          // Send the resolveEarnings to the address that requested the withdraw.
          (bool success, ) = sender.call{value: amount}("");
          require(success, "Transfer failed.");
  
          emit Withdraw( sender, stakeID, amount, weightDiff);
          return weightDiff;
      }
  
      event PullResolves( address indexed addr, uint indexed stakeID, uint256 pulledResolves, uint256 forfeiture);
      function pullResolves(uint stakeID, uint amount) public returns (uint forfeiture){
          Card storage stake = cards[stakeID];
          address sender = msg.sender;
          uint resolves = stake.mass;
          require(amount <= resolves && amount > 0 && stake.kind && ownerOf(stakeID)==sender );
          require(amount < dissolvingResolves);//"you can't forfeit the last resolve"
  
          uint yourTotalEarnings = (uint)((int256)(resolves * earningsPerResolve) - stake.payouts);
          uint forfeitedEarnings = yourTotalEarnings * amount / resolves;
  
          // Update the payout array so that the "resolve shareholder" cannot claim resolveEarnings on previous staked resolves.
          stake.payouts += (int256)(forfeitedEarnings) - (int256)(earningsPerResolve * amount);
  
          stake.mass -= amount;
          dissolvingResolves -= amount;
          // The Ether value per token is increased proportionally.
          earningsPerResolve += forfeitedEarnings / dissolvingResolves;
  
          resolveToken.transfer( sender, amount );
          emit PullResolves( sender, stakeID, amount, forfeitedEarnings / scaleFactor);
          return forfeitedEarnings / scaleFactor;
      }
  
      function mixBonds(uint bondID_from, uint bondID_to, uint _amount) external{
          //mix bond values
          address owner = ownerOf(bondID_to);
          Card storage bondFrom = cards[bondID_from];
          Card storage bondTo = cards[bondID_to];
          require( ownerOf(bondID_from) == owner && owner == msg.sender && !bondFrom.kind && !bondTo.kind);
          uint totalMassOfBond = bondFrom.mass;
          uint ethSpent = bondFrom.average_ethSpent * _amount / totalMassOfBond;
          uint buyInTimeSum = bondFrom.average_buyInTimeSum * _amount / totalMassOfBond;
          bondFrom.average_ethSpent -= ethSpent;
          bondFrom.average_buyInTimeSum -= buyInTimeSum;
          bondTo.average_ethSpent += ethSpent;
          bondTo.average_buyInTimeSum += buyInTimeSum;
      }
  
      function calculateMassReceived(uint256 _ethereumToSpend) 
          public 
          view 
          returns(uint256)
      {
          uint256 _dividends = fluxFee(_ethereumToSpend);
          uint256 _taxedEthereum = subtract(_ethereumToSpend, _dividends);
          uint256 _amountOfTokens = ethereumToTokens_(_taxedEthereum);
          
          return _amountOfTokens;
      }
  
      function ethereumToTokens_(uint256 _ethereum)
          internal
          view
          returns(uint256)
      {
          uint256 _tokenPriceInitial = tokenPriceInitial_ * 1e18;
          uint256 _tokensReceived = 
          (
              (
                  // underflow attempts BTFO
                  subtract(
                      (sqrt
                          (
                              (_tokenPriceInitial**2)
                              +
                              (2*(tokenPriceIncremental_ * 1e18)*(_ethereum * 1e18))
                              +
                              (((tokenPriceIncremental_)**2)*(totalMass**2))
                              +
                              (2*(tokenPriceIncremental_)*_tokenPriceInitial*totalMass)
                          )
                      ), _tokenPriceInitial
                  )
              )/(tokenPriceIncremental_)
          )-(totalMass)
          ;
  
          return _tokensReceived;
      }
  
      function tokensToEthereum_(uint256 _tokens)
          internal
          view
          returns(uint256)
      {
  
          uint256 tokens_ = (_tokens + 1e18);
          uint256 _tokenSupply = (totalMass + 1e18);
          uint256 _etherReceived =
          (
              subtract(
                  (
                      (
                          (
                              tokenPriceInitial_ +(tokenPriceIncremental_ * (_tokenSupply/1e18))
                          )-tokenPriceIncremental_
                      )*(tokens_ - 1e18)
                  ),(tokenPriceIncremental_*((tokens_**2-tokens_)/1e18))/2
              )
          /1e18);
          return _etherReceived;
      }
  
      function sqrt(uint x) internal pure returns (uint y) {
          uint z = (x + 1) / 2;
          y = x;
          while (z < y) {
              y = z;
              z = (x / z + z) / 2;
          }
      }
  
      function subtract(uint256 a, uint256 b) internal pure returns (uint256) {
          assert(b <= a);
          return a - b;
      }
  
      uint256 internal totalMinted = 0;
      
      // The following functions are overrides required by Solidity.
      function supportsInterface(bytes4 interfaceId)
          public
          view
          override(ERC721, ERC721Enumerable)
          returns (bool)
      {
          return super.supportsInterface(interfaceId);
      }
  
      function totalSupply() public view override(ERC721Enumerable)returns (uint256){
          return totalMinted;
      }
  
      function getAccount(address account) external view returns(uint[] memory cardIDs){
          uint L = balanceOf(account);
          cardIDs = new uint[](L);
          for(uint i;i<L;i++){
              cardIDs[i] = tokenOfOwnerByIndex(account,i);
          }
      }
  
      function getCardData(uint[] memory cardIDs) external view returns(bool[] memory kinds, uint[] memory masses,uint[] memory average_ethSpent,uint[] memory bondValues,uint[] memory average_buyInTimeSum,uint[] memory payouts,uint[] memory resolvePotential){
          uint L = cardIDs.length;
          masses = new uint[](L);
          average_ethSpent = new uint[](L);
          average_buyInTimeSum = new uint[](L);
          payouts = new uint[](L);
          resolvePotential = new uint[](L);
          bondValues = new uint[](L);
          kinds = new bool[](L);
          Card memory card;
          uint cardID;
          uint resolves;
          uint bondValue;
          for(uint i;i<L;i++){
              cardID = cardIDs[i];
              card = cards[cardID];
              kinds[i] = card.kind;
  
              masses[i] = card.mass;
              
              average_ethSpent[i] = card.average_ethSpent;
              average_buyInTimeSum[i] = (card.average_ethSpent>0)?card.average_buyInTimeSum/card.average_ethSpent/scaleFactor:0;
  
              if(card.kind){
                  payouts[i] = resolveEarnings(cardID);//what this staking position has earned
              }else{
                  payouts[i] = uint(card.payouts);//earnings this bond has yet to distribute when sold
              }
  
              if(card.mass>0 && !card.kind){
                  (bondValue,resolves,,,) = getReturnsForBonds(cardID,card.mass);
              }else{
                  bondValue = 0;
                  resolves = 0; 
              }
              
              bondValues[i] = bondValue;
              resolvePotential[i] = resolves;
          }
      }
  
      function getStakeValue(uint cardID) external view returns(uint){
          return resolveEarnings(cardID);
      }
  
      function globalData() external view returns(uint liquidity, uint _totalMass, uint _totalSupply, uint resolves_inside, uint resolves_total, uint _fluxFee, uint average_hodl){
          liquidity = totalMass>0?tokensToEthereum_(totalMass):0;
          _totalMass = totalMass;
          _totalSupply = totalSupply();
          resolves_inside = dissolvingResolves;
          resolves_total = resolveToken.totalSupply() - resolveToken.balanceOf( address(0) );
          _fluxFee = fluxFee(1000000);
          average_hodl = (sumOfInputETH - sumOfOutputETH)>0?avgHodl():0;
      }
  
      function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable){
          super._beforeTokenTransfer(from, to, tokenId, batchSize);
      }
      
      function tokenURI(uint256 tokenId) public view override(ERC721,ERC721URIStorage) returns (string memory) {
          require( _exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
          return string(abi.encodePacked("data:application/json;utf8,", renderer.json(tokenId)));
      }
  
      function json(uint256 id) public view returns(string memory){
          return '';
      }
  
      event Mint(uint indexed tokenId, address indexed mintedBy, address indexed mintedTo);
  }
  
  abstract contract ERC20TransferFrom {
      function transferFrom(address sender, address recipient, uint256 amount) public virtual returns (bool);
  }
  
  abstract contract Renderer{
      function json(uint id) public view virtual returns(string memory);
  }
  
  contract ResolveToken{
  
      string public name = "Resolve";
      string public symbol = "RSLV";
      uint8 constant public decimals = 18;
      address public hourglass;
  
      constructor(address _hourglass){
          hourglass = _hourglass;
      }
  
      modifier hourglassOnly{
          require(msg.sender == hourglass);
          _;
      }
  
      function mint(address _address, uint _value) external hourglassOnly(){
          balances[_address] += _value;
          _totalSupply += _value;
          emit Transfer(address(0), _address, _value);
      }
  
      mapping(address => uint256) public balances;
  
      uint public _totalSupply;
  
      mapping(address => mapping(address => uint)) approvals;
  
      event Transfer(
          address indexed from,
          address indexed to,
          uint256 amount,
          bytes data
      );
  
      event Transfer(
          address indexed from,
          address indexed to,
          uint256 amount
      );
      
      function totalSupply() public view returns (uint256) {
          return _totalSupply;
      }
  
      function balanceOf(address _owner) public view returns (uint256 balance) {
          return balances[_owner];
      }
  
      // Standard function transfer similar to ERC20 transfer with no _data.
      // Added due to backwards compatibility reasons.
      function transfer(address _to, uint _value) public virtual returns (bool) {
          //standard function transfer similar to ERC20 transfer with no _data
          //added due to backwards compatibility reasons
          bytes memory empty;
          if(isContract(_to)){
              return transferToContract(_to, _value, empty);
          }else{
              return transferToAddress(_to, _value, empty);
          }
      }
  
      // Function that is called when a user or another contract wants to transfer funds.
      function transfer(address _to, uint _value, bytes memory _data) public virtual returns (bool) {
          if( isContract(_to) ){
              return transferToContract(_to, _value, _data);
          }else{
              return transferToAddress(_to, _value, _data);
          }
      }
      
  
      //function that is called when transaction target is an address
      function transferToAddress(address _to, uint _value, bytes memory _data) private returns (bool) {
          moveTokens(msg.sender, _to, _value);
          emit Transfer(msg.sender, _to, _value, _data);
          emit Transfer(msg.sender, _to, _value);
          return true;
      }
  
      //function that is called when transaction target is a contract
      function transferToContract(address _to, uint _value, bytes memory _data) private returns (bool) {
          moveTokens(msg.sender, _to, _value);
          ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
          receiver.tokenFallback(msg.sender, _value, _data);
          emit Transfer(msg.sender, _to, _value, _data);
          emit Transfer(msg.sender, _to, _value);
          return true;
      }
  
      function moveTokens(address _from, address _to, uint _amount) internal{
          require( _amount <= balances[_from] );
  
          //update balances
          balances[_from] -= _amount;
          balances[_to] += _amount;
      }
  
      function allowance(address src, address guy) public view returns (uint) {
          return approvals[src][guy];
      }
      
      function transferFrom(address src, address dst, uint amount) public returns (bool){
          address sender = msg.sender;
          require(approvals[src][sender] >=  amount);
          require(balances[src] >= amount);
          approvals[src][sender] -= amount;
          moveTokens(src,dst,amount);
          bytes memory empty;
          emit Transfer(sender, dst, amount, empty);
          emit Transfer(msg.sender, dst, amount);
          return true;
      }
  
      event Approval(address indexed src, address indexed guy, uint amount);
      function approve(address guy, uint amount) public returns (bool) {
          address sender = msg.sender;
          approvals[sender][guy] = amount;
  
          emit Approval( sender, guy, amount );
          return true;
      }
  
      function isContract(address _addr) public view returns (bool is_contract) {
          return _addr.code.length > 0;
      }
  }
  
  abstract contract ERC223ReceivingContract{
      function tokenFallback(address _from, uint _value, bytes calldata _data) external virtual;
  }