ETH Price: $2,461.67 (+1.89%)

Transaction Decoder

Block:
19199357 at Feb-10-2024 06:02:59 PM +UTC
Transaction Fee:
0.0043968 ETH $10.82
Gas Used:
137,400 Gas / 32 Gwei

Emitted Events:

149 Governance.ProposalQueued( proposalId=17, caller=[Sender] 0xcbdc0c4c1d1eb586b96ee78143ef7291bbf9921f, eta=1707671218 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
46.101211687637732258 Eth46.101467005047875858 Eth0.0002553174101436
0x6f1e586C...68E109365
0xcbDC0C4c...1bbf9921F
0.054757410732565777 Eth
Nonce: 368
0.050360610732565777 Eth
Nonce: 369
0.0043968

Execution Trace

Governance.queue( proposalId=17 )
  • Barn.f77f962f( )
    • BarnFacet.bondStakedAtTs( timestamp=1707239218 ) => ( 309397176510151444715741960 )
      File 1 of 3: Governance
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "./interfaces/IBarn.sol";
      import "./Bridge.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      contract Governance is Bridge {
          using SafeMath for uint256;
          enum ProposalState {
              WarmUp,
              Active,
              Canceled,
              Failed,
              Accepted,
              Queued,
              Grace,
              Expired,
              Executed,
              Abrogated
          }
          struct Receipt {
              // Whether or not a vote has been cast
              bool hasVoted;
              // The number of votes the voter had, which were cast
              uint256 votes;
              // support
              bool support;
          }
          struct AbrogationProposal {
              address creator;
              uint256 createTime;
              string description;
              uint256 forVotes;
              uint256 againstVotes;
              mapping(address => Receipt) receipts;
          }
          struct ProposalParameters {
              uint256 warmUpDuration;
              uint256 activeDuration;
              uint256 queueDuration;
              uint256 gracePeriodDuration;
              uint256 acceptanceThreshold;
              uint256 minQuorum;
          }
          struct Proposal {
              // proposal identifiers
              // unique id
              uint256 id;
              // Creator of the proposal
              address proposer;
              // proposal description
              string description;
              string title;
              // proposal technical details
              // ordered list of target addresses to be made
              address[] targets;
              // The ordered list of values (i.e. msg.value) to be passed to the calls to be made
              uint256[] values;
              // The ordered list of function signatures to be called
              string[] signatures;
              // The ordered list of calldata to be passed to each call
              bytes[] calldatas;
              // proposal creation time - 1
              uint256 createTime;
              // votes status
              // The timestamp that the proposal will be available for execution, set once the vote succeeds
              uint256 eta;
              // Current number of votes in favor of this proposal
              uint256 forVotes;
              // Current number of votes in opposition to this proposal
              uint256 againstVotes;
              bool canceled;
              bool executed;
              // Receipts of ballots for the entire set of voters
              mapping(address => Receipt) receipts;
              ProposalParameters parameters;
          }
          uint256 public lastProposalId;
          mapping(uint256 => Proposal) public proposals;
          mapping(uint256 => AbrogationProposal) public abrogationProposals;
          mapping(address => uint256) public latestProposalIds;
          IBarn barn;
          bool isInitialized;
          bool public isActive;
          event ProposalCreated(uint256 indexed proposalId);
          event Vote(uint256 indexed proposalId, address indexed user, bool support, uint256 power);
          event VoteCanceled(uint256 indexed proposalId, address indexed user);
          event ProposalQueued(uint256 indexed proposalId, address caller, uint256 eta);
          event ProposalExecuted(uint256 indexed proposalId, address caller);
          event ProposalCanceled(uint256 indexed proposalId, address caller);
          event AbrogationProposalStarted(uint256 indexed proposalId, address caller);
          event AbrogationProposalExecuted(uint256 indexed proposalId, address caller);
          event AbrogationProposalVote(uint256 indexed proposalId, address indexed user, bool support, uint256 power);
          event AbrogationProposalVoteCancelled(uint256 indexed proposalId, address indexed user);
          receive() external payable {}
          // executed only once
          function initialize(address barnAddr) public {
              require(isInitialized == false, "Contract already initialized.");
              require(barnAddr != address(0), "barn must not be 0x0");
              barn = IBarn(barnAddr);
              isInitialized = true;
          }
          function activate() public {
              require(!isActive, "DAO already active");
              require(barn.bondStaked() >= ACTIVATION_THRESHOLD, "Threshold not met yet");
              isActive = true;
          }
          function propose(
              address[] memory targets,
              uint256[] memory values,
              string[] memory signatures,
              bytes[] memory calldatas,
              string memory description,
              string memory title
          )
          public returns (uint256)
          {
              if (!isActive) {
                  require(barn.bondStaked() >= ACTIVATION_THRESHOLD, "DAO not yet active");
                  isActive = true;
              }
              require(
                  barn.votingPowerAtTs(msg.sender, block.timestamp - 1) >= _getCreationThreshold(),
                  "Creation threshold not met"
              );
              require(
                  targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length,
                  "Proposal function information arity mismatch"
              );
              require(targets.length != 0, "Must provide actions");
              require(targets.length <= PROPOSAL_MAX_ACTIONS, "Too many actions on a vote");
              require(bytes(title).length > 0, "title can't be empty");
              require(bytes(description).length > 0, "description can't be empty");
              // check if user has another running vote
              uint256 previousProposalId = latestProposalIds[msg.sender];
              if (previousProposalId != 0) {
                  require(_isLiveState(previousProposalId) == false, "One live proposal per proposer");
              }
              uint256 newProposalId = lastProposalId + 1;
              Proposal storage newProposal = proposals[newProposalId];
              newProposal.id = newProposalId;
              newProposal.proposer = msg.sender;
              newProposal.description = description;
              newProposal.title = title;
              newProposal.targets = targets;
              newProposal.values = values;
              newProposal.signatures = signatures;
              newProposal.calldatas = calldatas;
              newProposal.createTime = block.timestamp - 1;
              newProposal.parameters.warmUpDuration = warmUpDuration;
              newProposal.parameters.activeDuration = activeDuration;
              newProposal.parameters.queueDuration = queueDuration;
              newProposal.parameters.gracePeriodDuration = gracePeriodDuration;
              newProposal.parameters.acceptanceThreshold = acceptanceThreshold;
              newProposal.parameters.minQuorum = minQuorum;
              lastProposalId = newProposalId;
              latestProposalIds[msg.sender] = newProposalId;
              emit ProposalCreated(newProposalId);
              return newProposalId;
          }
          function queue(uint256 proposalId) public {
              require(state(proposalId) == ProposalState.Accepted, "Proposal can only be queued if it is succeeded");
              Proposal storage proposal = proposals[proposalId];
              uint256 eta = proposal.createTime + proposal.parameters.warmUpDuration + proposal.parameters.activeDuration + proposal.parameters.queueDuration;
              proposal.eta = eta;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                  require(
                      !queuedTransactions[_getTxHash(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta)],
                      "proposal action already queued at eta"
                  );
                  queueTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
              }
              emit ProposalQueued(proposalId, msg.sender, eta);
          }
          function execute(uint256 proposalId) public payable {
              require(_canBeExecuted(proposalId), "Cannot be executed");
              Proposal storage proposal = proposals[proposalId];
              proposal.executed = true;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                  executeTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
              }
              emit ProposalExecuted(proposalId, msg.sender);
          }
          function cancelProposal(uint256 proposalId) public {
              require(_isCancellableState(proposalId), "Proposal in state that does not allow cancellation");
              require(_canCancelProposal(proposalId), "Cancellation requirements not met");
              Proposal storage proposal = proposals[proposalId];
              proposal.canceled = true;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                  cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
              }
              emit ProposalCanceled(proposalId, msg.sender);
          }
          function castVote(uint256 proposalId, bool support) public {
              require(state(proposalId) == ProposalState.Active, "Voting is closed");
              Proposal storage proposal = proposals[proposalId];
              Receipt storage receipt = proposal.receipts[msg.sender];
              // exit if user already voted
              require(receipt.hasVoted == false || receipt.hasVoted && receipt.support != support, "Already voted this option");
              uint256 votes = barn.votingPowerAtTs(msg.sender, _getSnapshotTimestamp(proposal));
              require(votes > 0, "no voting power");
              // means it changed its vote
              if (receipt.hasVoted) {
                  if (receipt.support) {
                      proposal.forVotes = proposal.forVotes.sub(receipt.votes);
                  } else {
                      proposal.againstVotes = proposal.againstVotes.sub(receipt.votes);
                  }
              }
              if (support) {
                  proposal.forVotes = proposal.forVotes.add(votes);
              } else {
                  proposal.againstVotes = proposal.againstVotes.add(votes);
              }
              receipt.hasVoted = true;
              receipt.votes = votes;
              receipt.support = support;
              emit Vote(proposalId, msg.sender, support, votes);
          }
          function cancelVote(uint256 proposalId) public {
              require(state(proposalId) == ProposalState.Active, "Voting is closed");
              Proposal storage proposal = proposals[proposalId];
              Receipt storage receipt = proposal.receipts[msg.sender];
              uint256 votes = barn.votingPowerAtTs(msg.sender, _getSnapshotTimestamp(proposal));
              require(receipt.hasVoted, "Cannot cancel if not voted yet");
              if (receipt.support) {
                  proposal.forVotes = proposal.forVotes.sub(votes);
              } else {
                  proposal.againstVotes = proposal.againstVotes.sub(votes);
              }
              receipt.hasVoted = false;
              receipt.votes = 0;
              receipt.support = false;
              emit VoteCanceled(proposalId, msg.sender);
          }
          // ======================================================================================================
          // Abrogation proposal methods
          // ======================================================================================================
          // the Abrogation Proposal is a mechanism for the DAO participants to veto the execution of a proposal that was already
          // accepted and it is currently queued. For the Abrogation Proposal to pass, 50% + 1 of the vBOND holders
          // must vote FOR the Abrogation Proposal
          function startAbrogationProposal(uint256 proposalId, string memory description) public {
              require(state(proposalId) == ProposalState.Queued, "Proposal must be in queue");
              require(
                  barn.votingPowerAtTs(msg.sender, block.timestamp - 1) >= _getCreationThreshold(),
                  "Creation threshold not met"
              );
              AbrogationProposal storage ap = abrogationProposals[proposalId];
              require(ap.createTime == 0, "Abrogation proposal already exists");
              require(bytes(description).length > 0, "description can't be empty");
              ap.createTime = block.timestamp;
              ap.creator = msg.sender;
              ap.description = description;
              emit AbrogationProposalStarted(proposalId, msg.sender);
          }
          // abrogateProposal cancels a proposal if there's an Abrogation Proposal that passed
          function abrogateProposal(uint256 proposalId) public {
              require(state(proposalId) == ProposalState.Abrogated, "Cannot be abrogated");
              Proposal storage proposal = proposals[proposalId];
              require(proposal.canceled == false, "Cannot be abrogated");
              proposal.canceled = true;
              for (uint256 i = 0; i < proposal.targets.length; i++) {
                  cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
              }
              emit AbrogationProposalExecuted(proposalId, msg.sender);
          }
          function abrogationProposal_castVote(uint256 proposalId, bool support) public {
              require(0 < proposalId && proposalId <= lastProposalId, "invalid proposal id");
              AbrogationProposal storage abrogationProposal = abrogationProposals[proposalId];
              require(
                  state(proposalId) == ProposalState.Queued && abrogationProposal.createTime != 0,
                  "Abrogation Proposal not active"
              );
              Receipt storage receipt = abrogationProposal.receipts[msg.sender];
              require(
                  receipt.hasVoted == false || receipt.hasVoted && receipt.support != support,
                  "Already voted this option"
              );
              uint256 votes = barn.votingPowerAtTs(msg.sender, abrogationProposal.createTime - 1);
              require(votes > 0, "no voting power");
              // means it changed its vote
              if (receipt.hasVoted) {
                  if (receipt.support) {
                      abrogationProposal.forVotes = abrogationProposal.forVotes.sub(receipt.votes);
                  } else {
                      abrogationProposal.againstVotes = abrogationProposal.againstVotes.sub(receipt.votes);
                  }
              }
              if (support) {
                  abrogationProposal.forVotes = abrogationProposal.forVotes.add(votes);
              } else {
                  abrogationProposal.againstVotes = abrogationProposal.againstVotes.add(votes);
              }
              receipt.hasVoted = true;
              receipt.votes = votes;
              receipt.support = support;
              emit AbrogationProposalVote(proposalId, msg.sender, support, votes);
          }
          function abrogationProposal_cancelVote(uint256 proposalId) public {
              require(0 < proposalId && proposalId <= lastProposalId, "invalid proposal id");
              AbrogationProposal storage abrogationProposal = abrogationProposals[proposalId];
              Receipt storage receipt = abrogationProposal.receipts[msg.sender];
              require(
                  state(proposalId) == ProposalState.Queued && abrogationProposal.createTime != 0,
                  "Abrogation Proposal not active"
              );
              uint256 votes = barn.votingPowerAtTs(msg.sender, abrogationProposal.createTime - 1);
              require(receipt.hasVoted, "Cannot cancel if not voted yet");
              if (receipt.support) {
                  abrogationProposal.forVotes = abrogationProposal.forVotes.sub(votes);
              } else {
                  abrogationProposal.againstVotes = abrogationProposal.againstVotes.sub(votes);
              }
              receipt.hasVoted = false;
              receipt.votes = 0;
              receipt.support = false;
              emit AbrogationProposalVoteCancelled(proposalId, msg.sender);
          }
          // ======================================================================================================
          // views
          // ======================================================================================================
          function state(uint256 proposalId) public view returns (ProposalState) {
              require(0 < proposalId && proposalId <= lastProposalId, "invalid proposal id");
              Proposal storage proposal = proposals[proposalId];
              if (proposal.canceled) {
                  return ProposalState.Canceled;
              }
              if (proposal.executed) {
                  return ProposalState.Executed;
              }
              if (block.timestamp <= proposal.createTime + proposal.parameters.warmUpDuration) {
                  return ProposalState.WarmUp;
              }
              if (block.timestamp <= proposal.createTime + proposal.parameters.warmUpDuration + proposal.parameters.activeDuration) {
                  return ProposalState.Active;
              }
              if ((proposal.forVotes + proposal.againstVotes) < _getQuorum(proposal) ||
                  (proposal.forVotes < _getMinForVotes(proposal))) {
                  return ProposalState.Failed;
              }
              if (proposal.eta == 0) {
                  return ProposalState.Accepted;
              }
              if (block.timestamp < proposal.eta) {
                  return ProposalState.Queued;
              }
              if (_proposalAbrogated(proposalId)) {
                  return ProposalState.Abrogated;
              }
              if (block.timestamp <= proposal.eta + proposal.parameters.gracePeriodDuration) {
                  return ProposalState.Grace;
              }
              return ProposalState.Expired;
          }
          function getReceipt(uint256 proposalId, address voter) public view returns (Receipt memory) {
              return proposals[proposalId].receipts[voter];
          }
          function getProposalParameters(uint256 proposalId) public view returns (ProposalParameters memory) {
              return proposals[proposalId].parameters;
          }
          function getAbrogationProposalReceipt(uint256 proposalId, address voter) public view returns (Receipt memory) {
              return abrogationProposals[proposalId].receipts[voter];
          }
          function getActions(uint256 proposalId) public view returns (
              address[] memory targets,
              uint256[] memory values,
              string[] memory signatures,
              bytes[] memory calldatas
          ) {
              Proposal storage p = proposals[proposalId];
              return (p.targets, p.values, p.signatures, p.calldatas);
          }
          function getProposalQuorum(uint256 proposalId) public view returns (uint256) {
              require(0 < proposalId && proposalId <= lastProposalId, "invalid proposal id");
              return _getQuorum(proposals[proposalId]);
          }
          // ======================================================================================================
          // internal methods
          // ======================================================================================================
          function _canCancelProposal(uint256 proposalId) internal view returns (bool){
              Proposal storage proposal = proposals[proposalId];
              if (msg.sender == proposal.proposer ||
                  barn.votingPower(proposal.proposer) < _getCreationThreshold()
              ) {
                  return true;
              }
              return false;
          }
          function _isCancellableState(uint256 proposalId) internal view returns (bool) {
              ProposalState s = state(proposalId);
              return s == ProposalState.WarmUp || s == ProposalState.Active;
          }
          function _isLiveState(uint256 proposalId) internal view returns (bool) {
              ProposalState s = state(proposalId);
              return s == ProposalState.WarmUp ||
              s == ProposalState.Active ||
              s == ProposalState.Accepted ||
              s == ProposalState.Queued ||
              s == ProposalState.Grace;
          }
          function _canBeExecuted(uint256 proposalId) internal view returns (bool) {
              return state(proposalId) == ProposalState.Grace;
          }
          function _getMinForVotes(Proposal storage proposal) internal view returns (uint256) {
              return (proposal.forVotes + proposal.againstVotes).mul(proposal.parameters.acceptanceThreshold).div(100);
          }
          function _getCreationThreshold() internal view returns (uint256) {
              return barn.bondStaked().div(100);
          }
          // Returns the timestamp of the snapshot for a given proposal
          // If the current block's timestamp is equal to `proposal.createTime + warmUpDuration` then the state function
          // will return WarmUp as state which will prevent any vote to be cast which will gracefully avoid any flashloan attack
          function _getSnapshotTimestamp(Proposal storage proposal) internal view returns (uint256) {
              return proposal.createTime + proposal.parameters.warmUpDuration;
          }
          function _getQuorum(Proposal storage proposal) internal view returns (uint256) {
              return barn.bondStakedAtTs(_getSnapshotTimestamp(proposal)).mul(proposal.parameters.minQuorum).div(100);
          }
          function _proposalAbrogated(uint256 proposalId) internal view returns (bool) {
              Proposal storage p = proposals[proposalId];
              AbrogationProposal storage cp = abrogationProposals[proposalId];
              if (cp.createTime == 0 || block.timestamp < p.eta) {
                  return false;
              }
              return cp.forVotes >= barn.bondStakedAtTs(cp.createTime - 1).div(2);
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      interface IBarn {
          struct Stake {
              uint256 timestamp;
              uint256 amount;
              uint256 expiryTimestamp;
              address delegatedTo;
          }
          // deposit allows a user to add more bond to his staked balance
          function deposit(uint256 amount) external;
          // withdraw allows a user to withdraw funds if the balance is not locked
          function withdraw(uint256 amount) external;
          // lock a user's currently staked balance until timestamp & add the bonus to his voting power
          function lock(uint256 timestamp) external;
          // delegate allows a user to delegate his voting power to another user
          function delegate(address to) external;
          // stopDelegate allows a user to take back the delegated voting power
          function stopDelegate() external;
          // balanceOf returns the current BOND balance of a user (bonus not included)
          function balanceOf(address user) external view returns (uint256);
          // balanceAtTs returns the amount of BOND that the user currently staked (bonus NOT included)
          function balanceAtTs(address user, uint256 timestamp) external view returns (uint256);
          // stakeAtTs returns the Stake object of the user that was valid at `timestamp`
          function stakeAtTs(address user, uint256 timestamp) external view returns (Stake memory);
          // votingPower returns the voting power (bonus included) + delegated voting power for a user at the current block
          function votingPower(address user) external view returns (uint256);
          // votingPowerAtTs returns the voting power (bonus included) + delegated voting power for a user at a point in time
          function votingPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // bondStaked returns the total raw amount of BOND staked at the current block
          function bondStaked() external view returns (uint256);
          // bondStakedAtTs returns the total raw amount of BOND users have deposited into the contract
          // it does not include any bonus
          function bondStakedAtTs(uint256 timestamp) external view returns (uint256);
          // delegatedPower returns the total voting power that a user received from other users
          function delegatedPower(address user) external view returns (uint256);
          // delegatedPowerAtTs returns the total voting power that a user received from other users at a point in time
          function delegatedPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // multiplierAtTs calculates the multiplier at a given timestamp based on the user's stake a the given timestamp
          // it includes the decay mechanism
          function multiplierAtTs(address user, uint256 timestamp) external view returns (uint256);
          // userLockedUntil returns the timestamp until the user's balance is locked
          function userLockedUntil(address user) external view returns (uint256);
          // userDidDelegate returns the address to which a user delegated their voting power; address(0) if not delegated
          function userDelegatedTo(address user) external view returns (address);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      import "./Parameters.sol";
      abstract contract Bridge is Parameters {
          mapping(bytes32 => bool) public queuedTransactions;
          function queueTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 eta) internal returns (bytes32) {
              bytes32 txHash = _getTxHash(target, value, signature, data, eta);
              queuedTransactions[txHash] = true;
              return txHash;
          }
          function cancelTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 eta) internal {
              bytes32 txHash = _getTxHash(target, value, signature, data, eta);
              queuedTransactions[txHash] = false;
          }
          function executeTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 eta) internal returns (bytes memory) {
              bytes32 txHash = _getTxHash(target, value, signature, data, eta);
              require(block.timestamp >= eta, "executeTransaction: Transaction hasn't surpassed time lock.");
              require(block.timestamp <= eta + gracePeriodDuration, "executeTransaction: Transaction is stale.");
              queuedTransactions[txHash] = false;
              bytes memory callData;
              if (bytes(signature).length == 0) {
                  callData = data;
              } else {
                  callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
              }
              // solium-disable-next-line security/no-call-value
              (bool success, bytes memory returnData) = target.call{value : value}(callData);
              require(success, string(returnData));
              return returnData;
          }
          function _getTxHash(address target, uint256 value, string memory signature, bytes memory data, uint256 eta) internal returns (bytes32) {
              return keccak256(abi.encode(target, value, signature, data, eta));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.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, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              uint256 c = a + b;
              if (c < a) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the substraction of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b > a) return (false, 0);
              return (true, a - b);
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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-contracts/pull/522
              if (a == 0) return (true, 0);
              uint256 c = a * b;
              if (c / a != b) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a / b);
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a % b);
          }
          /**
           * @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");
              return a - b;
          }
          /**
           * @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) {
              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, reverting 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) {
              require(b > 0, "SafeMath: division by zero");
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting 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;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {trySub}.
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              return a - b;
          }
          /**
           * @dev Returns the integer division of two unsigned integers, reverting with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryDiv}.
           *
           * 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, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting with custom message when dividing by zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryMod}.
           *
           * 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, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              return a % b;
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      abstract contract Parameters {
          uint256 public warmUpDuration = 4 days;
          uint256 public activeDuration = 4 days;
          uint256 public queueDuration = 4 days;
          uint256 public gracePeriodDuration = 4 days;
          uint256 public acceptanceThreshold = 60;
          uint256 public minQuorum = 45;
          uint256 constant ACTIVATION_THRESHOLD = 26_000_000*10**18;
          uint256 constant PROPOSAL_MAX_ACTIONS = 10;
          modifier onlyDAO () {
              require(msg.sender == address(this), "Only DAO can call");
              _;
          }
          function setWarmUpDuration(uint256 period) public onlyDAO {
              warmUpDuration = period;
          }
          function setActiveDuration(uint256 period) public onlyDAO {
              require(period >= 4 hours, "period must be > 0");
              activeDuration = period;
          }
          function setQueueDuration(uint256 period) public onlyDAO {
              queueDuration = period;
          }
          function setGracePeriodDuration(uint256 period) public onlyDAO {
              require(period >= 4 hours, "period must be > 0");
              gracePeriodDuration = period;
          }
          function setAcceptanceThreshold(uint256 threshold) public onlyDAO {
              require(threshold <= 100, "Maximum is 100.");
              require(threshold > 50, "Minimum is 50.");
              acceptanceThreshold = threshold;
          }
          function setMinQuorum(uint256 quorum) public onlyDAO {
              require(quorum > 5, "quorum must be greater than 5");
              require(quorum <= 100, "Maximum is 100.");
              minQuorum = quorum;
          }
      }
      

      File 2 of 3: Barn
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "./interfaces/IDiamondCut.sol";
      import "./interfaces/IDiamondLoupe.sol";
      import "./libraries/LibDiamond.sol";
      import "./libraries/LibOwnership.sol";
      import "./libraries/LibDiamondStorage.sol";
      import "./interfaces/IERC165.sol";
      import "./interfaces/IERC173.sol";
      contract Barn {
          constructor(IDiamondCut.FacetCut[] memory _diamondCut, address _owner) payable {
              require(_owner != address(0), "owner must not be 0x0");
              LibDiamond.diamondCut(_diamondCut, address(0), new bytes(0));
              LibOwnership.setContractOwner(_owner);
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              // adding ERC165 data
              ds.supportedInterfaces[type(IERC165).interfaceId] = true;
              ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
              ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
              ds.supportedInterfaces[type(IERC173).interfaceId] = true;
          }
          // Find facet for function that is called and execute the
          // function if a facet is found and return any value.
          fallback() external payable {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              address facet = address(bytes20(ds.facets[msg.sig].facetAddress));
              require(facet != address(0), "Diamond: Function does not exist");
              assembly {
                  calldatacopy(0, 0, calldatasize())
                  let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
                  returndatacopy(0, 0, returndatasize())
                  switch result
                  case 0 {
                      revert(0, returndatasize())
                  }
                  default {
                      return (0, returndatasize())
                  }
              }
          }
          receive() external payable {}
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      interface IDiamondCut {
          enum FacetCutAction {Add, Replace, Remove}
          // Add=0, Replace=1, Remove=2
          struct FacetCut {
              address facetAddress;
              FacetCutAction action;
              bytes4[] functionSelectors;
          }
          /// @notice Add/replace/remove any number of functions and optionally execute
          ///         a function with delegatecall
          /// @param _diamondCut Contains the facet addresses and function selectors
          /// @param _init The address of the contract or facet to execute _calldata
          /// @param _calldata A function call, including function selector and arguments
          ///                  _calldata is executed with delegatecall on _init
          function diamondCut(
              FacetCut[] calldata _diamondCut,
              address _init,
              bytes calldata _calldata
          ) external;
          event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      // A loupe is a small magnifying glass used to look at diamonds.
      // These functions look at diamonds
      interface IDiamondLoupe {
          /// These functions are expected to be called frequently
          /// by tools.
          struct Facet {
              address facetAddress;
              bytes4[] functionSelectors;
          }
          /// @notice Gets all facet addresses and their four byte function selectors.
          /// @return facets_ Facet
          function facets() external view returns (Facet[] memory facets_);
          /// @notice Gets all the function selectors supported by a specific facet.
          /// @param _facet The facet address.
          /// @return facetFunctionSelectors_
          function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);
          /// @notice Get all the facet addresses used by a diamond.
          /// @return facetAddresses_
          function facetAddresses() external view returns (address[] memory facetAddresses_);
          /// @notice Gets the facet that supports the given selector.
          /// @dev If facet is not found return address(0).
          /// @param _functionSelector The function selector.
          /// @return facetAddress_ The facet address.
          function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IDiamondCut.sol";
      import "./LibDiamondStorage.sol";
      library LibDiamond {
          event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
          // Internal function version of diamondCut
          // This code is almost the same as the external diamondCut,
          // except it is using 'Facet[] memory _diamondCut' instead of
          // 'Facet[] calldata _diamondCut'.
          // The code is duplicated to prevent copying calldata to memory which
          // causes an error for a two dimensional array.
          function diamondCut(
              IDiamondCut.FacetCut[] memory _diamondCut,
              address _init,
              bytes memory _calldata
          ) internal {
              uint256 selectorCount = LibDiamondStorage.diamondStorage().selectors.length;
              for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
                  selectorCount = executeDiamondCut(selectorCount, _diamondCut[facetIndex]);
              }
              emit DiamondCut(_diamondCut, _init, _calldata);
              initializeDiamondCut(_init, _calldata);
          }
          // executeDiamondCut takes one single FacetCut action and executes it
          // if FacetCutAction can't be identified, it reverts
          function executeDiamondCut(uint256 selectorCount, IDiamondCut.FacetCut memory cut) internal returns (uint256) {
              require(cut.functionSelectors.length > 0, "LibDiamond: No selectors in facet to cut");
              if (cut.action == IDiamondCut.FacetCutAction.Add) {
                  require(cut.facetAddress != address(0), "LibDiamond: add facet address can't be address(0)");
                  enforceHasContractCode(cut.facetAddress, "LibDiamond: add facet must have code");
                  return _handleAddCut(selectorCount, cut);
              }
              if (cut.action == IDiamondCut.FacetCutAction.Replace) {
                  require(cut.facetAddress != address(0), "LibDiamond: remove facet address can't be address(0)");
                  enforceHasContractCode(cut.facetAddress, "LibDiamond: remove facet must have code");
                  return _handleReplaceCut(selectorCount, cut);
              }
              if (cut.action == IDiamondCut.FacetCutAction.Remove) {
                  require(cut.facetAddress == address(0), "LibDiamond: remove facet address must be address(0)");
                  return _handleRemoveCut(selectorCount, cut);
              }
              revert("LibDiamondCut: Incorrect FacetCutAction");
          }
          // _handleAddCut executes a cut with the type Add
          // it reverts if the selector already exists
          function _handleAddCut(uint256 selectorCount, IDiamondCut.FacetCut memory cut) internal returns (uint256) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              for (uint256 selectorIndex; selectorIndex < cut.functionSelectors.length; selectorIndex++) {
                  bytes4 selector = cut.functionSelectors[selectorIndex];
                  address oldFacetAddress = ds.facets[selector].facetAddress;
                  require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
                  ds.facets[selector] = LibDiamondStorage.Facet(
                      cut.facetAddress,
                      uint16(selectorCount)
                  );
                  ds.selectors.push(selector);
                  selectorCount++;
              }
              return selectorCount;
          }
          // _handleReplaceCut executes a cut with the type Replace
          // it does not allow replacing immutable functions
          // it does not allow replacing with the same function
          // it does not allow replacing a function that does not exist
          function _handleReplaceCut(uint256 selectorCount, IDiamondCut.FacetCut memory cut) internal returns (uint256) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              for (uint256 selectorIndex; selectorIndex < cut.functionSelectors.length; selectorIndex++) {
                  bytes4 selector = cut.functionSelectors[selectorIndex];
                  address oldFacetAddress = ds.facets[selector].facetAddress;
                  // only useful if immutable functions exist
                  require(oldFacetAddress != address(this), "LibDiamondCut: Can't replace immutable function");
                  require(oldFacetAddress != cut.facetAddress, "LibDiamondCut: Can't replace function with same function");
                  require(oldFacetAddress != address(0), "LibDiamondCut: Can't replace function that doesn't exist");
                  // replace old facet address
                  ds.facets[selector].facetAddress = cut.facetAddress;
              }
              return selectorCount;
          }
          // _handleRemoveCut executes a cut with the type Remove
          // for efficiency, the selector to be deleted is replaced with the last one and then the last one is popped
          // it reverts if the function doesn't exist or it's immutable
          function _handleRemoveCut(uint256 selectorCount, IDiamondCut.FacetCut memory cut) internal returns (uint256) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              for (uint256 selectorIndex; selectorIndex < cut.functionSelectors.length; selectorIndex++) {
                  bytes4 selector = cut.functionSelectors[selectorIndex];
                  LibDiamondStorage.Facet memory oldFacet = ds.facets[selector];
                  require(oldFacet.facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
                  require(oldFacet.facetAddress != address(this), "LibDiamondCut: Can't remove immutable function.");
                  // replace selector with last selector
                  if (oldFacet.selectorPosition != selectorCount - 1) {
                      bytes4 lastSelector = ds.selectors[selectorCount - 1];
                      ds.selectors[oldFacet.selectorPosition] = lastSelector;
                      ds.facets[lastSelector].selectorPosition = oldFacet.selectorPosition;
                  }
                  // delete last selector
                  ds.selectors.pop();
                  delete ds.facets[selector];
                  selectorCount--;
              }
              return selectorCount;
          }
          function initializeDiamondCut(address _init, bytes memory _calldata) internal {
              if (_init == address(0)) {
                  require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but _calldata is not empty");
                  return;
              }
              require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)");
              if (_init != address(this)) {
                  enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
              }
              (bool success, bytes memory error) = _init.delegatecall(_calldata);
              if (!success) {
                  if (error.length > 0) {
                      // bubble up the error
                      revert(string(error));
                  } else {
                      revert("LibDiamondCut: _init function reverted");
                  }
              }
          }
          function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
              uint256 contractSize;
              assembly {
                  contractSize := extcodesize(_contract)
              }
              require(contractSize > 0, _errorMessage);
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "./LibDiamondStorage.sol";
      library LibOwnership {
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          function setContractOwner(address _newOwner) internal {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              address previousOwner = ds.contractOwner;
              require(previousOwner != _newOwner, "Previous owner and new owner must be different");
              ds.contractOwner = _newOwner;
              emit OwnershipTransferred(previousOwner, _newOwner);
          }
          function contractOwner() internal view returns (address contractOwner_) {
              contractOwner_ = LibDiamondStorage.diamondStorage().contractOwner;
          }
          function enforceIsContractOwner() view internal {
              require(msg.sender == LibDiamondStorage.diamondStorage().contractOwner, "Must be contract owner");
          }
          modifier onlyOwner {
              require(msg.sender == LibDiamondStorage.diamondStorage().contractOwner, "Must be contract owner");
              _;
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      library LibDiamondStorage {
          bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
          struct Facet {
              address facetAddress;
              uint16 selectorPosition;
          }
          struct DiamondStorage {
              // function selector => facet address and selector position in selectors array
              mapping(bytes4 => Facet) facets;
              bytes4[] selectors;
              // ERC165
              mapping(bytes4 => bool) supportedInterfaces;
              // owner of the contract
              address contractOwner;
          }
          function diamondStorage() internal pure returns (DiamondStorage storage ds) {
              bytes32 position = DIAMOND_STORAGE_POSITION;
              assembly {
                  ds.slot := position
              }
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      interface IERC165 {
          /// @notice Query if a contract implements an interface
          /// @param interfaceId The interface identifier, as specified in ERC-165
          /// @dev Interface identification is specified in ERC-165. This function
          ///  uses less than 30,000 gas.
          /// @return `true` if the contract implements `interfaceID` and
          ///  `interfaceID` is not 0xffffffff, `false` otherwise
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      /// @title ERC-173 Contract Ownership Standard
      ///  Note: the ERC-165 identifier for this interface is 0x7f5828d0
      /* is ERC165 */
      interface IERC173 {
          /// @dev This emits when ownership of a contract changes.
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /// @notice Get the address of the owner
          /// @return owner_ The address of the owner.
          function owner() external view returns (address owner_);
          /// @notice Set the address of the new owner of the contract
          /// @dev Set _newOwner to address(0) to renounce any ownership.
          /// @param _newOwner The address of the new owner of the contract
          function transferOwnership(address _newOwner) external;
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      import "../libraries/LibOwnership.sol";
      import "../interfaces/IERC173.sol";
      contract OwnershipFacet is IERC173 {
          function transferOwnership(address _newOwner) external override {
              LibOwnership.enforceIsContractOwner();
              LibOwnership.setContractOwner(_newOwner);
          }
          function owner() external override view returns (address owner_) {
              owner_ = LibOwnership.contractOwner();
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IDiamondCut.sol";
      import "../libraries/LibDiamond.sol";
      import "../libraries/LibOwnership.sol";
      contract DiamondCutFacet is IDiamondCut {
          /// @notice Add/replace/remove any number of functions and optionally execute
          ///         a function with delegatecall
          /// @param _diamondCut Contains the facet addresses and function selectors
          /// @param _init The address of the contract or facet to execute _calldata
          /// @param _calldata A function call, including function selector and arguments
          ///                  _calldata is executed with delegatecall on _init
          function diamondCut(
              FacetCut[] calldata _diamondCut,
              address _init,
              bytes calldata _calldata
          ) external override {
              LibOwnership.enforceIsContractOwner();
              uint256 selectorCount = LibDiamondStorage.diamondStorage().selectors.length;
              for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
                  FacetCut memory cut;
                  cut.action = _diamondCut[facetIndex].action;
                  cut.facetAddress = _diamondCut[facetIndex].facetAddress;
                  cut.functionSelectors = _diamondCut[facetIndex].functionSelectors;
                  selectorCount = LibDiamond.executeDiamondCut(selectorCount, cut);
              }
              emit DiamondCut(_diamondCut, _init, _calldata);
              LibDiamond.initializeDiamondCut(_init, _calldata);
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../libraries/LibDiamondStorage.sol";
      import "../interfaces/IDiamondLoupe.sol";
      import "../interfaces/IERC165.sol";
      contract DiamondLoupeFacet is IDiamondLoupe, IERC165 {
          // Diamond Loupe Functions
          ////////////////////////////////////////////////////////////////////
          /// These functions are expected to be called frequently by tools.
          //
          // struct Facet {
          //     address facetAddress;
          //     bytes4[] functionSelectors;
          // }
          /// @notice Gets all facets and their selectors.
          /// @return facets_ Facet
          function facets() external override view returns (Facet[] memory facets_) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              uint256 selectorCount = ds.selectors.length;
              // create an array set to the maximum size possible
              facets_ = new Facet[](selectorCount);
              // create an array for counting the number of selectors for each facet
              uint8[] memory numFacetSelectors = new uint8[](selectorCount);
              // total number of facets
              uint256 numFacets;
              // loop through function selectors
              for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
                  bytes4 selector = ds.selectors[selectorIndex];
                  address facetAddress_ = ds.facets[selector].facetAddress;
                  bool continueLoop = false;
                  // find the functionSelectors array for selector and add selector to it
                  for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
                      if (facets_[facetIndex].facetAddress == facetAddress_) {
                          facets_[facetIndex].functionSelectors[numFacetSelectors[facetIndex]] = selector;
                          // probably will never have more than 256 functions from one facet contract
                          require(numFacetSelectors[facetIndex] < 255);
                          numFacetSelectors[facetIndex]++;
                          continueLoop = true;
                          break;
                      }
                  }
                  // if functionSelectors array exists for selector then continue loop
                  if (continueLoop) {
                      continueLoop = false;
                      continue;
                  }
                  // create a new functionSelectors array for selector
                  facets_[numFacets].facetAddress = facetAddress_;
                  facets_[numFacets].functionSelectors = new bytes4[](selectorCount);
                  facets_[numFacets].functionSelectors[0] = selector;
                  numFacetSelectors[numFacets] = 1;
                  numFacets++;
              }
              for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
                  uint256 numSelectors = numFacetSelectors[facetIndex];
                  bytes4[] memory selectors = facets_[facetIndex].functionSelectors;
                  // setting the number of selectors
                  assembly {
                      mstore(selectors, numSelectors)
                  }
              }
              // setting the number of facets
              assembly {
                  mstore(facets_, numFacets)
              }
          }
          /// @notice Gets all the function selectors supported by a specific facet.
          /// @param _facet The facet address.
          /// @return _facetFunctionSelectors The selectors associated with a facet address.
          function facetFunctionSelectors(address _facet) external override view returns (bytes4[] memory _facetFunctionSelectors) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              uint256 selectorCount = ds.selectors.length;
              uint256 numSelectors;
              _facetFunctionSelectors = new bytes4[](selectorCount);
              // loop through function selectors
              for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
                  bytes4 selector = ds.selectors[selectorIndex];
                  address facetAddress_ = ds.facets[selector].facetAddress;
                  if (_facet == facetAddress_) {
                      _facetFunctionSelectors[numSelectors] = selector;
                      numSelectors++;
                  }
              }
              // Set the number of selectors in the array
              assembly {
                  mstore(_facetFunctionSelectors, numSelectors)
              }
          }
          /// @notice Get all the facet addresses used by a diamond.
          /// @return facetAddresses_
          function facetAddresses() external override view returns (address[] memory facetAddresses_) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              uint256 selectorCount = ds.selectors.length;
              // create an array set to the maximum size possible
              facetAddresses_ = new address[](selectorCount);
              uint256 numFacets;
              // loop through function selectors
              for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
                  bytes4 selector = ds.selectors[selectorIndex];
                  address facetAddress_ = ds.facets[selector].facetAddress;
                  bool continueLoop = false;
                  // see if we have collected the address already and break out of loop if we have
                  for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
                      if (facetAddress_ == facetAddresses_[facetIndex]) {
                          continueLoop = true;
                          break;
                      }
                  }
                  // continue loop if we already have the address
                  if (continueLoop) {
                      continueLoop = false;
                      continue;
                  }
                  // include address
                  facetAddresses_[numFacets] = facetAddress_;
                  numFacets++;
              }
              // Set the number of facet addresses in the array
              assembly {
                  mstore(facetAddresses_, numFacets)
              }
          }
          /// @notice Gets the facet address that supports the given selector.
          /// @dev If facet is not found return address(0).
          /// @param _functionSelector The function selector.
          /// @return facetAddress_ The facet address.
          function facetAddress(bytes4 _functionSelector) external override view returns (address facetAddress_) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              facetAddress_ = ds.facets[_functionSelector].facetAddress;
          }
          // This implements ERC-165.
          function supportsInterface(bytes4 _interfaceId) external override view returns (bool) {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              return ds.supportedInterfaces[_interfaceId];
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../libraries/LibBarnStorage.sol";
      import "../libraries/LibOwnership.sol";
      contract ChangeRewardsFacet {
          function changeRewardsAddress(address _rewards) public {
              LibOwnership.enforceIsContractOwner();
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              ds.rewards = IRewards(_rewards);
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "../interfaces/IRewards.sol";
      library LibBarnStorage {
          bytes32 constant STORAGE_POSITION = keccak256("com.barnbridge.barn.storage");
          struct Checkpoint {
              uint256 timestamp;
              uint256 amount;
          }
          struct Stake {
              uint256 timestamp;
              uint256 amount;
              uint256 expiryTimestamp;
              address delegatedTo;
          }
          struct Storage {
              bool initialized;
              // mapping of user address to history of Stake objects
              // every user action creates a new object in the history
              mapping(address => Stake[]) userStakeHistory;
              // array of bond staked Checkpoint
              // deposits/withdrawals create a new object in the history (max one per block)
              Checkpoint[] bondStakedHistory;
              // mapping of user address to history of delegated power
              // every delegate/stopDelegate call create a new checkpoint (max one per block)
              mapping(address => Checkpoint[]) delegatedPowerHistory;
              IERC20 bond;
              IRewards rewards;
          }
          function barnStorage() internal pure returns (Storage storage ds) {
              bytes32 position = STORAGE_POSITION;
              assembly {
                  ds.slot := position
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      interface IRewards {
          function registerUserAction(address user) external;
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      import "../interfaces/IRewards.sol";
      contract BarnMock {
          IRewards public r;
          uint256 public bondStaked;
          mapping(address => uint256) private balances;
          function setRewards(address rewards) public {
              r = IRewards(rewards);
          }
          function callRegisterUserAction(address user) public {
              return r.registerUserAction(user);
          }
          function deposit(address user, uint256 amount) public {
              callRegisterUserAction(user);
              balances[user] = balances[user] + amount;
              bondStaked = bondStaked + amount;
          }
          function withdraw(address user, uint256 amount) public {
              require(balances[user] >= amount, "insufficient balance");
              callRegisterUserAction(user);
              balances[user] = balances[user] - amount;
              bondStaked = bondStaked - amount;
          }
          function balanceOf(address user) public view returns (uint256) {
              return balances[user];
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "./interfaces/IBarn.sol";
      contract Rewards is Ownable {
          using SafeMath for uint256;
          uint256 constant decimals = 10 ** 18;
          struct Pull {
              address source;
              uint256 startTs;
              uint256 endTs;
              uint256 totalDuration;
              uint256 totalAmount;
          }
          Pull public pullFeature;
          bool public disabled;
          uint256 public lastPullTs;
          uint256 public balanceBefore;
          uint256 public currentMultiplier;
          mapping(address => uint256) public userMultiplier;
          mapping(address => uint256) public owed;
          IBarn public barn;
          IERC20 public rewardToken;
          event Claim(address indexed user, uint256 amount);
          constructor(address _owner, address _token, address _barn) {
              require(_token != address(0), "reward token must not be 0x0");
              require(_barn != address(0), "barn address must not be 0x0");
              transferOwnership(_owner);
              rewardToken = IERC20(_token);
              barn = IBarn(_barn);
          }
          // registerUserAction is called by the Barn every time the user does a deposit or withdrawal in order to
          // account for the changes in reward that the user should get
          // it updates the amount owed to the user without transferring the funds
          function registerUserAction(address user) public {
              require(msg.sender == address(barn), 'only callable by barn');
              _calculateOwed(user);
          }
          // claim calculates the currently owed reward and transfers the funds to the user
          function claim() public returns (uint256){
              _calculateOwed(msg.sender);
              uint256 amount = owed[msg.sender];
              require(amount > 0, "nothing to claim");
              owed[msg.sender] = 0;
              rewardToken.transfer(msg.sender, amount);
              // acknowledge the amount that was transferred to the user
              ackFunds();
              emit Claim(msg.sender, amount);
              return amount;
          }
          // ackFunds checks the difference between the last known balance of `token` and the current one
          // if it goes up, the multiplier is re-calculated
          // if it goes down, it only updates the known balance
          function ackFunds() public {
              uint256 balanceNow = rewardToken.balanceOf(address(this));
              if (balanceNow == 0 || balanceNow <= balanceBefore) {
                  balanceBefore = balanceNow;
                  return;
              }
              uint256 totalStakedBond = barn.bondStaked();
              // if there's no bond staked, it doesn't make sense to ackFunds because there's nobody to distribute them to
              // and the calculation would fail anyways due to division by 0
              if (totalStakedBond == 0) {
                  return;
              }
              uint256 diff = balanceNow.sub(balanceBefore);
              uint256 multiplier = currentMultiplier.add(diff.mul(decimals).div(totalStakedBond));
              balanceBefore = balanceNow;
              currentMultiplier = multiplier;
          }
          // setupPullToken is used to setup the rewards system; only callable by contract owner
          // set source to address(0) to disable the functionality
          function setupPullToken(address source, uint256 startTs, uint256 endTs, uint256 amount) public {
              require(msg.sender == owner(), "!owner");
              require(!disabled, "contract is disabled");
              if (pullFeature.source != address(0)) {
                  require(source == address(0), "contract is already set up, source must be 0x0");
                  disabled = true;
              } else {
                  require(source != address(0), "contract is not setup, source must be != 0x0");
              }
              if (source == address(0)) {
                  require(startTs == 0, "disable contract: startTs must be 0");
                  require(endTs == 0, "disable contract: endTs must be 0");
                  require(amount == 0, "disable contract: amount must be 0");
              } else {
                  require(endTs > startTs, "setup contract: endTs must be greater than startTs");
                  require(amount > 0, "setup contract: amount must be greater than 0");
              }
              pullFeature.source = source;
              pullFeature.startTs = startTs;
              pullFeature.endTs = endTs;
              pullFeature.totalDuration = endTs.sub(startTs);
              pullFeature.totalAmount = amount;
              if (lastPullTs < startTs) {
                  lastPullTs = startTs;
              }
          }
          // setBarn sets the address of the BarnBridge Barn into the state variable
          function setBarn(address _barn) public {
              require(_barn != address(0), 'barn address must not be 0x0');
              require(msg.sender == owner(), '!owner');
              barn = IBarn(_barn);
          }
          // _pullToken calculates the amount based on the time passed since the last pull relative
          // to the total amount of time that the pull functionality is active and executes a transferFrom from the
          // address supplied as `pullTokenFrom`, if enabled
          function _pullToken() internal {
              if (
                  pullFeature.source == address(0) ||
                  block.timestamp < pullFeature.startTs
              ) {
                  return;
              }
              uint256 timestampCap = pullFeature.endTs;
              if (block.timestamp < pullFeature.endTs) {
                  timestampCap = block.timestamp;
              }
              if (lastPullTs >= timestampCap) {
                  return;
              }
              uint256 timeSinceLastPull = timestampCap.sub(lastPullTs);
              uint256 shareToPull = timeSinceLastPull.mul(decimals).div(pullFeature.totalDuration);
              uint256 amountToPull = pullFeature.totalAmount.mul(shareToPull).div(decimals);
              lastPullTs = block.timestamp;
              rewardToken.transferFrom(pullFeature.source, address(this), amountToPull);
          }
          // _calculateOwed calculates and updates the total amount that is owed to an user and updates the user's multiplier
          // to the current value
          // it automatically attempts to pull the token from the source and acknowledge the funds
          function _calculateOwed(address user) internal {
              _pullToken();
              ackFunds();
              uint256 reward = _userPendingReward(user);
              owed[user] = owed[user].add(reward);
              userMultiplier[user] = currentMultiplier;
          }
          // _userPendingReward calculates the reward that should be based on the current multiplier / anything that's not included in the `owed[user]` value
          // it does not represent the entire reward that's due to the user unless added on top of `owed[user]`
          function _userPendingReward(address user) internal view returns (uint256) {
              uint256 multiplier = currentMultiplier.sub(userMultiplier[user]);
              return barn.balanceOf(user).mul(multiplier).div(decimals);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      import "../GSN/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.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * 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.
       */
      abstract 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 () {
              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(_owner == _msgSender(), "Ownable: caller is not the 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 virtual 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 virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.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) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              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-contracts/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) {
              return div(a, b, "SafeMath: division by zero");
          }
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              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) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../libraries/LibBarnStorage.sol";
      interface IBarn {
          // deposit allows a user to add more bond to his staked balance
          function deposit(uint256 amount) external;
          // withdraw allows a user to withdraw funds if the balance is not locked
          function withdraw(uint256 amount) external;
          // lock a user's currently staked balance until timestamp & add the bonus to his voting power
          function lock(uint256 timestamp) external;
          // delegate allows a user to delegate his voting power to another user
          function delegate(address to) external;
          // stopDelegate allows a user to take back the delegated voting power
          function stopDelegate() external;
          // lock the balance of a proposal creator until the voting ends; only callable by DAO
          function lockCreatorBalance(address user, uint256 timestamp) external;
          // balanceOf returns the current BOND balance of a user (bonus not included)
          function balanceOf(address user) external view returns (uint256);
          // balanceAtTs returns the amount of BOND that the user currently staked (bonus NOT included)
          function balanceAtTs(address user, uint256 timestamp) external view returns (uint256);
          // stakeAtTs returns the Stake object of the user that was valid at `timestamp`
          function stakeAtTs(address user, uint256 timestamp) external view returns (LibBarnStorage.Stake memory);
          // votingPower returns the voting power (bonus included) + delegated voting power for a user at the current block
          function votingPower(address user) external view returns (uint256);
          // votingPowerAtTs returns the voting power (bonus included) + delegated voting power for a user at a point in time
          function votingPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // bondStaked returns the total raw amount of BOND staked at the current block
          function bondStaked() external view returns (uint256);
          // bondStakedAtTs returns the total raw amount of BOND users have deposited into the contract
          // it does not include any bonus
          function bondStakedAtTs(uint256 timestamp) external view returns (uint256);
          // delegatedPower returns the total voting power that a user received from other users
          function delegatedPower(address user) external view returns (uint256);
          // delegatedPowerAtTs returns the total voting power that a user received from other users at a point in time
          function delegatedPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // multiplierAtTs calculates the multiplier at a given timestamp based on the user's stake a the given timestamp
          // it includes the decay mechanism
          function multiplierAtTs(address user, uint256 timestamp) external view returns (uint256);
          // userLockedUntil returns the timestamp until the user's balance is locked
          function userLockedUntil(address user) external view returns (uint256);
          // userDidDelegate returns the address to which a user delegated their voting power; address(0) if not delegated
          function userDelegatedTo(address user) external view returns (address);
          // bondCirculatingSupply returns the current circulating supply of BOND
          function bondCirculatingSupply() external view returns (uint256);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.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.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address payable) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      import "../interfaces/IBarn.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      contract MulticallMock {
          using SafeMath for uint256;
          IBarn barn;
          IERC20 bond;
          constructor(address _barn, address _bond) {
              barn = IBarn(_barn);
              bond = IERC20(_bond);
          }
          function multiDelegate(uint256 amount, address user1, address user2) public {
              bond.approve(address(barn), amount);
              barn.deposit(amount);
              barn.delegate(user1);
              barn.delegate(user2);
              barn.delegate(user1);
          }
          function multiDeposit(uint256 amount) public {
              bond.approve(address(barn), amount.mul(3));
              barn.deposit(amount);
              barn.deposit(amount);
              barn.deposit(amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      import "../../GSN/Context.sol";
      import "./IERC20.sol";
      import "../../math/SafeMath.sol";
      import "../../utils/Address.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin guidelines: functions revert instead
       * of returning `false` on failure. This behavior is nonetheless conventional
       * and does not conflict with the expectations of ERC20 applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is Context, IERC20 {
          using SafeMath for uint256;
          using Address for address;
          mapping (address => uint256) private _balances;
          mapping (address => mapping (address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          uint8 private _decimals;
          /**
           * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
           * a default value of 18.
           *
           * To select a different value for {decimals}, use {_setupDecimals}.
           *
           * All three of these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
              _decimals = 18;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
           * called.
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view returns (uint8) {
              return _decimals;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20};
           *
           * Requirements:
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for ``sender``'s tokens of at least
           * `amount`.
           */
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
              return true;
          }
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(address sender, address recipient, uint256 amount) internal virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(sender, recipient, amount);
              _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements
           *
           * - `to` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Sets {decimals} to a value other than the default one of 18.
           *
           * WARNING: This function should only be called from the constructor. Most
           * applications that interact with token contracts will not expect
           * {decimals} to ever change, and may work incorrectly if it does.
           */
          function _setupDecimals(uint8 decimals_) internal {
              _decimals = decimals_;
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be to transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
              // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
              // for accounts without code, i.e. `keccak256('')`
              bytes32 codehash;
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly { codehash := extcodehash(account) }
              return (codehash != accountHash && codehash != 0x0);
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              return _functionCallWithValue(target, data, value, errorMessage);
          }
          function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
      contract ERC20Mock is ERC20("ERC20Mock", "MCK") {
          bool public transferFromCalled = false;
          bool public transferCalled = false;
          address public transferRecipient = address(0);
          uint256 public transferAmount = 0;
          function mint(address user, uint256 amount) public {
              _mint(user, amount);
          }
          function burnFrom(address user, uint256 amount) public {
              _burn(user, amount);
          }
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              transferFromCalled = true;
              return super.transferFrom(sender, recipient, amount);
          }
          function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              transferCalled = true;
              transferRecipient = recipient;
              transferAmount = amount;
              return super.transfer(recipient, amount);
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IBarn.sol";
      import "../libraries/LibBarnStorage.sol";
      import "../libraries/LibOwnership.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      contract BarnFacet {
          using SafeMath for uint256;
          uint256 constant public MAX_LOCK = 365 days;
          uint256 constant BASE_MULTIPLIER = 1e18;
          event Deposit(address indexed user, uint256 amount, uint256 newBalance);
          event Withdraw(address indexed user, uint256 amountWithdrew, uint256 amountLeft);
          event Lock(address indexed user, uint256 timestamp);
          event Delegate(address indexed from, address indexed to);
          event DelegatedPowerIncreased(address indexed from, address indexed to, uint256 amount, uint256 to_newDelegatedPower);
          event DelegatedPowerDecreased(address indexed from, address indexed to, uint256 amount, uint256 to_newDelegatedPower);
          function initBarn(address _bond, address _rewards) public {
              require(_bond != address(0), "BOND address must not be 0x0");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              require(!ds.initialized, "Barn: already initialized");
              LibOwnership.enforceIsContractOwner();
              ds.initialized = true;
              ds.bond = IERC20(_bond);
              ds.rewards = IRewards(_rewards);
          }
          // deposit allows a user to add more bond to his staked balance
          function deposit(uint256 amount) public {
              require(amount > 0, "Amount must be greater than 0");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              uint256 allowance = ds.bond.allowance(msg.sender, address(this));
              require(allowance >= amount, "Token allowance too small");
              // this must be called before the user's balance is updated so the rewards contract can calculate
              // the amount owed correctly
              if (address(ds.rewards) != address(0)) {
                  ds.rewards.registerUserAction(msg.sender);
              }
              uint256 newBalance = balanceOf(msg.sender).add(amount);
              _updateUserBalance(ds.userStakeHistory[msg.sender], newBalance);
              _updateLockedBond(bondStakedAtTs(block.timestamp).add(amount));
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).add(amount);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerIncreased(msg.sender, delegatedTo, amount, newDelegatedPower);
              }
              ds.bond.transferFrom(msg.sender, address(this), amount);
              emit Deposit(msg.sender, amount, newBalance);
          }
          // withdraw allows a user to withdraw funds if the balance is not locked
          function withdraw(uint256 amount) public {
              require(amount > 0, "Amount must be greater than 0");
              require(userLockedUntil(msg.sender) <= block.timestamp, "User balance is locked");
              uint256 balance = balanceOf(msg.sender);
              require(balance >= amount, "Insufficient balance");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              // this must be called before the user's balance is updated so the rewards contract can calculate
              // the amount owed correctly
              if (address(ds.rewards) != address(0)) {
                  ds.rewards.registerUserAction(msg.sender);
              }
              _updateUserBalance(ds.userStakeHistory[msg.sender], balance.sub(amount));
              _updateLockedBond(bondStakedAtTs(block.timestamp).sub(amount));
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).sub(amount);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerDecreased(msg.sender, delegatedTo, amount, newDelegatedPower);
              }
              ds.bond.transfer(msg.sender, amount);
              emit Withdraw(msg.sender, amount, balance.sub(amount));
          }
          // lock a user's currently staked balance until timestamp & add the bonus to his voting power
          function lock(uint256 timestamp) public {
              require(timestamp > block.timestamp, "Timestamp must be in the future");
              require(timestamp <= block.timestamp + MAX_LOCK, "Timestamp too big");
              require(balanceOf(msg.sender) > 0, "Sender has no balance");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              LibBarnStorage.Stake[] storage checkpoints = ds.userStakeHistory[msg.sender];
              LibBarnStorage.Stake storage currentStake = checkpoints[checkpoints.length - 1];
              require(timestamp > currentStake.expiryTimestamp, "New timestamp lower than current lock timestamp");
              _updateUserLock(checkpoints, timestamp);
              emit Lock(msg.sender, timestamp);
          }
          function depositAndLock(uint256 amount, uint256 timestamp) public {
              deposit(amount);
              lock(timestamp);
          }
          // delegate allows a user to delegate his voting power to another user
          function delegate(address to) public {
              require(msg.sender != to, "Can't delegate to self");
              uint256 senderBalance = balanceOf(msg.sender);
              require(senderBalance > 0, "No balance to delegate");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              emit Delegate(msg.sender, to);
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).sub(senderBalance);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerDecreased(msg.sender, delegatedTo, senderBalance, newDelegatedPower);
              }
              if (to != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(to).add(senderBalance);
                  _updateDelegatedPower(ds.delegatedPowerHistory[to], newDelegatedPower);
                  emit DelegatedPowerIncreased(msg.sender, to, senderBalance, newDelegatedPower);
              }
              _updateUserDelegatedTo(ds.userStakeHistory[msg.sender], to);
          }
          // stopDelegate allows a user to take back the delegated voting power
          function stopDelegate() public {
              return delegate(address(0));
          }
          // balanceOf returns the current BOND balance of a user (bonus not included)
          function balanceOf(address user) public view returns (uint256) {
              return balanceAtTs(user, block.timestamp);
          }
          // balanceAtTs returns the amount of BOND that the user currently staked (bonus NOT included)
          function balanceAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              return stake.amount;
          }
          // stakeAtTs returns the Stake object of the user that was valid at `timestamp`
          function stakeAtTs(address user, uint256 timestamp) public view returns (LibBarnStorage.Stake memory) {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              LibBarnStorage.Stake[] storage stakeHistory = ds.userStakeHistory[user];
              if (stakeHistory.length == 0 || timestamp < stakeHistory[0].timestamp) {
                  return LibBarnStorage.Stake(block.timestamp, 0, block.timestamp, address(0));
              }
              uint256 min = 0;
              uint256 max = stakeHistory.length - 1;
              if (timestamp >= stakeHistory[max].timestamp) {
                  return stakeHistory[max];
              }
              // binary search of the value in the array
              while (max > min) {
                  uint256 mid = (max + min + 1) / 2;
                  if (stakeHistory[mid].timestamp <= timestamp) {
                      min = mid;
                  } else {
                      max = mid - 1;
                  }
              }
              return stakeHistory[min];
          }
          // votingPower returns the voting power (bonus included) + delegated voting power for a user at the current block
          function votingPower(address user) public view returns (uint256) {
              return votingPowerAtTs(user, block.timestamp);
          }
          // votingPowerAtTs returns the voting power (bonus included) + delegated voting power for a user at a point in time
          function votingPowerAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              uint256 ownVotingPower;
              // if the user delegated his voting power to another user, then he doesn't have any voting power left
              if (stake.delegatedTo != address(0)) {
                  ownVotingPower = 0;
              } else {
                  uint256 balance = stake.amount;
                  uint256 multiplier = _stakeMultiplier(stake, timestamp);
                  ownVotingPower = balance.mul(multiplier).div(BASE_MULTIPLIER);
              }
              uint256 delegatedVotingPower = delegatedPowerAtTs(user, timestamp);
              return ownVotingPower.add(delegatedVotingPower);
          }
          // bondStaked returns the total raw amount of BOND staked at the current block
          function bondStaked() public view returns (uint256) {
              return bondStakedAtTs(block.timestamp);
          }
          // bondStakedAtTs returns the total raw amount of BOND users have deposited into the contract
          // it does not include any bonus
          function bondStakedAtTs(uint256 timestamp) public view returns (uint256) {
              return _checkpointsBinarySearch(LibBarnStorage.barnStorage().bondStakedHistory, timestamp);
          }
          // delegatedPower returns the total voting power that a user received from other users
          function delegatedPower(address user) public view returns (uint256) {
              return delegatedPowerAtTs(user, block.timestamp);
          }
          // delegatedPowerAtTs returns the total voting power that a user received from other users at a point in time
          function delegatedPowerAtTs(address user, uint256 timestamp) public view returns (uint256) {
              return _checkpointsBinarySearch(LibBarnStorage.barnStorage().delegatedPowerHistory[user], timestamp);
          }
          // same as multiplierAtTs but for the current block timestamp
          function multiplierOf(address user) public view returns (uint256) {
              return multiplierAtTs(user, block.timestamp);
          }
          // multiplierAtTs calculates the multiplier at a given timestamp based on the user's stake a the given timestamp
          // it includes the decay mechanism
          function multiplierAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              return _stakeMultiplier(stake, timestamp);
          }
          // userLockedUntil returns the timestamp until the user's balance is locked
          function userLockedUntil(address user) public view returns (uint256) {
              LibBarnStorage.Stake memory c = stakeAtTs(user, block.timestamp);
              return c.expiryTimestamp;
          }
          // userDelegatedTo returns the address to which a user delegated their voting power; address(0) if not delegated
          function userDelegatedTo(address user) public view returns (address) {
              LibBarnStorage.Stake memory c = stakeAtTs(user, block.timestamp);
              return c.delegatedTo;
          }
          // _checkpointsBinarySearch executes a binary search on a list of checkpoints that's sorted chronologically
          // looking for the closest checkpoint that matches the specified timestamp
          function _checkpointsBinarySearch(LibBarnStorage.Checkpoint[] storage checkpoints, uint256 timestamp) internal view returns (uint256) {
              if (checkpoints.length == 0 || timestamp < checkpoints[0].timestamp) {
                  return 0;
              }
              uint256 min = 0;
              uint256 max = checkpoints.length - 1;
              if (timestamp >= checkpoints[max].timestamp) {
                  return checkpoints[max].amount;
              }
              // binary search of the value in the array
              while (max > min) {
                  uint256 mid = (max + min + 1) / 2;
                  if (checkpoints[mid].timestamp <= timestamp) {
                      min = mid;
                  } else {
                      max = mid - 1;
                  }
              }
              return checkpoints[min].amount;
          }
          // _stakeMultiplier calculates the multiplier for the given stake at the given timestamp
          function _stakeMultiplier(LibBarnStorage.Stake memory stake, uint256 timestamp) internal view returns (uint256) {
              if (timestamp >= stake.expiryTimestamp) {
                  return BASE_MULTIPLIER;
              }
              uint256 diff = stake.expiryTimestamp - timestamp;
              if (diff >= MAX_LOCK) {
                  return BASE_MULTIPLIER.mul(2);
              }
              return BASE_MULTIPLIER.add(diff.mul(BASE_MULTIPLIER).div(MAX_LOCK));
          }
          // _updateUserBalance manages an array of checkpoints
          // if there's already a checkpoint for the same timestamp, the amount is updated
          // otherwise, a new checkpoint is inserted
          function _updateUserBalance(LibBarnStorage.Stake[] storage checkpoints, uint256 amount) internal {
              if (checkpoints.length == 0) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, amount, block.timestamp, address(0)));
              } else {
                  LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
                  if (old.timestamp == block.timestamp) {
                      old.amount = amount;
                  } else {
                      checkpoints.push(LibBarnStorage.Stake(block.timestamp, amount, old.expiryTimestamp, old.delegatedTo));
                  }
              }
          }
          // _updateUserLock updates the expiry timestamp on the user's stake
          // it assumes that if the user already has a balance, which is checked for in the lock function
          // then there must be at least 1 checkpoint
          function _updateUserLock(LibBarnStorage.Stake[] storage checkpoints, uint256 expiryTimestamp) internal {
              LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
              if (old.timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, old.amount, expiryTimestamp, old.delegatedTo));
              } else {
                  old.expiryTimestamp = expiryTimestamp;
              }
          }
          // _updateUserDelegatedTo updates the delegateTo property on the user's stake
          // it assumes that if the user already has a balance, which is checked for in the delegate function
          // then there must be at least 1 checkpoint
          function _updateUserDelegatedTo(LibBarnStorage.Stake[] storage checkpoints, address to) internal {
              LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
              if (old.timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, old.amount, old.expiryTimestamp, to));
              } else {
                  old.delegatedTo = to;
              }
          }
          // _updateDelegatedPower updates the power delegated TO the user in the checkpoints history
          function _updateDelegatedPower(LibBarnStorage.Checkpoint[] storage checkpoints, uint256 amount) internal {
              if (checkpoints.length == 0 || checkpoints[checkpoints.length - 1].timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Checkpoint(block.timestamp, amount));
              } else {
                  LibBarnStorage.Checkpoint storage old = checkpoints[checkpoints.length - 1];
                  old.amount = amount;
              }
          }
          // _updateLockedBond stores the new `amount` into the BOND locked history
          function _updateLockedBond(uint256 amount) internal {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              if (ds.bondStakedHistory.length == 0 || ds.bondStakedHistory[ds.bondStakedHistory.length - 1].timestamp < block.timestamp) {
                  ds.bondStakedHistory.push(LibBarnStorage.Checkpoint(block.timestamp, amount));
              } else {
                  LibBarnStorage.Checkpoint storage old = ds.bondStakedHistory[ds.bondStakedHistory.length - 1];
                  old.amount = amount;
              }
          }
      }
      

      File 3 of 3: BarnFacet
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../interfaces/IBarn.sol";
      import "../libraries/LibBarnStorage.sol";
      import "../libraries/LibOwnership.sol";
      import "@openzeppelin/contracts/math/SafeMath.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      contract BarnFacet {
          using SafeMath for uint256;
          uint256 constant public MAX_LOCK = 365 days;
          uint256 constant BASE_MULTIPLIER = 1e18;
          event Deposit(address indexed user, uint256 amount, uint256 newBalance);
          event Withdraw(address indexed user, uint256 amountWithdrew, uint256 amountLeft);
          event Lock(address indexed user, uint256 timestamp);
          event Timelock(address indexed user, uint256 timestamp, uint8 dataType, bytes32 data);
          event UnlockTimelock(address indexed user, uint8 dataType);
          event Delegate(address indexed from, address indexed to);
          event DelegatedPowerIncreased(address indexed from, address indexed to, uint256 amount, uint256 to_newDelegatedPower);
          event DelegatedPowerDecreased(address indexed from, address indexed to, uint256 amount, uint256 to_newDelegatedPower);
          function initBarn(address _bond, address _rewards) public {
              require(_bond != address(0), "BOND address must not be 0x0");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              require(!ds.initialized, "Barn: already initialized");
              LibOwnership.enforceIsContractOwner();
              ds.initialized = true;
              ds.bond = IERC20(_bond);
              ds.rewards = IRewards(_rewards);
          }
          // deposit allows a user to add more bond to his staked balance
          function deposit(uint256 amount) public {
              require(amount > 0, "Amount must be greater than 0");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              uint256 allowance = ds.bond.allowance(msg.sender, address(this));
              require(allowance >= amount, "Token allowance too small");
              // this must be called before the user's balance is updated so the rewards contract can calculate
              // the amount owed correctly
              if (address(ds.rewards) != address(0)) {
                  ds.rewards.registerUserAction(msg.sender);
              }
              uint256 newBalance = balanceOf(msg.sender).add(amount);
              _updateUserBalance(ds.userStakeHistory[msg.sender], newBalance);
              _updateLockedBond(bondStakedAtTs(block.timestamp).add(amount));
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).add(amount);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerIncreased(msg.sender, delegatedTo, amount, newDelegatedPower);
              }
              ds.bond.transferFrom(msg.sender, address(this), amount);
              emit Deposit(msg.sender, amount, newBalance);
          }
          // withdraw allows a user to withdraw funds if the balance is not locked
          function withdraw(uint256 amount) public {
              require(amount > 0, "Amount must be greater than 0");
              require(userLockedUntil(msg.sender) <= block.timestamp, "User balance is locked");
              uint256 balance = balanceOf(msg.sender);
              require(balance >= amount, "Insufficient balance");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              // this must be called before the user's balance is updated so the rewards contract can calculate
              // the amount owed correctly
              if (address(ds.rewards) != address(0)) {
                  ds.rewards.registerUserAction(msg.sender);
              }
              _updateUserBalance(ds.userStakeHistory[msg.sender], balance.sub(amount));
              _updateLockedBond(bondStakedAtTs(block.timestamp).sub(amount));
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).sub(amount);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerDecreased(msg.sender, delegatedTo, amount, newDelegatedPower);
              }
              _deleteUserP2pKey(msg.sender);
              ds.bond.transfer(msg.sender, amount);
              emit Withdraw(msg.sender, amount, balance.sub(amount));
          }
          // lock a user's currently staked balance until timestamp & add the bonus to his voting power
          function lock(uint256 timestamp) public {
              require(timestamp > block.timestamp, "Timestamp must be in the future");
              require(timestamp <= block.timestamp + MAX_LOCK, "Timestamp too big");
              require(balanceOf(msg.sender) > 0, "Sender has no balance");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              LibBarnStorage.Stake[] storage checkpoints = ds.userStakeHistory[msg.sender];
              LibBarnStorage.Stake storage currentStake = checkpoints[checkpoints.length - 1];
              require(timestamp > currentStake.expiryTimestamp, "New timestamp lower than current lock timestamp");
              _updateUserLock(checkpoints, timestamp);
              emit Lock(msg.sender, timestamp);
          }
          function depositAndLock(uint256 amount, uint256 timestamp) public {
              deposit(amount);
              lock(timestamp);
          }
          function addOrAdjusttimelock(uint256 _amount, uint256 _timestamp, uint8 _type, bytes32 _data) public {
              deposit(_amount);
              lock(_timestamp);
              _updateUserP2pKey(msg.sender, _type, _data);
              emit Timelock(msg.sender, _timestamp, _type, _data);
          }
          // delegate allows a user to delegate his voting power to another user
          function delegate(address to) public {
              require(msg.sender != to, "Can't delegate to self");
              uint256 senderBalance = balanceOf(msg.sender);
              require(senderBalance > 0, "No balance to delegate");
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              emit Delegate(msg.sender, to);
              address delegatedTo = userDelegatedTo(msg.sender);
              if (delegatedTo != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(delegatedTo).sub(senderBalance);
                  _updateDelegatedPower(ds.delegatedPowerHistory[delegatedTo], newDelegatedPower);
                  emit DelegatedPowerDecreased(msg.sender, delegatedTo, senderBalance, newDelegatedPower);
              }
              if (to != address(0)) {
                  uint256 newDelegatedPower = delegatedPower(to).add(senderBalance);
                  _updateDelegatedPower(ds.delegatedPowerHistory[to], newDelegatedPower);
                  emit DelegatedPowerIncreased(msg.sender, to, senderBalance, newDelegatedPower);
              }
              _updateUserDelegatedTo(ds.userStakeHistory[msg.sender], to);
          }
          // stopDelegate allows a user to take back the delegated voting power
          function stopDelegate() public {
              return delegate(address(0));
          }
          // balanceOf returns the current BOND balance of a user (bonus not included)
          function balanceOf(address user) public view returns (uint256) {
              return balanceAtTs(user, block.timestamp);
          }
          // balanceAtTs returns the amount of BOND that the user currently staked (bonus NOT included)
          function balanceAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              return stake.amount;
          }
          // stakeAtTs returns the Stake object of the user that was valid at `timestamp`
          function stakeAtTs(address user, uint256 timestamp) public view returns (LibBarnStorage.Stake memory) {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              LibBarnStorage.Stake[] storage stakeHistory = ds.userStakeHistory[user];
              if (stakeHistory.length == 0 || timestamp < stakeHistory[0].timestamp) {
                  return LibBarnStorage.Stake(block.timestamp, 0, block.timestamp, address(0));
              }
              uint256 min = 0;
              uint256 max = stakeHistory.length - 1;
              if (timestamp >= stakeHistory[max].timestamp) {
                  return stakeHistory[max];
              }
              // binary search of the value in the array
              while (max > min) {
                  uint256 mid = (max + min + 1) / 2;
                  if (stakeHistory[mid].timestamp <= timestamp) {
                      min = mid;
                  } else {
                      max = mid - 1;
                  }
              }
              return stakeHistory[min];
          }
          // votingPower returns the voting power (bonus included) + delegated voting power for a user at the current block
          function votingPower(address user) public view returns (uint256) {
              return votingPowerAtTs(user, block.timestamp);
          }
          // votingPowerAtTs returns the voting power (bonus included) + delegated voting power for a user at a point in time
          function votingPowerAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              uint256 ownVotingPower;
              // if the user delegated his voting power to another user, then he doesn't have any voting power left
              if (stake.delegatedTo != address(0)) {
                  ownVotingPower = 0;
              } else {
                  uint256 balance = stake.amount;
                  uint256 multiplier = _stakeMultiplier(stake, timestamp);
                  ownVotingPower = balance.mul(multiplier).div(BASE_MULTIPLIER);
              }
              uint256 delegatedVotingPower = delegatedPowerAtTs(user, timestamp);
              return ownVotingPower.add(delegatedVotingPower);
          }
          // bondStaked returns the total raw amount of BOND staked at the current block
          function bondStaked() public view returns (uint256) {
              return bondStakedAtTs(block.timestamp);
          }
          // bondStakedAtTs returns the total raw amount of BOND users have deposited into the contract
          // it does not include any bonus
          function bondStakedAtTs(uint256 timestamp) public view returns (uint256) {
              return _checkpointsBinarySearch(LibBarnStorage.barnStorage().bondStakedHistory, timestamp);
          }
          // delegatedPower returns the total voting power that a user received from other users
          function delegatedPower(address user) public view returns (uint256) {
              return delegatedPowerAtTs(user, block.timestamp);
          }
          // delegatedPowerAtTs returns the total voting power that a user received from other users at a point in time
          function delegatedPowerAtTs(address user, uint256 timestamp) public view returns (uint256) {
              return _checkpointsBinarySearch(LibBarnStorage.barnStorage().delegatedPowerHistory[user], timestamp);
          }
          // same as multiplierAtTs but for the current block timestamp
          function multiplierOf(address user) public view returns (uint256) {
              return multiplierAtTs(user, block.timestamp);
          }
          // multiplierAtTs calculates the multiplier at a given timestamp based on the user's stake a the given timestamp
          // it includes the decay mechanism
          function multiplierAtTs(address user, uint256 timestamp) public view returns (uint256) {
              LibBarnStorage.Stake memory stake = stakeAtTs(user, timestamp);
              return _stakeMultiplier(stake, timestamp);
          }
          // userLockedUntil returns the timestamp until the user's balance is locked
          function userLockedUntil(address user) public view returns (uint256) {
              LibBarnStorage.Stake memory c = stakeAtTs(user, block.timestamp);
              return c.expiryTimestamp;
          }
          function checkTimeLock(address _user, uint8 _type) public view returns (uint256 amount, uint256 expiry, bytes32 data) {
              LibBarnStorage.NodeInfo memory nInfo = LibBarnStorage.barnStorage().nodeInfo[_user];
              if (nInfo.dataType == _type) {
                  amount = balanceOf(_user);
                  expiry = userLockedUntil(_user);
                  data = nInfo.p2pkey;
              }
          }
          // userDelegatedTo returns the address to which a user delegated their voting power; address(0) if not delegated
          function userDelegatedTo(address user) public view returns (address) {
              LibBarnStorage.Stake memory c = stakeAtTs(user, block.timestamp);
              return c.delegatedTo;
          }
          // _checkpointsBinarySearch executes a binary search on a list of checkpoints that's sorted chronologically
          // looking for the closest checkpoint that matches the specified timestamp
          function _checkpointsBinarySearch(LibBarnStorage.Checkpoint[] storage checkpoints, uint256 timestamp) internal view returns (uint256) {
              if (checkpoints.length == 0 || timestamp < checkpoints[0].timestamp) {
                  return 0;
              }
              uint256 min = 0;
              uint256 max = checkpoints.length - 1;
              if (timestamp >= checkpoints[max].timestamp) {
                  return checkpoints[max].amount;
              }
              // binary search of the value in the array
              while (max > min) {
                  uint256 mid = (max + min + 1) / 2;
                  if (checkpoints[mid].timestamp <= timestamp) {
                      min = mid;
                  } else {
                      max = mid - 1;
                  }
              }
              return checkpoints[min].amount;
          }
          // _stakeMultiplier calculates the multiplier for the given stake at the given timestamp
          function _stakeMultiplier(LibBarnStorage.Stake memory stake, uint256 timestamp) internal view returns (uint256) {
              if (timestamp >= stake.expiryTimestamp) {
                  return BASE_MULTIPLIER;
              }
              uint256 diff = stake.expiryTimestamp - timestamp;
              if (diff >= MAX_LOCK) {
                  return BASE_MULTIPLIER.mul(2);
              }
              return BASE_MULTIPLIER.add(diff.mul(BASE_MULTIPLIER).div(MAX_LOCK));
          }
          // _updateUserBalance manages an array of checkpoints
          // if there's already a checkpoint for the same timestamp, the amount is updated
          // otherwise, a new checkpoint is inserted
          function _updateUserBalance(LibBarnStorage.Stake[] storage checkpoints, uint256 amount) internal {
              if (checkpoints.length == 0) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, amount, block.timestamp, address(0)));
              } else {
                  LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
                  if (old.timestamp == block.timestamp) {
                      old.amount = amount;
                  } else {
                      checkpoints.push(LibBarnStorage.Stake(block.timestamp, amount, old.expiryTimestamp, old.delegatedTo));
                  }
              }
          }
          // _updateUserLock updates the expiry timestamp on the user's stake
          // it assumes that if the user already has a balance, which is checked for in the lock function
          // then there must be at least 1 checkpoint
          function _updateUserLock(LibBarnStorage.Stake[] storage checkpoints, uint256 expiryTimestamp) internal {
              LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
              if (old.timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, old.amount, expiryTimestamp, old.delegatedTo));
              } else {
                  old.expiryTimestamp = expiryTimestamp;
              }
          }
          // _updateUserDelegatedTo updates the delegateTo property on the user's stake
          // it assumes that if the user already has a balance, which is checked for in the delegate function
          // then there must be at least 1 checkpoint
          function _updateUserDelegatedTo(LibBarnStorage.Stake[] storage checkpoints, address to) internal {
              LibBarnStorage.Stake storage old = checkpoints[checkpoints.length - 1];
              if (old.timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Stake(block.timestamp, old.amount, old.expiryTimestamp, to));
              } else {
                  old.delegatedTo = to;
              }
          }
          // _updateDelegatedPower updates the power delegated TO the user in the checkpoints history
          function _updateDelegatedPower(LibBarnStorage.Checkpoint[] storage checkpoints, uint256 amount) internal {
              if (checkpoints.length == 0 || checkpoints[checkpoints.length - 1].timestamp < block.timestamp) {
                  checkpoints.push(LibBarnStorage.Checkpoint(block.timestamp, amount));
              } else {
                  LibBarnStorage.Checkpoint storage old = checkpoints[checkpoints.length - 1];
                  old.amount = amount;
              }
          }
          // _updateLockedBond stores the new `amount` into the BOND locked history
          function _updateLockedBond(uint256 amount) internal {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              if (ds.bondStakedHistory.length == 0 || ds.bondStakedHistory[ds.bondStakedHistory.length - 1].timestamp < block.timestamp) {
                  ds.bondStakedHistory.push(LibBarnStorage.Checkpoint(block.timestamp, amount));
              } else {
                  LibBarnStorage.Checkpoint storage old = ds.bondStakedHistory[ds.bondStakedHistory.length - 1];
                  old.amount = amount;
              }
          }
          function _updateUserP2pKey(address _user, uint8 _type, bytes32 _data) internal {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              LibBarnStorage.NodeInfo storage nInfo = ds.nodeInfo[_user];
              nInfo.p2pkey = _data;
              nInfo.dataType = _type;
          }
          function _deleteUserP2pKey(address _user) internal {
              LibBarnStorage.Storage storage ds = LibBarnStorage.barnStorage();
              delete ds.nodeInfo[_user];
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "../libraries/LibBarnStorage.sol";
      interface IBarn {
          // deposit allows a user to add more bond to his staked balance
          function deposit(uint256 amount) external;
          // withdraw allows a user to withdraw funds if the balance is not locked
          function withdraw(uint256 amount) external;
          // lock a user's currently staked balance until timestamp & add the bonus to his voting power
          function lock(uint256 timestamp) external;
          // delegate allows a user to delegate his voting power to another user
          function delegate(address to) external;
          // stopDelegate allows a user to take back the delegated voting power
          function stopDelegate() external;
          // lock the balance of a proposal creator until the voting ends; only callable by DAO
          function lockCreatorBalance(address user, uint256 timestamp) external;
          // balanceOf returns the current BOND balance of a user (bonus not included)
          function balanceOf(address user) external view returns (uint256);
          // balanceAtTs returns the amount of BOND that the user currently staked (bonus NOT included)
          function balanceAtTs(address user, uint256 timestamp) external view returns (uint256);
          // stakeAtTs returns the Stake object of the user that was valid at `timestamp`
          function stakeAtTs(address user, uint256 timestamp) external view returns (LibBarnStorage.Stake memory);
          // votingPower returns the voting power (bonus included) + delegated voting power for a user at the current block
          function votingPower(address user) external view returns (uint256);
          // votingPowerAtTs returns the voting power (bonus included) + delegated voting power for a user at a point in time
          function votingPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // bondStaked returns the total raw amount of BOND staked at the current block
          function bondStaked() external view returns (uint256);
          // bondStakedAtTs returns the total raw amount of BOND users have deposited into the contract
          // it does not include any bonus
          function bondStakedAtTs(uint256 timestamp) external view returns (uint256);
          // delegatedPower returns the total voting power that a user received from other users
          function delegatedPower(address user) external view returns (uint256);
          // delegatedPowerAtTs returns the total voting power that a user received from other users at a point in time
          function delegatedPowerAtTs(address user, uint256 timestamp) external view returns (uint256);
          // multiplierAtTs calculates the multiplier at a given timestamp based on the user's stake a the given timestamp
          // it includes the decay mechanism
          function multiplierAtTs(address user, uint256 timestamp) external view returns (uint256);
          // userLockedUntil returns the timestamp until the user's balance is locked
          function userLockedUntil(address user) external view returns (uint256);
          // userDidDelegate returns the address to which a user delegated their voting power; address(0) if not delegated
          function userDelegatedTo(address user) external view returns (address);
          // bondCirculatingSupply returns the current circulating supply of BOND
          function bondCirculatingSupply() external view returns (uint256);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "../interfaces/IRewards.sol";
      library LibBarnStorage {
          bytes32 constant STORAGE_POSITION = keccak256("com.barnbridge.barn.storage");
          struct Checkpoint {
              uint256 timestamp;
              uint256 amount;
          }
          struct Stake {
              uint256 timestamp;
              uint256 amount;
              uint256 expiryTimestamp;
              address delegatedTo;
          }
          struct NodeInfo {
              bytes32 p2pkey;
              uint8   dataType;
          }
          struct Storage {
              bool initialized;
              // mapping of user address to history of Stake objects
              // every user action creates a new object in the history
              mapping(address => Stake[]) userStakeHistory;
              // array of bond staked Checkpoint
              // deposits/withdrawals create a new object in the history (max one per block)
              Checkpoint[] bondStakedHistory;
              // mapping of user address to history of delegated power
              // every delegate/stopDelegate call create a new checkpoint (max one per block)
              mapping(address => Checkpoint[]) delegatedPowerHistory;
              // mapping of user address to <p2pkey,dataType> for swingby node. (no history)
              mapping(address => NodeInfo) nodeInfo;
              IERC20 bond;
              IRewards rewards;
          }
          function barnStorage() internal pure returns (Storage storage ds) {
              bytes32 position = STORAGE_POSITION;
              assembly {
                  ds.slot := position
              }
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      import "./LibDiamondStorage.sol";
      library LibOwnership {
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          function setContractOwner(address _newOwner) internal {
              LibDiamondStorage.DiamondStorage storage ds = LibDiamondStorage.diamondStorage();
              address previousOwner = ds.contractOwner;
              require(previousOwner != _newOwner, "Previous owner and new owner must be different");
              ds.contractOwner = _newOwner;
              emit OwnershipTransferred(previousOwner, _newOwner);
          }
          function contractOwner() internal view returns (address contractOwner_) {
              contractOwner_ = LibDiamondStorage.diamondStorage().contractOwner;
          }
          function enforceIsContractOwner() view internal {
              require(msg.sender == LibDiamondStorage.diamondStorage().contractOwner, "Must be contract owner");
          }
          modifier onlyOwner {
              require(msg.sender == LibDiamondStorage.diamondStorage().contractOwner, "Must be contract owner");
              _;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.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, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              uint256 c = a + b;
              if (c < a) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the substraction of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b > a) return (false, 0);
              return (true, a - b);
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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-contracts/pull/522
              if (a == 0) return (true, 0);
              uint256 c = a * b;
              if (c / a != b) return (false, 0);
              return (true, c);
          }
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a / b);
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              if (b == 0) return (false, 0);
              return (true, a % b);
          }
          /**
           * @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");
              return a - b;
          }
          /**
           * @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) {
              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, reverting 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) {
              require(b > 0, "SafeMath: division by zero");
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting 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;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {trySub}.
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              return a - b;
          }
          /**
           * @dev Returns the integer division of two unsigned integers, reverting with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryDiv}.
           *
           * 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, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting with custom message when dividing by zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryMod}.
           *
           * 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, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              return a % b;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.0 <0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      interface IRewards {
          function registerUserAction(address user) external;
          function setNewAPR(uint256 _apr) external;
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity 0.7.6;
      pragma experimental ABIEncoderV2;
      library LibDiamondStorage {
          bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
          struct Facet {
              address facetAddress;
              uint16 selectorPosition;
          }
          struct DiamondStorage {
              // function selector => facet address and selector position in selectors array
              mapping(bytes4 => Facet) facets;
              bytes4[] selectors;
              // ERC165
              mapping(bytes4 => bool) supportedInterfaces;
              // owner of the contract
              address contractOwner;
          }
          function diamondStorage() internal pure returns (DiamondStorage storage ds) {
              bytes32 position = DIAMOND_STORAGE_POSITION;
              assembly {
                  ds.slot := position
              }
          }
      }