make event state public

make event state public

pragma solidity ^0.4.11;

import './Nutz.sol';
import "./SafeMath.sol";

contract PowerEvent {
  using SafeMath for uint;

  // states
  //   - waiting, initial state
  //   - collecting, after waiting, before collection stopped
  //   - failed, after collecting, if softcap missed
  //   - closed, after collecting, if softcap reached
  //   - complete, after closed or failed, when job done
  enum EventState { Waiting, Collecting, Closed, Failed, Complete }
  EventState public state;
  uint256 public RATE_FACTOR = 1000000;

  // Terms
  uint256 public startTime;
  uint256 public minDuration;
  uint256 public maxDuration;
  uint256 public softCap;
  uint256 public hardCap;
  uint256 public discountRate; // if rate 30%, this will be 300,000
  address[] public milestoneRecipients;
  uint256[] public milestoneShares;

  // Params
  address public ntzAddr;
  address public powerAddr;
  uint256 public initialReserve;
  uint256 public initialSupply;

  function PowerEvent(address _ntzAddr, uint256 _startTime, uint256 _minDuration, uint256 _maxDuration, uint256 _softCap, uint256 _hardCap, uint256 _discount, address[] _milestoneRecipients, uint256[] _milestoneShares) {
    ntzAddr = _ntzAddr;
    startTime = _startTime;
    minDuration = _minDuration;
    maxDuration = _maxDuration;
    softCap = _softCap;
    hardCap = _hardCap;
    discountRate = _discount;
    state = EventState.Waiting;
    milestoneRecipients = _milestoneRecipients;
    milestoneShares = _milestoneShares;
  }

  modifier isState(EventState _state) {
    require(state == _state);
    _;
  }

  function startCollection() isState(EventState.Waiting) {
    // check time
    require(now > startTime);
    // assert(now < startTime.add(minDuration));
    // read initial values
    var NutzContract = Nutz(ntzAddr);
    powerAddr = NutzContract.powerAddr();
    initialSupply = NutzContract.totalSupply();
    initialReserve = NutzContract.reserve();
    uint256 ceiling = NutzContract.ceiling();
    // move ceiling
    uint256 newCeiling = ceiling.mul(discountRate).div(RATE_FACTOR);
    NutzContract.moveCeiling(newCeiling);
    // set state
    state = EventState.Collecting;
  }

  function stopCollection() isState(EventState.Collecting) {
    var NutzContract = Nutz(ntzAddr);
    uint256 collected = NutzContract.reserve().sub(initialReserve);
    if (now > startTime.add(maxDuration)) {
      if (collected >= softCap) {
        // softCap reached, close
        state = EventState.Closed;
        return;
      } else {
        // softCap missed, fail
        state = EventState.Failed;
        return;
      }
    } else if (now > startTime.add(minDuration)) {
      if (collected >= hardCap) {
        // hardCap reached, close
        state = EventState.Closed;
        return;
      } else {
        // keep going
        revert();
      }
    }
    // keep going
    revert();
  }

  function completeFailed() isState(EventState.Failed) {
    var NutzContract = Nutz(ntzAddr);
    // move floor (set ceiling or max floor)
    uint256 ceiling = NutzContract.ceiling();
    NutzContract.moveFloor(ceiling);
    // remove access
    NutzContract.removeAdmin(address(this));
    // set state
    state = EventState.Complete;
  }

  function completeClosed() isState(EventState.Closed) {
    var NutzContract = Nutz(ntzAddr);
    // move ceiling
    uint256 ceiling = NutzContract.ceiling();
    uint256 newCeiling = ceiling.mul(RATE_FACTOR).div(discountRate);
    NutzContract.moveCeiling(newCeiling);
    // dilute power
    uint256 newSupply = NutzContract.totalSupply().sub(initialSupply);
    NutzContract.dilutePower(newSupply);
    // set max power
    var PowerContract = ERC20(powerAddr);
    uint256 authorizedPower = PowerContract.totalSupply();
    NutzContract.setMaxPower(authorizedPower.div(2));
    // pay out milestone
    uint256 collected = NutzContract.reserve().sub(initialReserve);
    for (uint256 i = 0; i < milestoneRecipients.length; i++) {
      uint256 payoutAmount = collected.mul(milestoneShares[i]).div(RATE_FACTOR);
      NutzContract.allocateEther(payoutAmount, milestoneRecipients[i]);
    }
    // remove access
    NutzContract.removeAdmin(address(this));
    // set state
    state = EventState.Complete;
  }

}

pragma solidity ^0.4.11;

import './Nutz.sol';
import "./SafeMath.sol";

contract PowerEvent {
  using SafeMath for uint;

  // states
  //   - waiting, initial state
  //   - collecting, after waiting, before collection stopped
  //   - failed, after collecting, if softcap missed
  //   - closed, after collecting, if softcap reached
  //   - complete, after closed or failed, when job done
  enum EventState { Waiting, Collecting, Closed, Failed, Complete }
  EventState state;
  uint256 public RATE_FACTOR = 1000000;

  // Terms
  uint256 public startTime;
  uint256 public minDuration;
  uint256 public maxDuration;
  uint256 public softCap;
  uint256 public hardCap;
  uint256 public discountRate; // if rate 30%, this will be 300,000
  address[] public milestoneRecipients;
  uint256[] public milestoneShares;

  // Params
  address public ntzAddr;
  address public powerAddr;
  uint256 public initialReserve;
  uint256 public initialSupply;

  function PowerEvent(address _ntzAddr, uint256 _startTime, uint256 _minDuration, uint256 _maxDuration, uint256 _softCap, uint256 _hardCap, uint256 _discount, address[] _milestoneRecipients, uint256[] _milestoneShares) {
    ntzAddr = _ntzAddr;
    startTime = _startTime;
    minDuration = _minDuration;
    maxDuration = _maxDuration;
    softCap = _softCap;
    hardCap = _hardCap;
    discountRate = _discount;
    state = EventState.Waiting;
    milestoneRecipients = _milestoneRecipients;
    milestoneShares = _milestoneShares;
  }

  modifier isState(EventState _state) {
    require(state == _state);
    _;
  }

  function startCollection() isState(EventState.Waiting) {
    // check time
    require(now > startTime);
    // assert(now < startTime.add(minDuration));
    // read initial values
    var NutzContract = Nutz(ntzAddr);
    powerAddr = NutzContract.powerAddr();
    initialSupply = NutzContract.totalSupply();
    initialReserve = NutzContract.reserve();
    uint256 ceiling = NutzContract.ceiling();
    // move ceiling
    uint256 newCeiling = ceiling.mul(discountRate).div(RATE_FACTOR);
    NutzContract.moveCeiling(newCeiling);
    // set state
    state = EventState.Collecting;
  }

  function stopCollection() isState(EventState.Collecting) {
    var NutzContract = Nutz(ntzAddr);
    uint256 collected = NutzContract.reserve().sub(initialReserve);
    if (now > startTime.add(maxDuration)) {
      if (collected >= softCap) {
        // softCap reached, close
        state = EventState.Closed;
        return;
      } else {
        // softCap missed, fail
        state = EventState.Failed;
        return;
      }
    } else if (now > startTime.add(minDuration)) {
      if (collected >= hardCap) {
        // hardCap reached, close
        state = EventState.Closed;
        return;
      } else {
        // keep going
        revert();
      }
    }
    // keep going
    revert();
  }

  function completeFailed() isState(EventState.Failed) {
    var NutzContract = Nutz(ntzAddr);
    // move floor (set ceiling or max floor)
    uint256 ceiling = NutzContract.ceiling();
    NutzContract.moveFloor(ceiling);
    // remove access
    NutzContract.removeAdmin(address(this));
    // set state
    state = EventState.Complete;
  }

  function completeClosed() isState(EventState.Closed) {
    var NutzContract = Nutz(ntzAddr);
    // move ceiling
    uint256 ceiling = NutzContract.ceiling();
    uint256 newCeiling = ceiling.mul(RATE_FACTOR).div(discountRate);
    NutzContract.moveCeiling(newCeiling);
    // dilute power
    uint256 newSupply = NutzContract.totalSupply().sub(initialSupply);
    NutzContract.dilutePower(newSupply);
    // set max power
    var PowerContract = ERC20(powerAddr);
    uint256 authorizedPower = PowerContract.totalSupply();
    NutzContract.setMaxPower(authorizedPower.div(2));
    // pay out milestone
    uint256 collected = NutzContract.reserve().sub(initialReserve);
    for (uint256 i = 0; i < milestoneRecipients.length; i++) {
      uint256 payoutAmount = collected.mul(milestoneShares[i]).div(RATE_FACTOR);
      NutzContract.allocateEther(payoutAmount, milestoneRecipients[i]);
    }
    // remove access
    NutzContract.removeAdmin(address(this));
    // set state
    state = EventState.Complete;
  }

}


better comments

better comments

//pragma solidity 0.4.1;

import {ExecutionLib} from "contracts/ExecutionLib.sol";
import {RequestScheduleLib} from "contracts/RequestScheduleLib.sol";
import {ClaimLib} from "contracts/ClaimLib.sol";
import {RequestMetaLib} from "contracts/RequestMetaLib.sol";
import {PaymentLib} from "contracts/PaymentLib.sol";
import {SafeSendLib} from "contracts/SafeSendLib.sol";
import {MathLib} from "contracts/MathLib.sol";

library RequestLib {
    using ExecutionLib for ExecutionLib.ExecutionData;
    using RequestScheduleLib for RequestScheduleLib.ExecutionWindow;
    using ClaimLib for ClaimLib.ClaimData;
    using RequestMetaLib for RequestMetaLib.RequestMeta;
    using PaymentLib for PaymentLib.PaymentData;
    using SafeSendLib for address;
    using MathLib for uint;

    /*
     *  This struct exists to circumvent an issue with returning multiple
     *  values from a library function.  I found through experimentation that I
     *  could not return more than 4 things from a library function, even if I
     *  put them in arrays.
     */
    struct SerializedRequest {
        address[6] addressValues;
        bool[3] boolValues;
        uint[15] uintValues;
        uint8[1] uint8Values;
    }

    struct Request {
        ExecutionLib.ExecutionData txnData;
        RequestMetaLib.RequestMeta meta;
        PaymentLib.PaymentData paymentData;
        ClaimLib.ClaimData claimData;
        RequestScheduleLib.ExecutionWindow schedule;
        SerializedRequest serializedValues;
    }

    enum AbortReason {
        WasCancelled,
        AlreadyCalled,
        BeforeCallWindow,
        AfterCallWindow,
        ReservedForClaimer,
        StackTooDeep,
        InsufficientGas
    }

    event Cancelled(uint rewardPayment, uint measuredGasConsumption);
    event Claimed();
    // TODO: Figure out how log topics are constructed for events that use
    // Enums as args.
    //event Aborted(AbortReason reason);
    event Aborted(uint8 reason);
    event Executed(uint payment, uint donation, uint measuredGasConsumption);

    /*
     *  Validate the initialization parameters for a transaction request.
     */
    function validate(address[4] addressArgs,
                      uint[11] uintArgs,
                      bytes callData,
                      uint endowment) returns (bool[7] is_valid) {
        Request memory request;

        // callData is special.
        request.txnData.callData = callData;

        // Address values
        request.claimData.claimedBy = 0x0;
        request.meta.createdBy = addressArgs[0];
        request.meta.owner = addressArgs[1];
        request.paymentData.donationBenefactor = addressArgs[2];
        request.paymentData.donationBenefactor = 0x0;
        request.txnData.toAddress = addressArgs[3];

        // Boolean values
        request.meta.isCancelled = false;
        request.meta.wasCalled = false;
        request.meta.wasSuccessful = false;

        // UInt values
        request.claimData.claimDeposit = 0;
        request.paymentData.anchorGasPrice = tx.gasprice;
        request.paymentData.donation = uintArgs[0];
        request.paymentData.payment = uintArgs[1];
        request.paymentData.donationOwed = 0;
        request.paymentData.paymentOwed = 0;
        request.schedule.claimWindowSize = uintArgs[2];
        request.schedule.freezePeriod = uintArgs[3];
        request.schedule.reservedWindowSize = uintArgs[4];
        // This must be capped at 1 or it throws an exception.
        request.schedule.temporalUnit = RequestScheduleLib.TemporalUnit(uintArgs[5].min(1));
        request.schedule.windowStart = uintArgs[6];
        request.schedule.windowSize = uintArgs[7];
        request.txnData.callGas = uintArgs[8];
        request.txnData.callValue = uintArgs[9];
        request.txnData.requiredStackDepth = uintArgs[10];

        // Uint8 values
        request.claimData.paymentModifier = 0;

        // The order of these errors matters as it determines which
        // ValidationError event codes are logged when validation fails.
        is_valid[0] = PaymentLib.validateEndowment(endowment,
                                                   request.paymentData.payment,
                                                   request.paymentData.donation,
                                                   request.txnData.callGas,
                                                   request.txnData.callValue);
        is_valid[1] = RequestScheduleLib.validateReservedWindowSize(request.schedule.reservedWindowSize,
                                                                    request.schedule.windowSize);
        is_valid[2] = RequestScheduleLib.validateTemporalUnit(uintArgs[5]);
        is_valid[3] = RequestScheduleLib.validateWindowStart(request.schedule.temporalUnit,
                                                             request.schedule.freezePeriod,
                                                             request.schedule.windowStart);
        is_valid[4] = ExecutionLib.validateRequiredStackDepth(request.txnData.requiredStackDepth);
        is_valid[5] = ExecutionLib.validateCallGas(request.txnData.callGas, _EXECUTE_EXTRA_GAS);
        is_valid[6] = ExecutionLib.validateToAddress(request.txnData.toAddress);

        return is_valid;
    }

    /*
     *  Initialize a new Request.
     */
    function initialize(Request storage self,
                        address[4] addressArgs,
                        uint[11] uintArgs,
                        bytes callData) returns (bool) {
        address[6] memory addressValues = [
            0x0,             // self.claimData.claimedBy
            addressArgs[0],  // self.meta.createdBy
            addressArgs[1],  // self.meta.owner
            addressArgs[2],  // self.paymentData.donationBenefactor
            0x0,             // self.paymentData.paymentBenefactor
            addressArgs[3]   // self.txnData.toAddress
        ];

        bool[3] memory boolValues = [false, false, false];

        uint[15] memory uintValues = [
            0,               // self.claimData.claimDeposit
            tx.gasprice,     // self.paymentData.anchorGasPrice
            uintArgs[0],     // self.paymentData.donation
            0,               // self.paymentData.donationOwed
            uintArgs[1],     // self.paymentData.payment
            0,               // self.paymentData.paymentOwed
            uintArgs[2],     // self.schedule.claimWindowSize
            uintArgs[3],     // self.schedule.freezePeriod
            uintArgs[4],     // self.schedule.reservedWindowSize
            uintArgs[5],     // self.schedule.temporalUnit
            uintArgs[6],     // self.schedule.windowStart
            uintArgs[7],     // self.schedule.windowSize
            uintArgs[8],     // self.txnData.callGas
            uintArgs[9],     // self.txnData.callValue
            uintArgs[10]     // self.txnData.requiredStackDepth
        ];

        uint8[1] memory uint8Values = [
            0
        ];

        deserialize(self, addressValues, boolValues, uintValues, uint8Values, callData);

        return true;
    }

    /*
     *  Returns the entire data structure of the Request in a *serialized*
     *  format.  This will be missing the `callData` which must be requested
     *  separately
     *
     *  Parameter order is alphabetical by type, then namespace, then name
     *
     *  NOTE: This exists because of an issue I ran into related to returning
     *  multiple values from a library function.  I found through
     *  experimentation that I was unable to return more than 4 things, even if
     *  I used the trick of returning arrays of items.
     */
    function serialize(Request storage self) returns (bool) {
        // Address values
        self.serializedValues.addressValues[0] = self.claimData.claimedBy;
        self.serializedValues.addressValues[1] = self.meta.createdBy;
        self.serializedValues.addressValues[2] = self.meta.owner;
        self.serializedValues.addressValues[3] = self.paymentData.donationBenefactor;
        self.serializedValues.addressValues[4] = self.paymentData.paymentBenefactor;
        self.serializedValues.addressValues[5] = self.txnData.toAddress;

        // Boolean values
        self.serializedValues.boolValues[0] = self.meta.isCancelled;
        self.serializedValues.boolValues[1] = self.meta.wasCalled;
        self.serializedValues.boolValues[2] = self.meta.wasSuccessful;

        // UInt256 values
        self.serializedValues.uintValues[0] = self.claimData.claimDeposit;
        self.serializedValues.uintValues[1] = self.paymentData.anchorGasPrice;
        self.serializedValues.uintValues[2] = self.paymentData.donation;
        self.serializedValues.uintValues[3] = self.paymentData.donationOwed;
        self.serializedValues.uintValues[4] = self.paymentData.payment;
        self.serializedValues.uintValues[5] = self.paymentData.paymentOwed;
        self.serializedValues.uintValues[6] = self.schedule.claimWindowSize;
        self.serializedValues.uintValues[7] = self.schedule.freezePeriod;
        self.serializedValues.uintValues[8] = self.schedule.reservedWindowSize;
        self.serializedValues.uintValues[9] = uint(self.schedule.temporalUnit);
        self.serializedValues.uintValues[10] = self.schedule.windowStart;
        self.serializedValues.uintValues[11] = self.schedule.windowSize;
        self.serializedValues.uintValues[12] = self.txnData.callGas;
        self.serializedValues.uintValues[13] = self.txnData.callValue;
        self.serializedValues.uintValues[14] = self.txnData.requiredStackDepth;

        // Uint8 values
        self.serializedValues.uint8Values[0] = self.claimData.paymentModifier;

        return true;
    }

    /*
     *  Populates a Request object from the full output of `serialize`.
     *
     *  Parameter order is alphabetical by type, then namespace, then name.
     */
    function deserialize(Request storage self,
                         address[6] addressValues,
                         bool[3] boolValues,
                         uint[15] uintValues,
                         uint8[1] uint8Values,
                         bytes callData) returns (bool) {
        // callData is special.
        self.txnData.callData = callData;

        // Address values
        self.claimData.claimedBy = addressValues[0];
        self.meta.createdBy = addressValues[1];
        self.meta.owner = addressValues[2];
        self.paymentData.donationBenefactor = addressValues[3];
        self.paymentData.paymentBenefactor = addressValues[4];
        self.txnData.toAddress = addressValues[5];

        // Boolean values
        self.meta.isCancelled = boolValues[0];
        self.meta.wasCalled = boolValues[1];
        self.meta.wasSuccessful = boolValues[2];

        // UInt values
        self.claimData.claimDeposit = uintValues[0];
        self.paymentData.anchorGasPrice = uintValues[1];
        self.paymentData.donation = uintValues[2];
        self.paymentData.donationOwed = uintValues[3];
        self.paymentData.payment = uintValues[4];
        self.paymentData.paymentOwed = uintValues[5];
        self.schedule.claimWindowSize = uintValues[6];
        self.schedule.freezePeriod = uintValues[7];
        self.schedule.reservedWindowSize = uintValues[8];
        self.schedule.temporalUnit = RequestScheduleLib.TemporalUnit(uintValues[9]);
        self.schedule.windowStart = uintValues[10];
        self.schedule.windowSize = uintValues[11];
        self.txnData.callGas = uintValues[12];
        self.txnData.callValue = uintValues[13];
        self.txnData.requiredStackDepth = uintValues[14];

        // Uint8 values
        self.claimData.paymentModifier = uint8Values[0];
    }

    function execute(Request storage self) returns (bool) {
        /*
         *  Execute the TransactionRequest
         *
         *  +---------------------+
         *  | Phase 1: Accounting |
         *  +---------------------+
         *
         *  Must pass all of the following checks:
         *
         *  1. Not already called.
         *  2. Not cancelled.
         *  3. Not before the execution window.
         *  4. Not after the execution window.
         *  5. if (claimedBy == 0x0 or msg.sender == claimedBy):
         *         - windowStart <= block.number
         *         - block.number <= windowStart + windowSize
         *     else if (msg.sender != claimedBy):
         *         - windowStart + reservedWindowSize <= block.number
         *         - block.number <= windowStart + windowSize
         *     else:
         *         - throw (should be impossible)
         *  6. if (msg.sender != tx.origin):
         *         - Verify stack can be increased by requiredStackDepth
         *  7. msg.gas >= callGas
         *
         *  +--------------------+
         *  | Phase 2: Execution |
         *  +--------------------+
         *
         *  1. Mark as called (must be before actual execution to prevent
         *     re-entrance.
         *  2. Send Transaction and record success or failure.
         *
         *  +---------------------+
         *  | Phase 3: Accounting |
         *  +---------------------+
         *
         *  1. Calculate and send donation amount.
         *  2. Calculate and send payment amount.
         *  3. Send remaining ether back to owner.
         *
         */
        var startGas = msg.gas;

        // +----------------------+
        // | Begin: Authorization |
        // +----------------------+

        if (msg.gas < requiredExecutionGas(self)) {
            Aborted(uint8(AbortReason.InsufficientGas));
            return false;
        } else if (self.meta.wasCalled) {
            Aborted(uint8(AbortReason.AlreadyCalled));
            return false;
        } else if (self.meta.isCancelled) {
            Aborted(uint8(AbortReason.WasCancelled));
            return false;
        } else if (self.schedule.isBeforeWindow()) {
            Aborted(uint8(AbortReason.BeforeCallWindow));
            return false;
        } else if (self.schedule.isAfterWindow()) {
            Aborted(uint8(AbortReason.AfterCallWindow));
            return false;
        } else if (self.claimData.isClaimed() &&
                   msg.sender != self.claimData.claimedBy &&
                   self.schedule.inReservedWindow()) {
            Aborted(uint8(AbortReason.ReservedForClaimer));
            return false;
        } else if (msg.sender != tx.origin && !self.txnData.stackCanBeExtended()) {
            Aborted(uint8(AbortReason.StackTooDeep));
            return false;
        }

        // +--------------------+
        // | End: Authorization |
        // +--------------------+
        // +------------------+
        // | Begin: Execution |
        // +------------------+

        // Mark as being called before sending transaction to prevent re-entrance
        self.meta.wasCalled = true;

        // Send the transaction
        self.meta.wasSuccessful = self.txnData.sendTransaction();

        // +----------------+
        // | End: Execution |
        // +----------------+
        // +-------------------+
        // | Begin: Accounting |
        // +-------------------+

        // Compute the donation amount
        if (self.paymentData.hasBenefactor()) {
            self.paymentData.donationOwed = self.paymentData.getDonation()
                                                            .safeAdd(self.paymentData.donationOwed);
        }

        // record this so that we can log it later.
        var totalDonationPayment = self.paymentData.donationOwed;

        // Send the donation.
        self.paymentData.sendDonation();

        // Compute the payment amount and who it should be sent do.
        self.paymentData.paymentBenefactor = msg.sender;
        if (self.claimData.isClaimed()) {
            self.paymentData.paymentOwed = self.claimData.claimDeposit.safeAdd(self.paymentData.paymentOwed);
            // need to zero out the claim deposit since it is now accounted for
            // in the paymentOwed value.
            self.claimData.claimDeposit = 0;
            self.paymentData.paymentOwed = self.paymentData.getPaymentWithModifier(self.claimData.paymentModifier)
                                                           .safeAdd(self.paymentData.paymentOwed);
        } else {
            self.paymentData.paymentOwed = self.paymentData.getPayment()
                                                           .safeAdd(self.paymentData.paymentOwed);
        }

        // Record the amount of gas used by execution.
        uint measuredGasConsumption = startGas.flooredSub(msg.gas).safeAdd(_EXECUTE_EXTRA_GAS);

        // +----------------------------------------------------------------------+
        // | NOTE: All code after this must be accounted for by EXECUTE_EXTRA_GAS |
        // +----------------------------------------------------------------------+

        // Add the gas reimbursment amount to the payment.
        self.paymentData.paymentOwed = measuredGasConsumption.safeMultiply(tx.gasprice)
                                                             .safeAdd(self.paymentData.paymentOwed);

        // Log the two payment amounts.  Otherwise it is non-trivial to figure
        // out how much was payed.
        Executed(self.paymentData.paymentOwed,
                 totalDonationPayment,
                 measuredGasConsumption);

        // Send the payment.
        self.paymentData.sendPayment();

        // Send all extra ether back to the owner.
        sendOwnerEther(self);

        // +-----------------+
        // | End: Accounting |
        // +-----------------+

        return true;
    }

    function requiredExecutionGas(Request storage self) returns (uint) {
        return self.txnData.callGas.safeAdd(_GAS_TO_AUTHORIZE_EXECUTION)
                                   .safeAdd(_GAS_TO_COMPLETE_EXECUTION);
    }

    /*
     * The amount of gas needed to do all of the pre execution checks.
     */
    // TODO: measure this.
    uint constant _GAS_TO_AUTHORIZE_EXECUTION = 0;

    function GAS_TO_AUTHORIZE_EXECUTION() returns (uint) {
        return _GAS_TO_AUTHORIZE_EXECUTION;
    }

    /*
     * The amount of gas needed to complete the execute method after
     * the transaction has been sent.
     */
    uint constant _GAS_TO_COMPLETE_EXECUTION = 190000;

    function GAS_TO_COMPLETE_EXECUTION() returns (uint) {
        return _GAS_TO_COMPLETE_EXECUTION;
    }

    /*
     *  The amount of gas used by the portion of the `execute` function
     *  that cannot be accounted for via gas tracking.
     */
    uint constant _EXECUTE_EXTRA_GAS = 145000;

    function EXECUTE_EXTRA_GAS() returns (uint) {
        return _EXECUTE_EXTRA_GAS;
    }

    /*
     *  Return boolean whether the call can be cancelled.  Must satisfy the
     *  following conditions.
     *
     *  1. Not Cancelled
     *  2. either:
     *    * not wasCalled && afterExecutionWindow
     *    * not claimed && beforeFreezeWindow && msg.sender == owner
     */
    function isCancellable(Request storage self) returns (bool) {
        if (self.meta.isCancelled) {
            return false;
        } else if (!self.meta.wasCalled && self.schedule.isAfterWindow()) {
            return true;
        } else if (!self.claimData.isClaimed() && self.schedule.isBeforeFreeze() && msg.sender == self.meta.owner) {
            return true;
        } else {
            return false;
        }
    }

    /*
     *  Constant value to account for the gas usage that cannot be accounted
     *  for using gas-tracking within the `cancel` function.
     */
    uint constant _CANCEL_EXTRA_GAS = 85000;

    function CANCEL_EXTRA_GAS() returns (uint) {
        return _CANCEL_EXTRA_GAS;
    }

    /*
     *  Cancel the transaction request, attempting to send all appropriate
     *  refunds.  To incentivise cancellation by other parties, a small reward
     *  payment is issued to the party that cancels the request if they are not
     *  the owner.
     */
    function cancel(Request storage self) returns (bool) {
        uint startGas = msg.gas;
        uint rewardPayment;
        uint measuredGasConsumption;

        if (!isCancellable(self)) {
            return false;
        }

        // set this here to prevent re-entrance attacks.
        self.meta.isCancelled = true;

        // refund any claim deposit.
        self.claimData.refundDeposit();

        // send a reward to the canceller if it isn't the owner.  This also
        // guarantees that it is being cancelled after the call window since
        // the `isCancellable()` function checks this.
        if (msg.sender != self.meta.owner) {
            rewardPayment = self.paymentData.payment.safeMultiply(self.paymentData.getMultiplier()) / 100 / 100;
            measuredGasConsumption = startGas.flooredSub(msg.gas)
                                             .safeAdd(_CANCEL_EXTRA_GAS);
            rewardPayment = measuredGasConsumption.safeMultiply(tx.gasprice)
                                                  .safeAdd(rewardPayment);
            msg.sender.safeSend(rewardPayment);
        }

        // Log the event
        Cancelled(rewardPayment, measuredGasConsumption);

        // send the remaining ether to the owner.
        sendOwnerEther(self);

        return true;
    }

    /*
     * Return boolean as to whether the request may be claimed.
     */
    function isClaimable(Request storage self) returns (bool) {
        if (self.claimData.isClaimed()) {
            return false;
        } else if (self.meta.isCancelled) {
            return false;
        } else if (!self.schedule.inClaimWindow()) {
            return false;
        } else if (msg.value < ClaimLib.minimumDeposit(self.paymentData.payment)) {
            return false;
        } else {
            return true;
        }
    }

    /*
     * Claim the request
     */
    function claim(Request storage self) returns (bool) {
        if (!isClaimable(self)) {
            if (msg.sender.sendOrThrow(msg.value)) {
                return false;
            }
            throw;
        }
        self.claimData.claim(self.schedule.computePaymentModifier());
        Claimed();
        return true;
    }

    /*
     * Refund claimer deposit.
     */
    function refundClaimDeposit(Request storage self) returns (bool) {
        if (self.meta.isCancelled || self.meta.wasCalled) {
            return self.claimData.refundDeposit(0);
        }
        return false;
    }

    /*
     * Send donation
     */
    function sendDonation(Request storage self) returns (bool) {
        if (self.meta.wasCalled) {
            return self.paymentData.sendDonation(0);
        }
        return false;
    }

    /*
     * Send payment
     */
    function sendPayment(Request storage self) returns (bool) {
        if (self.meta.wasCalled) {
            return self.paymentData.sendPayment(0);
        }
        return false;
    }

    /*
     * Send all extra ether in the request contract back to the owner.
     */
    function sendOwnerEther(Request storage self) returns (bool) {
        return sendOwnerEther(self, SafeSendLib.DEFAULT_SEND_GAS());
    }

    function sendOwnerEther(Request storage self, uint sendGas) returns (bool) {
        if (self.meta.isCancelled || self.meta.wasCalled) {
            self.meta.owner.safeSend(this.balance.flooredSub(self.claimData.claimDeposit)
                                                 .flooredSub(self.paymentData.paymentOwed)
                                                 .flooredSub(self.paymentData.donationOwed),
                                     sendGas);
            return true;
        }
        return false;
    }
}

//pragma solidity 0.4.1;

import {ExecutionLib} from "contracts/ExecutionLib.sol";
import {RequestScheduleLib} from "contracts/RequestScheduleLib.sol";
import {ClaimLib} from "contracts/ClaimLib.sol";
import {RequestMetaLib} from "contracts/RequestMetaLib.sol";
import {PaymentLib} from "contracts/PaymentLib.sol";
import {SafeSendLib} from "contracts/SafeSendLib.sol";
import {MathLib} from "contracts/MathLib.sol";

library RequestLib {
    using ExecutionLib for ExecutionLib.ExecutionData;
    using RequestScheduleLib for RequestScheduleLib.ExecutionWindow;
    using ClaimLib for ClaimLib.ClaimData;
    using RequestMetaLib for RequestMetaLib.RequestMeta;
    using PaymentLib for PaymentLib.PaymentData;
    using SafeSendLib for address;
    using MathLib for uint;

    struct SerializedRequest {
        address[6] addressValues;
        bool[3] boolValues;
        uint[15] uintValues;
        uint8[1] uint8Values;
    }

    struct Request {
        ExecutionLib.ExecutionData txnData;
        RequestMetaLib.RequestMeta meta;
        PaymentLib.PaymentData paymentData;
        ClaimLib.ClaimData claimData;
        RequestScheduleLib.ExecutionWindow schedule;
        SerializedRequest serializedValues;
    }

    enum AbortReason {
        WasCancelled,
        AlreadyCalled,
        BeforeCallWindow,
        AfterCallWindow,
        ReservedForClaimer,
        StackTooDeep,
        InsufficientGas
    }

    event Cancelled(uint rewardPayment, uint measuredGasConsumption);
    event Claimed();
    // TODO: Figure out how log topics are constructed for events that use
    // Enums as args.
    //event Aborted(AbortReason reason);
    event Aborted(uint8 reason);
    event Executed(uint payment, uint donation, uint measuredGasConsumption);

    /*
     *  Validate the initialization parameters for a transaction request.
     */
    function validate(address[4] addressArgs,
                      uint[11] uintArgs,
                      bytes callData,
                      uint endowment) returns (bool[7] is_valid) {
        Request memory request;

        // callData is special.
        request.txnData.callData = callData;

        // Address values
        request.claimData.claimedBy = 0x0;
        request.meta.createdBy = addressArgs[0];
        request.meta.owner = addressArgs[1];
        request.paymentData.donationBenefactor = addressArgs[2];
        request.paymentData.donationBenefactor = 0x0;
        request.txnData.toAddress = addressArgs[3];

        // Boolean values
        request.meta.isCancelled = false;
        request.meta.wasCalled = false;
        request.meta.wasSuccessful = false;

        // UInt values
        request.claimData.claimDeposit = 0;
        request.paymentData.anchorGasPrice = tx.gasprice;
        request.paymentData.donation = uintArgs[0];
        request.paymentData.payment = uintArgs[1];
        request.paymentData.donationOwed = 0;
        request.paymentData.paymentOwed = 0;
        request.schedule.claimWindowSize = uintArgs[2];
        request.schedule.freezePeriod = uintArgs[3];
        request.schedule.reservedWindowSize = uintArgs[4];
        // This must be capped at 1 or it throws an exception.
        request.schedule.temporalUnit = RequestScheduleLib.TemporalUnit(uintArgs[5].min(1));
        request.schedule.windowStart = uintArgs[6];
        request.schedule.windowSize = uintArgs[7];
        request.txnData.callGas = uintArgs[8];
        request.txnData.callValue = uintArgs[9];
        request.txnData.requiredStackDepth = uintArgs[10];

        // Uint8 values
        request.claimData.paymentModifier = 0;

        // The order of these errors matters as it determines which
        // ValidationError event codes are logged when validation fails.
        is_valid[0] = PaymentLib.validateEndowment(endowment,
                                                   request.paymentData.payment,
                                                   request.paymentData.donation,
                                                   request.txnData.callGas,
                                                   request.txnData.callValue);
        is_valid[1] = RequestScheduleLib.validateReservedWindowSize(request.schedule.reservedWindowSize,
                                                                    request.schedule.windowSize);
        is_valid[2] = RequestScheduleLib.validateTemporalUnit(uintArgs[5]);
        is_valid[3] = RequestScheduleLib.validateWindowStart(request.schedule.temporalUnit,
                                                             request.schedule.freezePeriod,
                                                             request.schedule.windowStart);
        is_valid[4] = ExecutionLib.validateRequiredStackDepth(request.txnData.requiredStackDepth);
        is_valid[5] = ExecutionLib.validateCallGas(request.txnData.callGas, _EXECUTE_EXTRA_GAS);
        is_valid[6] = ExecutionLib.validateToAddress(request.txnData.toAddress);

        return is_valid;
    }

    /*
     *  Initialize a new Request.
     */
    function initialize(Request storage self,
                        address[4] addressArgs,
                        uint[11] uintArgs,
                        bytes callData) returns (bool) {
        address[6] memory addressValues = [
            0x0,             // self.claimData.claimedBy
            addressArgs[0],  // self.meta.createdBy
            addressArgs[1],  // self.meta.owner
            addressArgs[2],  // self.paymentData.donationBenefactor
            0x0,             // self.paymentData.paymentBenefactor
            addressArgs[3]   // self.txnData.toAddress
        ];

        bool[3] memory boolValues = [false, false, false];

        uint[15] memory uintValues = [
            0,               // self.claimData.claimDeposit
            tx.gasprice,     // self.paymentData.anchorGasPrice
            uintArgs[0],     // self.paymentData.donation
            0,               // self.paymentData.donationOwed
            uintArgs[1],     // self.paymentData.payment
            0,               // self.paymentData.paymentOwed
            uintArgs[2],     // self.schedule.claimWindowSize
            uintArgs[3],     // self.schedule.freezePeriod
            uintArgs[4],     // self.schedule.reservedWindowSize
            uintArgs[5],     // self.schedule.temporalUnit
            uintArgs[6],     // self.schedule.windowStart
            uintArgs[7],     // self.schedule.windowSize
            uintArgs[8],     // self.txnData.callGas
            uintArgs[9],     // self.txnData.callValue
            uintArgs[10]     // self.txnData.requiredStackDepth
        ];

        uint8[1] memory uint8Values = [
            0
        ];

        deserialize(self, addressValues, boolValues, uintValues, uint8Values, callData);

        return true;
    }

    /*
     *  Returns the entire data structure of the Request in a *serialized*
     *  format.  This will be missing the `callData` which must be requested
     *  separately
     *
     *  Parameter order is alphabetical by type, then namespace, then name
     *
     *  TODO: figure out why this fails.
     */
    function serialize(Request storage self) returns (bool) {
        // Address values
        self.serializedValues.addressValues[0] = self.claimData.claimedBy;
        self.serializedValues.addressValues[1] = self.meta.createdBy;
        self.serializedValues.addressValues[2] = self.meta.owner;
        self.serializedValues.addressValues[3] = self.paymentData.donationBenefactor;
        self.serializedValues.addressValues[4] = self.paymentData.paymentBenefactor;
        self.serializedValues.addressValues[5] = self.txnData.toAddress;

        // Boolean values
        self.serializedValues.boolValues[0] = self.meta.isCancelled;
        self.serializedValues.boolValues[1] = self.meta.wasCalled;
        self.serializedValues.boolValues[2] = self.meta.wasSuccessful;

        // UInt256 values
        self.serializedValues.uintValues[0] = self.claimData.claimDeposit;
        self.serializedValues.uintValues[1] = self.paymentData.anchorGasPrice;
        self.serializedValues.uintValues[2] = self.paymentData.donation;
        self.serializedValues.uintValues[3] = self.paymentData.donationOwed;
        self.serializedValues.uintValues[4] = self.paymentData.payment;
        self.serializedValues.uintValues[5] = self.paymentData.paymentOwed;
        self.serializedValues.uintValues[6] = self.schedule.claimWindowSize;
        self.serializedValues.uintValues[7] = self.schedule.freezePeriod;
        self.serializedValues.uintValues[8] = self.schedule.reservedWindowSize;
        self.serializedValues.uintValues[9] = uint(self.schedule.temporalUnit);
        self.serializedValues.uintValues[10] = self.schedule.windowStart;
        self.serializedValues.uintValues[11] = self.schedule.windowSize;
        self.serializedValues.uintValues[12] = self.txnData.callGas;
        self.serializedValues.uintValues[13] = self.txnData.callValue;
        self.serializedValues.uintValues[14] = self.txnData.requiredStackDepth;

        // Uint8 values
        self.serializedValues.uint8Values[0] = self.claimData.paymentModifier;

        return true;
    }

    /*
     *  Populates a Request object from the full output of `serialize`.
     *
     *  Parameter order is alphabetical by type, then namespace, then name.
     */
    function deserialize(Request storage self,
                         address[6] addressValues,
                         bool[3] boolValues,
                         uint[15] uintValues,
                         uint8[1] uint8Values,
                         bytes callData) returns (bool) {
        // callData is special.
        self.txnData.callData = callData;

        // Address values
        self.claimData.claimedBy = addressValues[0];
        self.meta.createdBy = addressValues[1];
        self.meta.owner = addressValues[2];
        self.paymentData.donationBenefactor = addressValues[3];
        self.paymentData.paymentBenefactor = addressValues[4];
        self.txnData.toAddress = addressValues[5];

        // Boolean values
        self.meta.isCancelled = boolValues[0];
        self.meta.wasCalled = boolValues[1];
        self.meta.wasSuccessful = boolValues[2];

        // UInt values
        self.claimData.claimDeposit = uintValues[0];
        self.paymentData.anchorGasPrice = uintValues[1];
        self.paymentData.donation = uintValues[2];
        self.paymentData.donationOwed = uintValues[3];
        self.paymentData.payment = uintValues[4];
        self.paymentData.paymentOwed = uintValues[5];
        self.schedule.claimWindowSize = uintValues[6];
        self.schedule.freezePeriod = uintValues[7];
        self.schedule.reservedWindowSize = uintValues[8];
        self.schedule.temporalUnit = RequestScheduleLib.TemporalUnit(uintValues[9]);
        self.schedule.windowStart = uintValues[10];
        self.schedule.windowSize = uintValues[11];
        self.txnData.callGas = uintValues[12];
        self.txnData.callValue = uintValues[13];
        self.txnData.requiredStackDepth = uintValues[14];

        // Uint8 values
        self.claimData.paymentModifier = uint8Values[0];
    }

    function execute(Request storage self) returns (bool) {
        /*
         *  Execute the TransactionRequest
         *
         *  +---------------------+
         *  | Phase 1: Accounting |
         *  +---------------------+
         *
         *  Must pass all of the following checks:
         *
         *  1. Not already called.
         *  2. Not cancelled.
         *  3. Not before the execution window.
         *  4. Not after the execution window.
         *  5. if (claimedBy == 0x0 or msg.sender == claimedBy):
         *         - windowStart <= block.number
         *         - block.number <= windowStart + windowSize
         *     else if (msg.sender != claimedBy):
         *         - windowStart + reservedWindowSize <= block.number
         *         - block.number <= windowStart + windowSize
         *     else:
         *         - throw (should be impossible)
         *  6. if (msg.sender != tx.origin):
         *         - Verify stack can be increased by requiredStackDepth
         *  7. msg.gas >= callGas
         *
         *  +--------------------+
         *  | Phase 2: Execution |
         *  +--------------------+
         *
         *  1. Mark as called (must be before actual execution to prevent
         *     re-entrance.
         *  2. Send Transaction and record success or failure.
         *
         *  +---------------------+
         *  | Phase 3: Accounting |
         *  +---------------------+
         *
         *  1. Calculate and send donation amount.
         *  2. Calculate and send payment amount.
         *  3. Send remaining ether back to owner.
         *
         */
        var startGas = msg.gas;

        // +----------------------+
        // | Begin: Authorization |
        // +----------------------+

        if (msg.gas < requiredExecutionGas(self)) {
            Aborted(uint8(AbortReason.InsufficientGas));
            return false;
        } else if (self.meta.wasCalled) {
            Aborted(uint8(AbortReason.AlreadyCalled));
            return false;
        } else if (self.meta.isCancelled) {
            Aborted(uint8(AbortReason.WasCancelled));
            return false;
        } else if (self.schedule.isBeforeWindow()) {
            Aborted(uint8(AbortReason.BeforeCallWindow));
            return false;
        } else if (self.schedule.isAfterWindow()) {
            Aborted(uint8(AbortReason.AfterCallWindow));
            return false;
        } else if (self.claimData.isClaimed() &&
                   msg.sender != self.claimData.claimedBy &&
                   self.schedule.inReservedWindow()) {
            Aborted(uint8(AbortReason.ReservedForClaimer));
            return false;
        } else if (msg.sender != tx.origin && !self.txnData.stackCanBeExtended()) {
            Aborted(uint8(AbortReason.StackTooDeep));
            return false;
        }

        // +--------------------+
        // | End: Authorization |
        // +--------------------+
        // +------------------+
        // | Begin: Execution |
        // +------------------+

        // Mark as being called before sending transaction to prevent re-entrance
        self.meta.wasCalled = true;

        // Send the transaction
        self.meta.wasSuccessful = self.txnData.sendTransaction();

        // +----------------+
        // | End: Execution |
        // +----------------+
        // +-------------------+
        // | Begin: Accounting |
        // +-------------------+

        // Compute the donation amount
        if (self.paymentData.hasBenefactor()) {
            self.paymentData.donationOwed = self.paymentData.getDonation()
                                                            .safeAdd(self.paymentData.donationOwed);
        }

        // record this so that we can log it later.
        var totalDonationPayment = self.paymentData.donationOwed;

        // Send the donation.
        self.paymentData.sendDonation();

        // Compute the payment amount and who it should be sent do.
        self.paymentData.paymentBenefactor = msg.sender;
        if (self.claimData.isClaimed()) {
            self.paymentData.paymentOwed = self.claimData.claimDeposit.safeAdd(self.paymentData.paymentOwed);
            // need to zero out the claim deposit since it is now accounted for
            // in the paymentOwed value.
            self.claimData.claimDeposit = 0;
            self.paymentData.paymentOwed = self.paymentData.getPaymentWithModifier(self.claimData.paymentModifier)
                                                           .safeAdd(self.paymentData.paymentOwed);
        } else {
            self.paymentData.paymentOwed = self.paymentData.getPayment()
                                                           .safeAdd(self.paymentData.paymentOwed);
        }

        // Record the amount of gas used by execution.
        uint measuredGasConsumption = startGas.flooredSub(msg.gas).safeAdd(_EXECUTE_EXTRA_GAS);

        // +----------------------------------------------------------------------+
        // | NOTE: All code after this must be accounted for by EXECUTE_EXTRA_GAS |
        // +----------------------------------------------------------------------+

        // Add the gas reimbursment amount to the payment.
        self.paymentData.paymentOwed = measuredGasConsumption.safeMultiply(tx.gasprice)
                                                             .safeAdd(self.paymentData.paymentOwed);

        // Log the two payment amounts.  Otherwise it is non-trivial to figure
        // out how much was payed.
        Executed(self.paymentData.paymentOwed,
                 totalDonationPayment,
                 measuredGasConsumption);

        // Send the payment.
        self.paymentData.sendPayment();

        // Send all extra ether back to the owner.
        sendOwnerEther(self);

        // +-----------------+
        // | End: Accounting |
        // +-----------------+

        return true;
    }

    function requiredExecutionGas(Request storage self) returns (uint) {
        return self.txnData.callGas.safeAdd(_GAS_TO_AUTHORIZE_EXECUTION)
                                   .safeAdd(_GAS_TO_COMPLETE_EXECUTION);
    }

    /*
     * The amount of gas needed to do all of the pre execution checks.
     */
    // TODO: measure this.
    uint constant _GAS_TO_AUTHORIZE_EXECUTION = 0;

    function GAS_TO_AUTHORIZE_EXECUTION() returns (uint) {
        return _GAS_TO_AUTHORIZE_EXECUTION;
    }

    /*
     * The amount of gas needed to complete the execute method after
     * the transaction has been sent.
     */
    uint constant _GAS_TO_COMPLETE_EXECUTION = 190000;

    function GAS_TO_COMPLETE_EXECUTION() returns (uint) {
        return _GAS_TO_COMPLETE_EXECUTION;
    }

    /*
     *  The amount of gas used by the portion of the `execute` function
     *  that cannot be accounted for via gas tracking.
     */
    uint constant _EXECUTE_EXTRA_GAS = 145000;

    function EXECUTE_EXTRA_GAS() returns (uint) {
        return _EXECUTE_EXTRA_GAS;
    }

    /*
     *  Return boolean whether the call can be cancelled.  Must satisfy the
     *  following conditions.
     *
     *  1. Not Cancelled
     *  2. either:
     *    * not wasCalled && afterExecutionWindow
     *    * not claimed && beforeFreezeWindow && msg.sender == owner
     */
    function isCancellable(Request storage self) returns (bool) {
        if (self.meta.isCancelled) {
            return false;
        } else if (!self.meta.wasCalled && self.schedule.isAfterWindow()) {
            return true;
        } else if (!self.claimData.isClaimed() && self.schedule.isBeforeFreeze() && msg.sender == self.meta.owner) {
            return true;
        } else {
            return false;
        }
    }

    /*
     *  Constant value to account for the gas usage that cannot be accounted
     *  for using gas-tracking within the `cancel` function.
     */
    uint constant _CANCEL_EXTRA_GAS = 85000;

    function CANCEL_EXTRA_GAS() returns (uint) {
        return _CANCEL_EXTRA_GAS;
    }

    /*
     *  Cancel the transaction request, attempting to send all appropriate
     *  refunds.  To incentivise cancellation by other parties, a small reward
     *  payment is issued to the party that cancels the request if they are not
     *  the owner.
     */
    function cancel(Request storage self) returns (bool) {
        uint startGas = msg.gas;
        uint rewardPayment;
        uint measuredGasConsumption;

        if (!isCancellable(self)) {
            return false;
        }

        // set this here to prevent re-entrance attacks.
        self.meta.isCancelled = true;

        // refund any claim deposit.
        self.claimData.refundDeposit();

        // send a reward to the canceller if it isn't the owner.  This also
        // guarantees that it is being cancelled after the call window since
        // the `isCancellable()` function checks this.
        if (msg.sender != self.meta.owner) {
            rewardPayment = self.paymentData.payment.safeMultiply(self.paymentData.getMultiplier()) / 100 / 100;
            measuredGasConsumption = startGas.flooredSub(msg.gas)
                                             .safeAdd(_CANCEL_EXTRA_GAS);
            rewardPayment = measuredGasConsumption.safeMultiply(tx.gasprice)
                                                  .safeAdd(rewardPayment);
            msg.sender.safeSend(rewardPayment);
        }

        // Log the event
        Cancelled(rewardPayment, measuredGasConsumption);

        // send the remaining ether to the owner.
        sendOwnerEther(self);

        return true;
    }

    /*
     * Return boolean as to whether the request may be claimed.
     */
    function isClaimable(Request storage self) returns (bool) {
        if (self.claimData.isClaimed()) {
            return false;
        } else if (self.meta.isCancelled) {
            return false;
        } else if (!self.schedule.inClaimWindow()) {
            return false;
        } else if (msg.value < ClaimLib.minimumDeposit(self.paymentData.payment)) {
            return false;
        } else {
            return true;
        }
    }

    /*
     * Claim the request
     */
    function claim(Request storage self) returns (bool) {
        if (!isClaimable(self)) {
            if (msg.sender.sendOrThrow(msg.value)) {
                return false;
            }
            throw;
        }
        self.claimData.claim(self.schedule.computePaymentModifier());
        Claimed();
        return true;
    }

    /*
     * Refund claimer deposit.
     */
    function refundClaimDeposit(Request storage self) returns (bool) {
        if (self.meta.isCancelled || self.meta.wasCalled) {
            return self.claimData.refundDeposit(0);
        }
        return false;
    }

    /*
     * Send donation
     */
    function sendDonation(Request storage self) returns (bool) {
        if (self.meta.wasCalled) {
            return self.paymentData.sendDonation(0);
        }
        return false;
    }

    /*
     * Send payment
     */
    function sendPayment(Request storage self) returns (bool) {
        if (self.meta.wasCalled) {
            return self.paymentData.sendPayment(0);
        }
        return false;
    }

    /*
     * Send all extra ether in the request contract back to the owner.
     */
    function sendOwnerEther(Request storage self) returns (bool) {
        return sendOwnerEther(self, SafeSendLib.DEFAULT_SEND_GAS());
    }

    function sendOwnerEther(Request storage self, uint sendGas) returns (bool) {
        if (self.meta.isCancelled || self.meta.wasCalled) {
            self.meta.owner.safeSend(this.balance.flooredSub(self.claimData.claimDeposit)
                                                 .flooredSub(self.paymentData.paymentOwed)
                                                 .flooredSub(self.paymentData.donationOwed),
                                     sendGas);
            return true;
        }
        return false;
    }
}


clean and organize code, implement parameter snapshot handling

pragma solidity 0.4.11;
import "./StandardToken.sol";
// import "./PartialLockVoting.sol";
// import "./Parametrizer.sol";

// to do:
// implement events
// check on delete in solidity
// keep deposit if never challenged (?) if win (?)
// ask about wallet location

contract Registry {

    address public wallet;
    StandardToken public token;

    struct Publisher {
        address owner;
        uint expTime;
        uint deposit;
    }

    struct Application {
        address owner;
        bool challenged;
        uint challengeTime;
        address challenger;
        Param snapshot;
    }

    struct Param {
        // parameters concerning the whitelist and application pool
    	uint minDeposit;
        uint challengeLen;
        uint registryLen;

        // parameters to be passed into the voting contract
        uint commitVoteLen;
        uint revealVoteLen;
        uint majority;

        // parameter representing the scale of how token rewards are distributed
        uint dispensationPct;
    }

    mapping(bytes32 => Publisher) public whitelist;
    mapping(bytes32 => Application) public appPool;
    mapping(uint => bool) public voteProcessed;
    mapping(address => mapping(uint => bool)) public voterInfo;

    function Registry(address _token, address _wallet) {
        token = StandardToken(_token);
        wallet = _wallet;
    }

    // called by an applicant to apply (moves them into the application pool on success)
    function apply(string _domain) {
        bytes32 domainHash = sha3(_domain);
        // applicant must pay the current value of minDeposit
        uint deposit = get("minDeposit");
        // check to prevent repeat applications
        require(appPool[domainHash].owner == 0);
        // check that registry can take sufficient amount of tokens from the applicant
        require(token.allowance(msg.sender, this) >= deposit);
        token.transferFrom(msg.sender, wallet, deposit);
        // initialize application with a snapshot with the current values of all parameters
        initializeSnapshot(_domain);
        appPool[domainHash].challengeTime = now + appPool[domainHash].snapshot[challengeLen];
        appPool[domainHash].owner = msg.sender;
    }

    // called by any adtoken holder to challenge an application to the whitelist and start a vote
    function challenge(string _domain) {
        bytes32 domainHash = sha3(_domain);
        // check that registry can take sufficient amount of tokens from the challenger
        uint deposit = appPool[domainHash].snapshot[minDeposit];
        require(token.allowance(msg.sender, this) >= deposit);
        token.transferFrom(msg.sender, wallet, deposit);
        // prevent someone from challenging an unintialized application, rechallenging,
        // or challenging after the challenge period has ended
        require(appPool[domainHash].owner != 0);
        require(appPool[domainHash].challenged == false);
        require(appPool[domainHash].challengeTime > now);
        // update the application's status
        appPool[domainHash].challenged = true;
        appPool[domainHash].challenger = msg.sender;
        // start a vote
        // poll ID = callVote(voting params);
    }

    function callVote(bytes32 _domainHash) private returns (bool) {
        // event that vote has started
        // ??
    }

    // a one-time function for each completed vote
    // if domain won: domain is moved to the whitelist and applicant is rewarded tokens
    // if domain lost: challenger is rewarded tokens
    function processResult(uint _pollID)
    {
        require(voteProcessed[_pollID] == false);
        // string domain = ??;
        bytes32 domainHash = sha3(domain);
        if (didProposalPass(_pollID)) {
            // add to registry
            add(domain, appPool[domainHash].owner);
            appPool[domainHash].owner = 0;
            // give tokens to applicant based on dist and total tokens
        }
        else {
            appPool[domainHash].owner = 0;
            // give tokens to challenger based on dist and total tokens
        }
        // ensures the result cannot be processed again
        voteProcessed[_pollID] = true;
    }

    // called by each voter to claim their reward for each completed vote
    function claimReward(uint _pollID) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        giveTokens(_pollID, msg.sender);
        // ensures a voter cannot claim tokens again
        voterInfo[msg.sender][_pollID] = true;
    }

    // called to move an applying domain to the whitelist
    // iff the domain's challenge period has passed without a challenge
    function moveToRegistry(string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(_domain, appPool[domainHash].owner);
        appPool[domainHash].owner = 0; // use delete or write a deleter
    }

    // private function to add a domain name to the whitelist
    function add(string _domain, address _owner)  {
        bytes32 domainHash = sha3(_domain);
        uint expiration = appPool[domainHash].snapshot[registryLen];
        whitelist[domainHash].expTime = now + expiration;
        whitelist[domainHash].owner = _owner;
    }

    // checks if a domain name is in the whitelist and unexpired
    function isVerified(string _domain) returns (bool) {
        bytes32 domainHash = sha3(_domain);
        if (whitelist[domainHash].expTime > now) {
            return true;
        }
        else {
            return false;
        }
    }

    // private function to initialize a snapshot of parameters for each application
    function initializeSnapshot(string _domain) {
        bytes32 domainHash = sha3(_domain);
        appPool[domainHash].snapshot[minDeposit] = get("minDeposit");
        appPool[domainHash].snapshot[challengeLen] = get("challengeLen");
        appPool[domainHash].snapshot[registryLen] = get("registryLen");
        appPool[domainHash].snapshot[commitVoteLen] = get("commitVoteLen");
        appPool[domainHash].snapshot[revealVoteLen] = get("revealVoteLen");
        appPool[domainHash].snapshot[majority] = get("majority");
        appPool[domainHash].snapshot[dispensationPct] = get("dispensationPct");
    }

    function giveTokens(uint _pollID, uint _salt) {
        // number of tokens person used to vote / total number of tokens for winning side
        // scale using distribution number
        // give the tokens
        // string domain = ??
        bytes32 domainHash = sha3(domain);
        getNumCorrectInvestment(_pollID, _salt)*appPool[domainHash].snapshot[minDeposit]*(1-dispensationPct)/getTotalNumberOfTokensForWinningOption(_pollID);
    }

    // FOR TESTING
    function toHash(string _domain) returns (bytes32){
        return sha3(_domain);
    }
    function getCurrentTime() returns (uint){
        return now;
    }

}

pragma solidity 0.4.11;
import "./StandardToken.sol";
// import "./PartialLockVoting.sol";
// import "./Parametrizer.sol";

// to do:
// implement events
// update domain name functionality (?)
// check on delete in solidity
// keep deposit if never challenged (?) if win (?)
// implement param - MAKE SURE ALL PARAM ARE STATIC FOR A SINGLE APP, including voting times
// do we need owner in publisher struct?

contract Registry {

    address public wallet;
    // this will later be the parameters from the parametrizer
    uint public expDuration;
    uint public applyCost;
    uint public challengeDuration;
    uint public distributionScale;
    StandardToken public token;

    struct Publisher {
        address owner;
        uint expTime;
        // keep deposit and implement that
    }

    struct Application {
        address owner;
        bool challenged;
        address challenger;
        struct Param snapshot;
    }

    struct Param {
    	uint minDeposit;
        uint challengeLen;
        uint registryLen;
        uint commitVoteLen;
        uint revealVoteLen;
        uint dispensationPct;
        uint proposalThresh;
        uint majority;
    }

    mapping(bytes32 => Publisher) public whitelist;
    mapping(bytes32 => Application) public appPool;
    mapping(uint => bool) public voteProcessed;
    mapping(address => mapping(uint => bool)) public voterInfo;

    function Registry(address _token, address _wallet) {
        token = StandardToken(_token);
        wallet = _wallet;
        // placeholder values, will be instantiated by application
        expDuration = 2000;
        applyCost = 50;
        challengeDuration = 2000;
        distributionScale = 0;
    }

    function add(string _domain)  {
        bytes32 domainHash = sha3(_domain);
        // exp from state
        whitelist[domainHash].expTime = now + expDuration;
        whitelist[domainHash].owner = appPool[domainHash].owner;  // pass in owner for clarity/remove owner
        // whitelist[domainHash].owner = 0x804;
    }

    // for testing purposes
    function toHash(string _domain) returns (bytes32){
        return sha3(_domain);
    }
    function getCurrentTime() returns (uint){
        return now;
    }

    function isVerified(string _domain) returns (bool) {
        bytes32 domainHash = sha3(_domain);
        if (whitelist[domainHash].expTime > now) {
            return true;
        }
        else {
            return false;
        }
    }

    function apply(string _domain) {
        // applyCost = get("minDeposit");
        // prevent repeat applications
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].owner == 0);
        require(token.allowance(msg.sender, this) >= applyCost);
        token.transferFrom(msg.sender, wallet, applyCost);
        appPool[domainHash].challengeTime = now + challengeDuration;
        appPool[domainHash].owner = msg.sender;
        // instantiate snapshot
    }

    function challenge(string _domain) {
        // from snapshot
        require(token.allowance(msg.sender, this) >= applyCost);
        token.transferFrom(msg.sender, wallet, applyCost);
        bytes32 domainHash = sha3(_domain);
        // prevent someone from challenging an unintialized application
        require(appPool[domainHash].owner != 0);
        require(appPool[domainHash].challenged == false);
        require(appPool[domainHash].challengeTime > now);
        appPool[domainHash].challenged = true;
        appPool[domainHash].challenger = msg.sender;
        // callVote();
    }

    function moveToRegistry(string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(_domain);
        appPool[domainHash].owner = 0; // use delete or write a deleter
    }

    //separate move to registry
    function claimReward(uint _pollID) {
    //check if the person claiming has alread claimed
    require(voterInfo[msg.sender][_pollID] == false);
    //check if poll has been moved to registry.
    if (voteProcessed[_pollID] == false) {
        // string domain = ??;
        bytes32 domainHash = sha3(domain);
        if (didProposalPass(_pollID)) {
            // add to registry
            add(domain);
            appPool[domainHash].owner = 0;
        }
        else {
            appPool[domainHash].owner = 0;
            // give tokens to challenger based on dist and total tokens
        }
        voteProcessed[_pollID] == true;
        giveTokens(_pollID, msg.sender);
    }
    else {
        giveTokens(_pollID, msg.sender);
    }
        // if winning vote transfer tokens based on distribution scale, else do nothing
        voterInfo[msg.sender][_pollID] = true;
    }

    function giveTokens(uint _pollID, address _voter) {
     // number of tokens person used to vote / total number of tokens for winning side
     // scale using distribution number
     // give the tokens
    }

    function callVote(bytes32 _domainHash) private returns (bool) {
     // event that vote has started
     // ??
    }

}


work in progress

work in progress

pragma solidity ^0.4.13;

contract Owned {
    address public owner;

    function Owned() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address newOwner) onlyOwner {
        owner = newOwner;
    }
}

contract BankManager is Owned {
    address public bankManager;

    function BankManager() {
        bankManager = msg.sender;
    }

    modifier onlyBankManager {
        require(msg.sender == bankManager);
        _;
    }

    function newBankManager(address newManager) onlyOwner {
        bankManager = newManager;
    }
}

contract MonopolyGame {
    mapping (address => address) public games;

    function newGame (string tokenName, string tokenSymbol) {
        uint256 monopolyTotalMoneyValue = 15140;
        uint8 decimals = 0;

        address newGameAddress = new MonopolyBank(monopolyTotalMoneyValue, tokenName, decimals, tokenSymbol, msg.sender);
        games[msg.sender] = newGameAddress;
    }
}

contract MonopolyBank is Owned, BankManager {
    /* Public variables of the token */
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;

    address bankAddress = 0x0;
    address fineAddress = 0x0;

    struct Charm {
        bool assigned;
        address addr;
    }

    mapping (string => Charm) charmOwners;

    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;

    /* This generates a public event on the blockchain that will notify clients */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MonopolyBank(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol,
        address owner
        ) {
        balanceOf[bankAddress] = initialSupply;                        // Update total supply
        totalSupply = initialSupply;                        // Update total supply
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
        decimals = decimalUnits;                            // Amount of decimals for display purposes
        owner = owner;

        resetGame();
    }

    /* Internal transfer, only can be called by this contract */
    function _transfer(address _from, address _to, uint _value) internal {
        require (balanceOf[_from] > _value);                // Check if the sender has enough
        require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        balanceOf[_from] -= _value;                         // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        Transfer(_from, _to, _value);
    }

    function _assignCharm(string charmName, address _to) internal onlyOwner {
        charmOwners[charmName] = Charm({assigned:true, addr: _to});
    }

    function _payToCharm(address _from, string charmName, uint value) internal {
        require(charmOwners[charmName].assigned);
        _transfer(_from, charmOwners[charmName].addr, value);
    }

    function resetGame() onlyOwner {
        balanceOf[bankAddress] = totalSupply;
        balanceOf[fineAddress] = 0;
        newBankManager(owner);

        balanceOf[charmOwners["Battleship"].addr] = 0;
        balanceOf[charmOwners["Boot"].addr] = 0;
        balanceOf[charmOwners["Scottie"].addr] = 0;
        balanceOf[charmOwners["Iron"].addr] = 0;
        balanceOf[charmOwners["Racecar"].addr] = 0;
        balanceOf[charmOwners["Hat"].addr] = 0;
        balanceOf[charmOwners["Thimble"].addr] = 0;
        balanceOf[charmOwners["Wheelbarrow"].addr] = 0;

        charmOwners["Battleship"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Boot"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Scottie"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Iron"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Racecar"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Hat"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Thimble"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Wheelbarrow"] = Charm({assigned:false, addr: 0x0});
    }

    // Assigning addresses to the different charms
    // PZ: shit way to do this
    function setBattleship(address _to) {
        _assignCharm("Battleship", _to);
    }

    function setBoot(address _to) {
        _assignCharm("Boot", _to);
    }

    function setScottie(address _to) {
        _assignCharm("Scottie", _to);
    }

    function setIron(address _to) {
        _assignCharm("Iron", _to);
    }

    function setRacecar(address _to) {
        _assignCharm("Racecar", _to);
    }

    function setHat(address _to) {
        _assignCharm("Hat", _to);
    }

    function setThimble(address _to) {
        _assignCharm("Thimble", _to);
    }

    function setWheelbarrow(address _to) {
        _assignCharm("Wheelbarrow", _to);
    }

    // Pay to a charm
    // PZ: shit way to do this
    function payBattleship(uint value) {
        _payToCharm(msg.sender, "Battleship", value);
    }

    function payBoot(uint value) {
        _payToCharm(msg.sender, "Boot", value);
    }

    function payScottie(uint value) {
        _payToCharm(msg.sender, "Scottie", value);
    }

    function payIron(uint value) {
        _payToCharm(msg.sender, "Iron", value);
    }

    function payRacecar(uint value) {
        _payToCharm(msg.sender, "Racecar", value);
    }

    function payHat(uint value) {
        _payToCharm(msg.sender, "Hat", value);
    }

    function payThimble(uint value) {
        _payToCharm(msg.sender, "Thimble", value);
    }

    function payWheelbarrow(uint value) {
        _payToCharm(msg.sender, "Wheelbarrow", value);
    }

    function payBank(uint value) {
        _transfer(msg.sender, bankAddress, value);
    }

    function payFine(uint value) {
        _transfer(msg.sender, fineAddress, value);
    }

    function claimFine() {
        _transfer(fineAddress, msg.sender, balanceOf[fineAddress]);
    }

    // Pay to a charm
    // PZ: shit way to do this
    function payBattleshipFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Battleship", value);
    }

    function payBootFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Boot", value);
    }

    function payScottieFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Scottie", value);
    }

    function payIronFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Iron", value);
    }

    function payRacecarFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Racecar", value);
    }

    function payHatFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Hat", value);
    }

    function payThimbleFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Thimble", value);
    }

    function payWheelbarrowFromBank(uint value) onlyBankManager {
        _payToCharm(bankAddress, "Wheelbarrow", value);
    }

}

pragma solidity ^0.4.13;

contract Owned {
    address public owner;

    function Owned() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address newOwner) onlyOwner {
        owner = newOwner;
    }
}

contract MonopolyGame {
    mapping (address => address) public games;

    function newGame (string tokenName, string tokenSymbol) {
        uint256 monopolyTotalMoneyValue = 15140;
        uint8 decimals = 0;

        address newGameAddress = new MonopolyBank(monopolyTotalMoneyValue, tokenName, decimals, tokenSymbol, msg.sender);
        games[msg.sender] = newGameAddress;
    }
}

contract MonopolyBank is Owned {
    /* Public variables of the token */
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;

    struct Charm {
        bool assigned;
        address addr;
    }

    mapping (string => Charm) public charmOwners;

    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;

    /* This generates a public event on the blockchain that will notify clients */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MonopolyBank(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol,
        address owner
        ) {
        balanceOf[owner] = initialSupply;              // Give the creator all initial tokens
        totalSupply = initialSupply;                        // Update total supply
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
        decimals = decimalUnits;                            // Amount of decimals for display purposes
        owner = owner;

        resetGame();
    }

    /* Internal transfer, only can be called by this contract */
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
        require (balanceOf[_from] > _value);                // Check if the sender has enough
        require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        balanceOf[_from] -= _value;                         // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        Transfer(_from, _to, _value);
    }

    function _assignCharm(string charmName, address _to) internal onlyOwner {
        charmOwners[charmName] = Charm({assigned:true, addr: _to});
    }

    function _payToCharm(string charmName, uint value) internal {
        require(charmOwners[charmName].assigned);
        _transfer(msg.sender, charmOwners[charmName].addr, value);
    }

    function resetGame() onlyOwner {
        charmOwners["Battleship"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Boot"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Scottie"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Iron"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Racecar"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Hat"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Thimble"] = Charm({assigned:false, addr: 0x0});
        charmOwners["Wheelbarrow"] = Charm({assigned:false, addr: 0x0});
    }

    // Assigning addresses to the different charms
    // PZ: shit way to do this
    function setBattleship(address _to) {
        _assignCharm("Battleship", _to);
    }

    function setBoot(address _to) {
        _assignCharm("Boot", _to);
    }

    function setScottie(address _to) {
        _assignCharm("Scottie", _to);
    }

    function setIron(address _to) {
        _assignCharm("Iron", _to);
    }

    function setRacecar(address _to) {
        _assignCharm("Racecar", _to);
    }

    function setHat(address _to) {
        _assignCharm("Hat", _to);
    }

    function setThimble(address _to) {
        _assignCharm("Thimble", _to);
    }

    function setWheelbarrow(address _to) {
        _assignCharm("Wheelbarrow", _to);
    }

    // Pay to a charm
    // PZ: shit way to do this
    function payBattleship(uint value) {
        _payToCharm("Battleship", value);
    }

    function payBoot(uint value) {
        _payToCharm("Boot", value);
    }

    function payScottie(uint value) {
        _payToCharm("Scottie", value);
    }

    function payIron(uint value) {
        _payToCharm("Iron", value);
    }

    function payRacecar(uint value) {
        _payToCharm("Racecar", value);
    }

    function payHat(uint value) {
        _payToCharm("Hat", value);
    }

    function payThimble(uint value) {
        _payToCharm("Thimble", value);
    }

    function payWheelbarrow(uint value) {
        _payToCharm("Wheelbarrow", value);
    }

}


[chore] Quota block limit

[chore] Quota block limit

pragma solidity ^0.4.14;

import "./strings.sol";
import "./quota_interface.sol";

contract QuotaManager is QuotaInterface {

    using strings for *;

    mapping (address => bool) admins;
    mapping (bytes32 => bool) is_global;
    mapping (bytes32 => bytes32) global;
    mapping (address => mapping(bytes32 => bytes32)) special;
    address[] special_users;
    bytes32[] users_quota;

    modifier onlyAdmin {
        require(admins[msg.sender]);
        _;
    }

    modifier checkLimit(uint _v) {
        uint maxLimit = 2 ** 63 - 1;
        uint baseLimit = 2 ** 22 - 1;
        require(_v <= maxLimit && _v >= baseLimit);
        _;
    }

    modifier checkBlockLimit(uint _v) {
        uint blockLimit = 2 ** 28 -1;
        require(_v > blockLimit);
        _;
    }

    function QuotaManager(address _account) public {
        admins[_account] = true;
        is_global["blockGasLimit"] = true;
        global["blockGasLimit"] = bytes32(1073741824);
        global["accountGasLimit"] = bytes32(268435456);
        special[_account]["accountGasLimit"] = bytes32(1073741824);
        special_users.push(_account);
        users_quota.push(bytes32(1073741824));
    }

    function addAdmin(address _account)
        public
        onlyAdmin
        returns (bool)
    {
        admins[_account] = true;
        AddAdminEvent(_account, msg.sender);
    }

    function setIsGlobal(bytes32 key, bool value)
        public
        onlyAdmin
        returns (bool)
    {
        is_global[key] = value;
        SetIsGlobalEvent(key, value, msg.sender);
        return true;
    }

    function setGlobal(bytes32 key, bytes32 value)
        public
        onlyAdmin
        returns (bool)
    {
        global[key] = value;
        SetGlobalEvent(key, value, msg.sender);
        return true;
    }

    function setSpecial(address _account, bytes32 key, bytes32 value)
        public
        onlyAdmin
        returns (bool)
    {
        special[_account][key] = value;
        SetSpecialEvent(
            _account,
            key,
            value,
            msg.sender
        );
        return true;
    }

    function setBlockGasLimit(uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        checkBlockLimit(_value)
        returns (bool)
    {
        bytes32 value = bytes32(_value);
        global["blockGasLimit"] = value;
        SetGlobalEvent(bytes32("blockGasLimit"), value, msg.sender);
        return true;
    }

    function setGlobalAccountGasLimit(uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        returns (bool)
    {
        bytes32 value = bytes32(_value);
        global["accountGasLimit"] = bytes32(value);
        SetGlobalEvent(bytes32("accountGasLimit"), bytes32(value), msg.sender);
        return true;
    }

    function setAccountGasLimit(address _account, uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        returns (bool)
    {
        bytes32 key = bytes32("accountGasLimit");
        bytes32 value = bytes32(_value);
        special[_account]["accountGasLimit"] = value;
        special_users.push(_account);
        users_quota.push(bytes32(value));
        SetSpecialEvent(
            _account,
            key,
            value,
            msg.sender
        );
        return true;
    }

    /// Cancat bytes32
    function concatBytes(bytes32[] _users)
        pure
        internal
        returns (string bytes32List)
    {
        if (_users.length > 0)
            bytes32List = bytes32ToString(_users[0]);

        for (uint i = 1; i < _users.length; i++)
            bytes32List = bytes32List.toSlice().concat(bytes32ToString(_users[i]).toSlice());
    }

    /// Cancat address
    function concatUser(address[] _users)
        pure
        internal
        returns (string userList)
    {
        if (_users.length > 0)
            userList = toString(_users[0]);

        for (uint i = 1; i < _users.length; i++)
            userList = userList.toSlice().concat(toString(_users[i]).toSlice());
    }

    function _getData(bytes32 key)
        view
        internal
        returns (bytes32)
    {
        bytes32 blank;
        if (special[msg.sender][key] != blank)
            return special[msg.sender][key];
        else
            return global[key];
    }

    /// Address to string
    /// The returned string is ABI encoded
    function toString(address x)
        pure
        internal
        returns (string)
    {
        bytes memory b = new bytes(20);

        for (uint i = 0; i < 20; i++)
            b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));

        return string(b);
    }

    function bytes32ToString(bytes32 x)
        pure
        internal
        returns (string)
    {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;

        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }

        bytes memory bytesStringTrimmed = new bytes(charCount);

        for (j = 0; j < charCount; j++)
            bytesStringTrimmed[j] = bytesString[j];

        return string(bytesStringTrimmed);
    }

    function isAdmin(address _account)
        view
        public
        returns (bool)
    {
        return admins[_account];
    }

    function getData(bytes32 key)
        view
        public
        returns (bytes32)
    {
        if (is_global[key])
            return global[key];
        else
            return _getData(key);
    }

    function getSpecialUsers()
        view
        public
        returns (string)
    {
        return concatUser(special_users);
    }

    function getUsersQuota()
        view
        public
        returns (string)
    {
        return concatBytes(users_quota);
    }

    function getblockGasLimit()
        view
        public
        returns (bytes32)
    {
        return global["blockGasLimit"];
    }

    function getAccountGasLimit()
        view
        public
        returns (bytes32)
    {
        return global["accountGasLimit"];
    }

    function getAccountQuota(address _user)
        view
        public
        returns (bytes32)
    {
        // Not special users, then return accountGasLimit
        // or query the special users array
        if (special[_user]["accountGasLimit"] == bytes32(0))
            return global["accountGasLimit"];
        return special[_user]["accountGasLimit"];
    }
}

pragma solidity ^0.4.14;

import "./strings.sol";
import "./quota_interface.sol";

contract QuotaManager is QuotaInterface {

    using strings for *;

    mapping (address => bool) admins;
    mapping (bytes32 => bool) is_global;
    mapping (bytes32 => bytes32) global;
    mapping (address => mapping(bytes32 => bytes32)) special;
    address[] special_users;
    bytes32[] users_quota;

    modifier onlyAdmin {
        require(admins[msg.sender]);
        _;
    }

    modifier checkLimit(uint _v) {
        uint maxLimit = 2 ** 63 - 1;
        uint baseLimit = 2 ** 22 - 1;
        require(_v <= maxLimit && _v >= baseLimit);
        _;
    }

    modifier checkBlockLimit(uint _v) {
        uint blockLimit = 2 ** 25 -1;
        require(_v >= blockLimit);
        _;
    }

    function QuotaManager(address _account) public {
        admins[_account] = true;
        is_global["blockGasLimit"] = true;
        global["blockGasLimit"] = bytes32(1073741824);
        global["accountGasLimit"] = bytes32(268435456);
        special[_account]["accountGasLimit"] = bytes32(1073741824);
        special_users.push(_account);
        users_quota.push(bytes32(1073741824));
    }

    function addAdmin(address _account)
        public
        onlyAdmin
        returns (bool)
    {
        admins[_account] = true;
        AddAdminEvent(_account, msg.sender);
    }

    function setIsGlobal(bytes32 key, bool value)
        public
        onlyAdmin
        returns (bool)
    {
        is_global[key] = value;
        SetIsGlobalEvent(key, value, msg.sender);
        return true;
    }

    function setGlobal(bytes32 key, bytes32 value)
        public
        onlyAdmin
        returns (bool)
    {
        global[key] = value;
        SetGlobalEvent(key, value, msg.sender);
        return true;
    }

    function setSpecial(address _account, bytes32 key, bytes32 value)
        public
        onlyAdmin
        returns (bool)
    {
        special[_account][key] = value;
        SetSpecialEvent(
            _account,
            key,
            value,
            msg.sender
        );
        return true;
    }

    function setBlockGasLimit(uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        returns (bool)
    {
        bytes32 value = bytes32(_value);
        global["blockGasLimit"] = value;
        SetGlobalEvent(bytes32("blockGasLimit"), value, msg.sender);
        return true;
    }

    function setGlobalAccountGasLimit(uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        returns (bool)
    {
        bytes32 value = bytes32(_value);
        global["accountGasLimit"] = bytes32(value);
        SetGlobalEvent(bytes32("accountGasLimit"), bytes32(value), msg.sender);
        return true;
    }

    function setAccountGasLimit(address _account, uint _value)
        public
        onlyAdmin
        checkLimit(_value)
        returns (bool)
    {
        bytes32 key = bytes32("accountGasLimit");
        bytes32 value = bytes32(_value);
        special[_account]["accountGasLimit"] = value;
        special_users.push(_account);
        users_quota.push(bytes32(value));
        SetSpecialEvent(
            _account,
            key,
            value,
            msg.sender
        );
        return true;
    }

    /// Cancat bytes32
    function concatBytes(bytes32[] _users)
        pure
        internal
        returns (string bytes32List)
    {
        if (_users.length > 0)
            bytes32List = bytes32ToString(_users[0]);

        for (uint i = 1; i < _users.length; i++)
            bytes32List = bytes32List.toSlice().concat(bytes32ToString(_users[i]).toSlice());
    }

    /// Cancat address
    function concatUser(address[] _users)
        pure
        internal
        returns (string userList)
    {
        if (_users.length > 0)
            userList = toString(_users[0]);

        for (uint i = 1; i < _users.length; i++)
            userList = userList.toSlice().concat(toString(_users[i]).toSlice());
    }

    function _getData(bytes32 key)
        view
        internal
        returns (bytes32)
    {
        bytes32 blank;
        if (special[msg.sender][key] != blank)
            return special[msg.sender][key];
        else
            return global[key];
    }

    /// Address to string
    /// The returned string is ABI encoded
    function toString(address x)
        pure
        internal
        returns (string)
    {
        bytes memory b = new bytes(20);

        for (uint i = 0; i < 20; i++)
            b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));

        return string(b);
    }

    function bytes32ToString(bytes32 x)
        pure
        internal
        returns (string)
    {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;

        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }

        bytes memory bytesStringTrimmed = new bytes(charCount);

        for (j = 0; j < charCount; j++)
            bytesStringTrimmed[j] = bytesString[j];

        return string(bytesStringTrimmed);
    }

    function isAdmin(address _account)
        view
        public
        returns (bool)
    {
        return admins[_account];
    }

    function getData(bytes32 key)
        view
        public
        returns (bytes32)
    {
        if (is_global[key])
            return global[key];
        else
            return _getData(key);
    }

    function getSpecialUsers()
        view
        public
        returns (string)
    {
        return concatUser(special_users);
    }

    function getUsersQuota()
        view
        public
        returns (string)
    {
        return concatBytes(users_quota);
    }

    function getblockGasLimit()
        view
        public
        returns (bytes32)
    {
        return global["blockGasLimit"];
    }

    function getAccountGasLimit()
        view
        public
        returns (bytes32)
    {
        return global["accountGasLimit"];
    }

    function getAccountQuota(address _user)
        view
        public
        returns (bytes32)
    {
        // Not special users, then return accountGasLimit
        // or query the special users array
        if (special[_user]["accountGasLimit"] == bytes32(0))
            return global["accountGasLimit"];
        return special[_user]["accountGasLimit"];
    }
}