"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const avatarService_1 = require("../avatarService");
const commonTypes_1 = require("../commonTypes");
const configService_1 = require("../configService");
const contractWrapperBase_1 = require("../contractWrapperBase");
const contractWrapperFactory_1 = require("../contractWrapperFactory");
const transactionService_1 = require("../transactionService");
const utils_1 = require("../utils");
class GenesisProtocolWrapper extends contractWrapperBase_1.ContractWrapperBase {
    constructor() {
        super(...arguments);
        this.name = "GenesisProtocol";
        this.friendlyName = "Genesis Protocol";
        this.factory = exports.GenesisProtocolFactory;
        /**
         * Events
         */
        /* tslint:disable:max-line-length */
        this.NewProposal = this.createEventFetcherFactory("NewProposal");
        this.ExecuteProposal = this.createEventFetcherFactory("ExecuteProposal");
        this.VoteProposal = this.createEventFetcherFactory("VoteProposal");
        this.Stake = this.createEventFetcherFactory("Stake");
        this.Redeem = this.createEventFetcherFactory("Redeem");
        this.RedeemDaoBounty = this.createEventFetcherFactory("RedeemDaoBounty");
        this.RedeemReputation = this.createEventFetcherFactory("RedeemReputation");
    }
    /* tslint:enable:max-line-length */
    /**
     * Create a proposal
     * @param {ProposeVoteConfig} options
     * @returns Promise<ArcTransactionProposalResult>
     */
    propose(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * see GenesisProtocolProposeVoteConfig
             */
            const defaults = {
                numOfChoices: 0,
                proposer: yield utils_1.Utils.getDefaultAccount(),
            };
            options = Object.assign({}, defaults, options);
            if (!options.avatar) {
                throw new Error("avatar is not defined");
            }
            if (!options.executable) {
                throw new Error("executable is not defined");
            }
            if (!options.proposer) {
                throw new Error("proposer is not defined");
            }
            if ((options.numOfChoices < 1) || (options.numOfChoices > 10)) {
                throw new Error("numOfChoices must be between 1 and 10");
            }
            this.logContractFunctionCall("GenesisProtocol.propose", options);
            const txResult = yield this.wrapTransactionInvocation("GenesisProtocol.propose", options, () => {
                return this.contract.propose(options.numOfChoices, utils_1.Utils.NULL_HASH, options.avatar, options.executable, options.proposer);
            });
            return new contractWrapperBase_1.ArcTransactionProposalResult(txResult.tx);
        });
    }
    /**
     * Stake some tokens on the final outcome matching this vote.
     *
     * A transfer of tokens from the staker to this GenesisProtocol scheme
     * is automatically approved and executed on the token with which
     * this GenesisProtocol scheme was deployed.
     *
     * @param {StakeConfig} options
     * @returns Promise<ArcTransactionResult>
     */
    stake(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const defaults = {
                onBehalfOf: null,
            };
            options = Object.assign({}, defaults, options);
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this._validateVote(options.vote, options.proposalId);
            const web3 = yield utils_1.Utils.getWeb3();
            const amount = web3.toBigNumber(options.amount);
            if (amount.lte(0)) {
                throw new Error("amount must be > 0");
            }
            const autoApproveTransfer = configService_1.ConfigService.get("autoApproveTokenTransfers");
            const eventTopic = "txReceipts.GenesisProtocol.stake";
            const txReceiptEventPayload = transactionService_1.TransactionService.publishKickoffEvent(eventTopic, options, 1 + (autoApproveTransfer ? 1 : 0));
            let tx;
            /**
             * approve immediate transfer of staked tokens from onBehalfOf to this scheme
             */
            if (autoApproveTransfer) {
                const token = yield (yield utils_1.Utils.requireContract("StandardToken")).at(yield this.contract.stakingToken());
                tx = yield token.approve(this.address, amount, { from: options.onBehalfOf ? options.onBehalfOf : yield utils_1.Utils.getDefaultAccount() });
                transactionService_1.TransactionService.publishTxEvent(eventTopic, txReceiptEventPayload, tx);
            }
            this.logContractFunctionCall("GenesisProtocol.stake", options);
            tx = yield this.contract.stake(options.proposalId, options.vote, amount, options.onBehalfOf ? {
                from: options.onBehalfOf ? options.onBehalfOf :
                    yield utils_1.Utils.getDefaultAccount(),
            } : undefined);
            transactionService_1.TransactionService.publishTxEvent(eventTopic, txReceiptEventPayload, tx);
            return new contractWrapperBase_1.ArcTransactionResult(tx);
        });
    }
    /**
     * Vote on a proposal
     * @param {VoteConfig} options
     * @returns Promise<ArcTransactionResult>
     */
    vote(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const defaults = {
                onBehalfOf: null,
            };
            options = Object.assign({}, defaults, options);
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this._validateVote(options.vote, options.proposalId);
            this.logContractFunctionCall("GenesisProtocol.vote", options);
            return this.wrapTransactionInvocation("GenesisProtocol.vote", options, () => {
                return this.contract.vote(options.proposalId, options.vote, options.onBehalfOf ? { from: options.onBehalfOf } : undefined);
            });
        });
    }
    /**
     * Vote on a proposal, staking some reputation that the final outcome will match this vote.
     * Reputation of 0 will stake all the voter's reputation.
     * @param {VoteWithSpecifiedAmountsConfig} options
     * @returns Promise<ArcTransactionResult>
     */
    voteWithSpecifiedAmounts(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this._validateVote(options.vote, options.proposalId);
            if (!options.reputation) {
                throw new Error("reputation is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.voteWithSpecifiedAmounts", options);
            return this.wrapTransactionInvocation("GenesisProtocol.voteWithSpecifiedAmounts", options, () => {
                return this.contract.voteWithSpecifiedAmounts(options.proposalId, options.vote, options.reputation, 0);
            });
        });
    }
    /**
     * Redeem any tokens and reputation, excluding bounty, that are due the beneficiary from the outcome of the proposal.
     * @param {RedeemConfig} options
     * @returns Promise<ArcTransactionResult>
     */
    redeem(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            const proposalState = yield this.getState({ proposalId: options.proposalId });
            if ((proposalState !== ProposalState.Executed) &&
                (proposalState !== ProposalState.Closed)) {
                /* tslint:disable-next-line:max-line-length */
                throw new Error(`cannot redeem unless proposal state is either executed or closed. Current state: ${ProposalState[proposalState]}`);
            }
            this.logContractFunctionCall("GenesisProtocol.redeem", options);
            return this.wrapTransactionInvocation("GenesisProtocol.redeem", options, () => {
                return this.contract.redeem(options.proposalId, options.beneficiaryAddress);
            });
        });
    }
    /**
     * Redeem any token bounty that are due the beneficiary from the outcome of the proposal.
     * @param {RedeemConfig} options
     * @returns Promise<ArcTransactionResult>
     */
    redeemDaoBounty(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.redeemDaoBounty", options);
            const proposalState = yield this.getState({ proposalId: options.proposalId });
            if ((proposalState !== ProposalState.Executed) &&
                (proposalState !== ProposalState.Closed)) {
                throw new Error("cannot redeem bounty unless proposal state is either executed or closed");
            }
            return this.wrapTransactionInvocation("GenesisProtocol.redeemDaoBounty", options, () => {
                return this.contract.redeemDaoBounty(options.proposalId, options.beneficiaryAddress);
            });
        });
    }
    /**
     * Return whether a proposal should be shifted to the boosted phase.
     * @param {ShouldBoostConfig} options
     * @returns Promise<boolean>
     */
    shouldBoost(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.shouldBoost", options);
            return this.contract.shouldBoost(options.proposalId);
        });
    }
    /**
     * Return the current proposal score.
     * @param {GetScoreConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getScore(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.score", options);
            // TODO:  convert to a number?
            return this.contract.score(options.proposalId);
        });
    }
    /**
     * Return the threshold that is required by a proposal to it shift it into boosted state.
     * The computation depends on the current number of boosted proposals in the DAO
     * as well as the GenesisProtocol parameters thresholdConstA and thresholdConstB.
     * @param {GetThresholdConfig} options
     */
    getThreshold(options = {}) {
        if (!options.avatar) {
            throw new Error("avatar is not defined");
        }
        if (!options.proposalId) {
            throw new Error("proposalId is not defined");
        }
        this.logContractFunctionCall("GenesisProtocol.threshold", options);
        return this.contract.threshold(options.proposalId, options.avatar);
    }
    /**
     * Return the reputation amount to which the proposal proposer is entitled in the event that the proposal is approved.
     * @param {GetRedeemableReputationProposerConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableReputationProposer(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableReputationProposer", options);
            return this.contract.getRedeemableReputationProposer(options.proposalId);
        });
    }
    /**
     * Return the token amount to which the voter is entitled in the event that the proposal is approved.
     * @param {GetRedeemableTokensVoterConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableTokensVoter(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableTokensVoter", options);
            return this.contract.getRedeemableTokensVoter(options.proposalId, options.beneficiaryAddress);
        });
    }
    /**
     * Return the reputation amount to which the voter is entitled in the event that the proposal is approved.
     * @param {GetRedeemableReputationVoterConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableReputationVoter(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableReputationVoter", options);
            return this.contract.getRedeemableReputationVoter(options.proposalId, options.beneficiaryAddress);
        });
    }
    /**
     * Return the token amount to which the given staker is entitled in the event that the proposal is approved.
     * @param {GetRedeemableRewardsStakerConfig} opts
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableTokensStaker(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableTokensStaker", options);
            return this.contract.getRedeemableTokensStaker(options.proposalId, options.beneficiaryAddress);
        });
    }
    /**
     * Return the token amount of bounty to which the given staker is entitled in the event that the proposal is approved.
     * @param {GetRedeemableRewardsStakerConfig} opts
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableTokensStakerBounty(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableTokensStakerBounty", options);
            return this.contract.getRedeemableTokensStakerBounty(options.proposalId, options.beneficiaryAddress);
        });
    }
    /**
     * Return the reputation amount to which the staker is entitled in the event that the proposal is approved.
     * @param {GetRedeemableReputationStakerConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getRedeemableReputationStaker(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiaryAddress is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getRedeemableReputationStaker", options);
            return this.contract.getRedeemableReputationStaker(options.proposalId, options.beneficiaryAddress);
        });
    }
    /**
     * Return the current balances on this GenesisProtocol's staking and the given avatar's native tokens.
     * This can be useful, for example, if you want to know in advance whether the avatar has enough funds
     * at the moment to payout rewards to stakers and voters.
     * It also returns the respective tokens' truffle contracts.
     * @param options
     */
    getTokenBalances(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.avatarAddress) {
                throw new Error("avatarAddress is not defined");
            }
            const stakingTokenAddress = yield this.contract.stakingToken();
            const stakingToken = yield (yield utils_1.Utils.requireContract("StandardToken")).at(stakingTokenAddress);
            const stakingTokenBalance = yield stakingToken.balanceOf(options.avatarAddress);
            const avatarService = new avatarService_1.AvatarService(options.avatarAddress);
            const nativeToken = yield avatarService.getNativeToken();
            const nativeTokenBalance = yield nativeToken.balanceOf(options.avatarAddress);
            return {
                nativeToken,
                nativeTokenBalance,
                stakingToken,
                stakingTokenBalance,
            };
        });
    }
    /**
     * Return the number of possible choices when voting for the proposal.
     * @param {GetNumberOfChoicesConfig} options
     * @returns Promise<number>
     */
    getNumberOfChoices(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.getNumberOfChoices", options);
            const numOfChoices = yield this.contract.getNumberOfChoices(options.proposalId);
            return numOfChoices.toNumber();
        });
    }
    /**
     * Return the vote and the amount of reputation of the voter committed to this proposal
     * @param {GetVoterInfoResult} options
     * @returns Promise<GetVoterInfoResult>
     */
    getVoterInfo(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.voter) {
                throw new Error("voter is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.voteInfo", options);
            const result = yield this.contract.voteInfo(options.proposalId, options.voter);
            return {
                reputation: result[1],
                vote: result[0].toNumber(),
            };
        });
    }
    /**
     * Returns the reputation currently voted on the given choice.
     * @param {GetVoteStatusConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getVoteStatus(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this._validateVote(options.vote, options.proposalId);
            this.logContractFunctionCall("GenesisProtocol.voteStatus", options);
            /**
             * an array of number counts for each vote choice
             */
            return this.contract.voteStatus(options.proposalId, options.vote);
        });
    }
    /**
     * Return whether the proposal is in a votable state.
     * @param {IsVotableConfig} options
     * @returns Promise<boolean>
     */
    isVotable(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.isVotable", options);
            return this.contract.isVotable(options.proposalId);
        });
    }
    /**
     * Return the total votes, total staked, voter stakes and staker stakes for a given proposal
     * @param {GetProposalStatusConfig} options
     * @returns Promise<GetProposalStatusResult>
     */
    getProposalStatus(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.proposalStatus", options);
            const result = yield this.contract.proposalStatus(options.proposalId);
            return {
                totalStaked: result[2],
                totalStakerStakes: result[1],
                totalVoterStakes: result[3],
                totalVotes: result[0],
            };
        });
    }
    /**
     * Return the DAO avatar address under which the proposal was made
     * @param {GetProposalAvatarConfig} options
     * @returns Promise<string>
     */
    getProposalAvatar(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.proposalAvatar", options);
            return this.contract.proposalAvatar(options.proposalId);
        });
    }
    /**
     * Return the score threshold params for the given DAO.
     * @param {GetScoreThresholdParamsConfig} options
     * @returns Promise<GetScoreThresholdParamsResult>
     */
    getScoreThresholdParams(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.avatar) {
                throw new Error("avatar is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.scoreThresholdParams", options);
            const result = yield this.contract.scoreThresholdParams(options.avatar);
            return {
                thresholdConstA: result[0],
                thresholdConstB: result[1].toNumber(),
            };
        });
    }
    /**
     * Return the vote and stake amount for a given proposal and staker.
     * @param {GetStakerInfoConfig} options
     * @returns Promise<GetStakerInfoResult>
     */
    getStakerInfo(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            if (!options.staker) {
                throw new Error("staker is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.staker", options);
            const result = yield this.contract.staker(options.proposalId, options.staker);
            return {
                stake: result[1],
                vote: result[0].toNumber(),
            };
        });
    }
    /**
     * Return the amount stakes behind a given proposal and vote.
     * @param {GetVoteStakeConfig} options
     * @returns Promise<BigNumber.BigNumber>
     */
    getVoteStake(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this._validateVote(options.vote, options.proposalId);
            this.logContractFunctionCall("GenesisProtocol.voteStake", options);
            return this.contract.voteStake(options.proposalId, options.vote);
        });
    }
    /**
     * Return the winningVote for a given proposal.
     * @param {GetWinningVoteConfig} options
     * @returns Promise<number>
     */
    getWinningVote(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.winningVote", options);
            const winningVote = yield this.contract.winningVote(options.proposalId);
            return winningVote.toNumber();
        });
    }
    /**
     * Return the current state of a given proposal.
     * @param {GetStateConfig} options
     * @returns Promise<number>
     */
    getState(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.proposalId) {
                throw new Error("proposalId is not defined");
            }
            this.logContractFunctionCall("GenesisProtocol.state", options);
            const state = yield this.contract.state(options.proposalId);
            return state.toNumber();
        });
    }
    /**
     * Return all executed GenesisProtocol proposals created under the given avatar.
     * Filter by the optional proposalId.
     */
    getExecutedDaoProposals(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const defaults = {
                proposalId: null,
            };
            options = Object.assign({}, defaults, options);
            if (!options.avatar) {
                throw new Error("avatar address is not defined");
            }
            const proposals = new Array();
            const eventFetcher = this.ExecuteProposal({ _avatar: options.avatar, _proposalId: options.proposalId }, { fromBlock: 0 });
            yield new Promise((resolve) => {
                eventFetcher.get((err, log) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                    for (const event of log) {
                        proposals.push({
                            decision: event.args._decision.toNumber(),
                            executionState: event.args._executionState.toNumber(),
                            proposalId: event.args._proposalId,
                            totalReputation: event.args._totalReputation,
                        });
                    }
                    resolve();
                }));
            });
            return proposals;
        });
    }
    /**
     * Set the contract parameters.
     * @param {GenesisProtocolParams} params
     * @returns parameters hash
     */
    setParameters(params) {
        const _super = name => super[name];
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            params = Object.assign({}, yield exports.GetDefaultGenesisProtocolParameters(), params);
            // in Wei
            const web3 = yield utils_1.Utils.getWeb3();
            const maxEthValue = web3.toBigNumber(10).pow(26);
            const minimumStakingFee = web3.toBigNumber(params.minimumStakingFee);
            if (minimumStakingFee.lt(0)) {
                throw new Error("minimumStakingFee must be greater than or equal to 0");
            }
            if (minimumStakingFee.gt(maxEthValue)) {
                throw new Error(`minimumStakingFee must be less than ${maxEthValue}`);
            }
            const proposingRepRewardConstA = params.proposingRepRewardConstA || 0;
            if ((proposingRepRewardConstA < 0) || (proposingRepRewardConstA > 100000000)) {
                throw new Error("proposingRepRewardConstA must be greater than or equal to 0 and less than or equal to 100000000");
            }
            const proposingRepRewardConstB = params.proposingRepRewardConstB || 0;
            if ((proposingRepRewardConstB < 0) || (proposingRepRewardConstB > 100000000)) {
                throw new Error("proposingRepRewardConstB must be greater than or equal to 0 and less than or equal to 100000000");
            }
            const thresholdConstA = web3.toBigNumber(params.thresholdConstA);
            if (thresholdConstA.lt(0)) {
                throw new Error("thresholdConstA must be greater than or equal to 0");
            }
            if (thresholdConstA.gt(maxEthValue)) {
                throw new Error(`thresholdConstA must be less than ${maxEthValue}`);
            }
            const thresholdConstB = params.thresholdConstB || 0;
            if ((thresholdConstB <= 0) || (thresholdConstB > 100000000)) {
                throw new Error("thresholdConstB must be greater than 0 and less than or equal to 100000000");
            }
            const preBoostedVoteRequiredPercentage = params.preBoostedVoteRequiredPercentage || 0;
            if ((preBoostedVoteRequiredPercentage <= 0) || (preBoostedVoteRequiredPercentage > 100)) {
                throw new Error("preBoostedVoteRequiredPercentage must be greater than 0 and less than or equal to 100");
            }
            const stakerFeeRatioForVoters = params.stakerFeeRatioForVoters || 0;
            if ((stakerFeeRatioForVoters < 0) || (stakerFeeRatioForVoters > 100)) {
                throw new Error("stakerFeeRatioForVoters must be greater than or equal to 0 and less than or equal to 100");
            }
            const votersGainRepRatioFromLostRep = params.votersGainRepRatioFromLostRep || 0;
            if ((votersGainRepRatioFromLostRep < 0) || (votersGainRepRatioFromLostRep > 100)) {
                throw new Error("votersGainRepRatioFromLostRep must be greater than or equal to 0 and less than or equal to 100");
            }
            const votersReputationLossRatio = params.votersReputationLossRatio || 0;
            if ((votersReputationLossRatio < 0) || (votersReputationLossRatio > 100)) {
                throw new Error("votersReputationLossRatio must be greater than or equal to  0 and less than or equal to 100");
            }
            const daoBountyConst = params.daoBountyConst || 0;
            if ((daoBountyConst <= stakerFeeRatioForVoters) || (daoBountyConst >= stakerFeeRatioForVoters * 2)) {
                throw new Error("daoBountyConst must be greater than stakerFeeRatioForVoters and less than 2*stakerFeeRatioForVoters");
            }
            const daoBountyLimit = web3.toBigNumber(params.daoBountyLimit);
            if (daoBountyLimit.lt(0)) {
                throw new Error("daoBountyLimit must be greater than or equal to 0");
            }
            return _super("_setParameters").call(this, "GenesisProtocol.setParameters", [
                preBoostedVoteRequiredPercentage,
                params.preBoostedVotePeriodLimit,
                params.boostedVotePeriodLimit,
                thresholdConstA,
                thresholdConstB,
                minimumStakingFee,
                params.quietEndingPeriod,
                proposingRepRewardConstA,
                proposingRepRewardConstB,
                stakerFeeRatioForVoters,
                votersReputationLossRatio,
                votersGainRepRatioFromLostRep,
                daoBountyConst,
                daoBountyLimit,
            ]);
        });
    }
    getDefaultPermissions() {
        return commonTypes_1.DefaultSchemePermissions.GenesisProtocol;
    }
    getSchemePermissions(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this._getSchemePermissions(avatarAddress);
        });
    }
    getSchemeParameters(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this._getSchemeParameters(avatarAddress);
        });
    }
    getParameters(paramsHash) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const params = yield this.getParametersArray(paramsHash);
            return {
                boostedVotePeriodLimit: params[2].toNumber(),
                daoBountyConst: params[12].toNumber(),
                daoBountyLimit: params[13],
                minimumStakingFee: params[5].toNumber(),
                preBoostedVotePeriodLimit: params[1].toNumber(),
                preBoostedVoteRequiredPercentage: params[0].toNumber(),
                proposingRepRewardConstA: params[7].toNumber(),
                proposingRepRewardConstB: params[8].toNumber(),
                quietEndingPeriod: params[6].toNumber(),
                stakerFeeRatioForVoters: params[9].toNumber(),
                thresholdConstA: params[3],
                thresholdConstB: params[4].toNumber(),
                votersGainRepRatioFromLostRep: params[11].toNumber(),
                votersReputationLossRatio: params[10].toNumber(),
            };
        });
    }
    _validateVote(vote, proposalId) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const numChoices = yield this.getNumberOfChoices({ proposalId });
            if (!Number.isInteger(vote) || (vote < 0) || (vote > numChoices)) {
                throw new Error("vote is not valid");
            }
        });
    }
}
exports.GenesisProtocolWrapper = GenesisProtocolWrapper;
/**
 * defined just to add good type checking
 */
class GenesisProtocolFactoryType extends contractWrapperFactory_1.ContractWrapperFactory {
    /**
     * Migrate a new instance of GenesisProtocol.
     * @param stakingTokenAddress The token that will be used when staking.  Typically
     * is the token of the DAO that is going to use this GenesisProtocol.
     */
    new(stakingTokenAddress) {
        const _super = name => super[name];
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return _super("new").call(this, stakingTokenAddress);
        });
    }
}
exports.GenesisProtocolFactoryType = GenesisProtocolFactoryType;
exports.GenesisProtocolFactory = new GenesisProtocolFactoryType("GenesisProtocol", GenesisProtocolWrapper);
var ExecutionState;
(function (ExecutionState) {
    ExecutionState[ExecutionState["None"] = 0] = "None";
    ExecutionState[ExecutionState["PreBoostedTimeOut"] = 1] = "PreBoostedTimeOut";
    ExecutionState[ExecutionState["PreBoostedBarCrossed"] = 2] = "PreBoostedBarCrossed";
    ExecutionState[ExecutionState["BoostedTimeOut"] = 3] = "BoostedTimeOut";
    ExecutionState[ExecutionState["BoostedBarCrossed"] = 4] = "BoostedBarCrossed";
})(ExecutionState = exports.ExecutionState || (exports.ExecutionState = {}));
var ProposalState;
(function (ProposalState) {
    ProposalState[ProposalState["None"] = 0] = "None";
    ProposalState[ProposalState["Closed"] = 1] = "Closed";
    ProposalState[ProposalState["Executed"] = 2] = "Executed";
    ProposalState[ProposalState["PreBoosted"] = 3] = "PreBoosted";
    ProposalState[ProposalState["Boosted"] = 4] = "Boosted";
    ProposalState[ProposalState["QuietEndingPeriod"] = 5] = "QuietEndingPeriod";
})(ProposalState = exports.ProposalState || (exports.ProposalState = {}));
exports.GetDefaultGenesisProtocolParameters = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
    const web3 = yield utils_1.Utils.getWeb3();
    return {
        boostedVotePeriodLimit: 259200,
        daoBountyConst: 75,
        daoBountyLimit: web3.toWei(100),
        minimumStakingFee: web3.toWei(0),
        preBoostedVotePeriodLimit: 1814400,
        preBoostedVoteRequiredPercentage: 50,
        proposingRepRewardConstA: 5,
        proposingRepRewardConstB: 5,
        quietEndingPeriod: 86400,
        stakerFeeRatioForVoters: 50,
        thresholdConstA: web3.toWei(7),
        thresholdConstB: 3,
        votersGainRepRatioFromLostRep: 80,
        votersReputationLossRatio: 1,
    };
});
//# sourceMappingURL=genesisProtocol.js.map