"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const commonTypes_1 = require("../commonTypes");
const configService_1 = require("../configService");
const contractWrapperFactory_1 = require("../contractWrapperFactory");
const iContractWrapperBase_1 = require("../iContractWrapperBase");
const proposalGeneratorBase_1 = require("../proposalGeneratorBase");
const transactionService_1 = require("../transactionService");
const utils_1 = require("../utils");
const utilsInternal_1 = require("../utilsInternal");
const web3EventService_1 = require("../web3EventService");
const standardToken_1 = require("./standardToken");
class VestingSchemeWrapper extends proposalGeneratorBase_1.ProposalGeneratorBase {
    constructor() {
        super(...arguments);
        this.name = "VestingScheme";
        this.friendlyName = "Vesting Scheme";
        this.factory = exports.VestingSchemeFactory;
        /**
         * see CreateVestingAgreementConfig
         */
        this.defaultCreateOptions = {
            startingBlock: null,
        };
    }
    /**
     * Submit a proposal to create a vesting agreement.
     * @param {ProposeVestingAgreementConfig} options
     */
    proposeVestingAgreement(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * see ProposeVestingAgreementConfig
             */
            options = Object.assign({}, this.defaultCreateOptions, options);
            if (!options.avatar) {
                throw new Error("avatar is not defined");
            }
            yield this.validateCreateParams(options);
            this.logContractFunctionCall("VestingScheme.proposeVestingAgreement", options);
            const web3 = yield utils_1.Utils.getWeb3();
            const txResult = yield this.wrapTransactionInvocation("VestingScheme.propose", options, this.contract.proposeVestingAgreement, [options.beneficiaryAddress,
                options.returnOnCancelAddress,
                options.startingBlock,
                web3.toBigNumber(options.amountPerPeriod),
                options.periodLength,
                options.numOfAgreedPeriods,
                options.cliffInPeriods,
                options.signaturesReqToCancel,
                options.signers,
                options.avatar]);
            return new iContractWrapperBase_1.ArcTransactionProposalResult(txResult.tx, this.contract, yield this.getVotingMachine(options.avatar));
        });
    }
    /**
     * Create a new vesting agreement
     * @param {CreateVestingAgreementConfig} options
     */
    create(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            /**
             * See these properties in CreateVestingAgreementConfig
             */
            options = Object.assign({}, this.defaultCreateOptions, options);
            yield this.validateCreateParams(options);
            if (!options.token) {
                throw new Error("token is not defined");
            }
            const web3 = yield utils_1.Utils.getWeb3();
            const amountPerPeriod = web3.toBigNumber(options.amountPerPeriod);
            const autoApproveTransfer = configService_1.ConfigService.get("autoApproveTokenTransfers");
            const functionName = "VestingScheme.create";
            const payload = transactionService_1.TransactionService.publishKickoffEvent(functionName, options, 1 + (autoApproveTransfer ? 1 : 0));
            const eventContext = transactionService_1.TransactionService.newTxEventContext(functionName, payload, options);
            /**
             * approve immediate transfer of the given tokens from currentAccount to the VestingScheme
             */
            if (autoApproveTransfer) {
                const token = yield standardToken_1.StandardTokenFactory.at(options.token);
                const result = yield token.approve({
                    amount: amountPerPeriod.mul(options.numOfAgreedPeriods),
                    spender: this.address,
                    txEventContext: eventContext,
                });
                yield result.watchForTxMined();
            }
            this.logContractFunctionCall("VestingScheme.createVestedAgreement", options);
            const tx = yield this.sendTransaction(eventContext, this.contract.createVestedAgreement, [options.token,
                options.beneficiaryAddress,
                options.returnOnCancelAddress,
                options.startingBlock,
                amountPerPeriod,
                options.periodLength,
                options.numOfAgreedPeriods,
                options.cliffInPeriods,
                options.signaturesReqToCancel,
                options.signers]);
            if (tx) {
                transactionService_1.TransactionService.publishTxLifecycleEvents(eventContext, tx, this.contract);
            }
            return new ArcTransactionAgreementResult(tx, this.contract);
        });
    }
    /**
     * Sign to cancel a vesting agreement
     * @param {SignToCancelVestingAgreementConfig} options
     */
    signToCancel(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.signToCancelAgreement", options);
            return this.wrapTransactionInvocation("VestingScheme.signToCancel", options, this.contract.signToCancelAgreement, [options.agreementId]);
        });
    }
    /**
     * Revoke vote for cancelling a vesting agreement
     * @param {RevokeSignToCancelVestingAgreementConfig} options
     */
    revokeSignToCancel(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.revokeSignToCancelAgreement", options);
            return this.wrapTransactionInvocation("VestingScheme.revokeSignToCancel", options, this.contract.revokeSignToCancelAgreement, [options.agreementId]);
        });
    }
    /**
     * Collects for a beneficiary, according to the agreement
     * @param {CollectVestingAgreementConfig} options
     */
    collect(options = {}) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (options.agreementId === null) {
                throw new Error("agreementId is not defined");
            }
            this.logContractFunctionCall("VestingScheme.collect", options);
            return this.wrapTransactionInvocation("VestingScheme.collect", options, this.contract.collect, [options.agreementId]);
        });
    }
    /**
     * EntityFetcherFactory for votable Agreement.
     * @param avatarAddress
     */
    getVotableProposals(avatarAddress) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this.proposalService.getProposalEvents({
                baseArgFilter: { _avatar: avatarAddress },
                proposalsEventFetcher: this.AgreementProposal,
                transformEventCallback: (event) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                    return this.getVotableProposal(event.args._avatar, event.args._proposalId);
                }),
                votableOnly: true,
                votingMachine: yield this.getVotingMachine(avatarAddress),
            });
        });
    }
    /**
     * EntityFetcherFactory for executed proposals.
     * @param avatarAddress
     */
    getExecutedProposals(avatarAddress) {
        return this.proposalService.getProposalEvents({
            baseArgFilter: { _avatar: avatarAddress },
            proposalsEventFetcher: this.ProposalExecuted,
            transformEventCallback: (event) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                return Promise.resolve({
                    agreementId: yield this.getProposalAgreementId(event.args._proposalId),
                    avatarAddress: event.args._avatar,
                    proposalId: event.args._proposalId,
                    winningVote: event.args._param,
                });
            }),
        });
    }
    /**
     * Returns a promise of the agreementId associated with the given proposal. The result
     * is 0 if the proposal has not been executed or is not found.
     * @param proposalId
     */
    getProposalAgreementId(proposalId) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const fetcher = this.ProposedVestedAgreement({ _proposalId: proposalId }, { fromBlock: 0 });
            const events = yield fetcher.get();
            return events.length ? events[0].args._agreementId.toNumber() : 0;
        });
    }
    getVotableProposal(avatarAddress, proposalId) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const proposalParams = yield this.contract.organizationsProposals(avatarAddress, proposalId);
            const agreement = this.convertProposalPropsArrayToObject(proposalParams);
            agreement.proposalId = proposalId;
            return agreement;
        });
    }
    getAgreement(agreementId) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const agreementParams = yield this.contract.agreements(agreementId);
            const agreement = this.convertProposalPropsArrayToObject(agreementParams);
            agreement.agreementId = agreementId;
            return agreement;
        });
    }
    getParametersHash(params) {
        return this._getParametersHash(params.voteParametersHash, params.votingMachineAddress);
    }
    setParameters(params) {
        this.validateStandardSchemeParams(params);
        return super._setParameters("VestingScheme.setParameters", params.txEventContext, params.voteParametersHash, params.votingMachineAddress);
    }
    getDefaultPermissions() {
        return commonTypes_1.DefaultSchemePermissions.VestingScheme;
    }
    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 {
                voteParametersHash: params[0],
                votingMachineAddress: params[1],
            };
        });
    }
    hydrated() {
        /* tslint:disable:max-line-length */
        this.ProposalExecuted = this.createEventFetcherFactory(this.contract.ProposalExecuted);
        this.AgreementProposal = this.createEventFetcherFactory(this.contract.AgreementProposal);
        this.NewVestedAgreement = this.createEventFetcherFactory(this.contract.NewVestedAgreement);
        this.ProposedVestedAgreement = this.createEventFetcherFactory(this.contract.ProposedVestedAgreement);
        this.SignToCancelAgreement = this.createEventFetcherFactory(this.contract.SignToCancelAgreement);
        this.RevokeSignToCancelAgreement = this.createEventFetcherFactory(this.contract.RevokeSignToCancelAgreement);
        this.AgreementCancel = this.createEventFetcherFactory(this.contract.AgreementCancel);
        this.Collect = this.createEventFetcherFactory(this.contract.Collect);
        /* tslint:enable:max-line-length */
    }
    validateCreateParams(options) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!options.beneficiaryAddress) {
                throw new Error("beneficiary address is not set");
            }
            if (!options.returnOnCancelAddress) {
                throw new Error("returnOnCancelAddress is not set");
            }
            if (!Number.isInteger(options.signaturesReqToCancel) || (options.signaturesReqToCancel <= 0)) {
                throw new Error("signaturesReqToCancel must be greater than zero");
            }
            if (!Array.isArray(options.signers)) {
                throw new Error("signers is not set");
            }
            if (options.signers.length < 1) {
                throw new Error("the number of signers must be greater than 0");
            }
            if (options.signaturesReqToCancel > options.signers.length) {
                throw new Error("the number of signatures required to cancel cannpt be greater than the number of signers");
            }
            if (!Number.isInteger(options.periodLength) || (options.periodLength <= 0)) {
                throw new Error("periodLength must be greater than zero");
            }
            const web3 = yield utils_1.Utils.getWeb3();
            if (yield web3.toBigNumber(options.amountPerPeriod).lte(0)) {
                throw new Error("amountPerPeriod must be greater than zero");
            }
            if (!Number.isInteger(options.numOfAgreedPeriods) || (options.numOfAgreedPeriods <= 0)) {
                throw new Error("numOfAgreedPeriods must be greater than zero");
            }
            if (!Number.isInteger(options.cliffInPeriods) || (options.cliffInPeriods < 0)) {
                throw new Error("cliffInPeriods must be greater than or equal to zero");
            }
            if ((typeof options.startingBlock === "undefined") || (options.startingBlock === null)) {
                options.startingBlock = yield utilsInternal_1.UtilsInternal.lastBlock();
            }
            if (!Number.isInteger(options.startingBlock) || (options.startingBlock < 0)) {
                throw new Error("startingBlock must be greater than or equal to zero");
            }
        });
    }
    convertProposalPropsArrayToObject(propsArray) {
        return {
            amountPerPeriod: propsArray[4],
            beneficiaryAddress: propsArray[1],
            cliffInPeriods: propsArray[7],
            collectedPeriods: propsArray[9],
            numOfAgreedPeriods: propsArray[6],
            periodLength: propsArray[5],
            returnOnCancelAddress: propsArray[2],
            signaturesReqToCancel: propsArray[8],
            startingBlock: propsArray[3],
            tokenAddress: propsArray[0],
        };
    }
}
exports.VestingSchemeWrapper = VestingSchemeWrapper;
class ArcTransactionAgreementResult extends iContractWrapperBase_1.ArcTransactionResult {
    constructor(tx, contract) {
        super(tx, contract);
    }
    /**
     * Returns promise of the agreement id from the logs of the mined transaction. Will watch for the mined tx,
     * so could take a while to return.
     */
    getAgreementIdFromMinedTx() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return this.getValueFromMinedTx("_agreementId");
        });
    }
}
exports.ArcTransactionAgreementResult = ArcTransactionAgreementResult;
exports.VestingSchemeFactory = new contractWrapperFactory_1.ContractWrapperFactory("VestingScheme", VestingSchemeWrapper, new web3EventService_1.Web3EventService());
//# sourceMappingURL=vestingScheme.js.map