Source

models/Voting/VotingContract.js

import { voting } from '../../interfaces';
import ERC20Contract from '../ERC20/ERC20Contract';
import IContract from '../IContract';
import Numbers from '../../utils/Numbers';

/**
 * @typedef {Object} VotingContract~Options
 * @property {Boolean} test
 * @property {Boolean} localtest ganache local blockchain
 * @property {Web3Connection} [web3Connection=Web3Connection] created from params: 'test', 'localtest' and optional 'web3Connection' string and 'privateKey'
 * @property {string} [contractAddress]
 */

/**
 * Voting Contract Object
 * @class VotingContract
 * @param {VotingContract~Options} options
 */
class VotingContract extends IContract {
  constructor(params = {}) {
    super({ ...params, abi: voting });
  }

  /**
   * Get ERC20 Address of the Contract
   * @returns {Promise<Address>}
   */
  erc20() {
    return this.__sendTx(
      this.getContract().methods.erc20(),
      { call: true },
    );
  }

  /**
   * Creates a Pool
   * @param {String} params.description
   * @param {Integer} params.expirationTime
   * @param {Array | Integer} params.poolOptions
   * @return {Promise<TransactionObject>}
   */
  createPool({
    description,
    expirationTime,
    poolOptions,
  }, options) {
    return this.__sendTx(
      this.getContract()
        .methods.createPoll(
          description,
          Numbers.timeToSmartContractTime(expirationTime),
          poolOptions,
        ),
      options,
    );
  }

  /**
   * Creates a Pool
   * @param {Integer} params.poolId
   * @return {Promise<TransactionObject>}
   */
  endPool({
    poolId,
  }, options) {
    return this.__sendTx(
      this.getContract()
        .methods.endPool(
          poolId,
        ),
      options,
    );
  }

  /**
     * Cast Vote
     * @param {Integer} params.poolId Pool Id
     * @param {Integer} params.voteId Vote Position on Length
     * @return {Promise<TransactionObject>}
     */
  castVote({
    poolId,
    voteId,
  }, options) {
    return this.__sendTx(
      this.getContract()
        .methods.castVote(
          poolId,
          voteId,
        ),
      options,
    );
  }

  /**
     * Stake Voting Tokens
     * @param {Integer} params.tokens Tokens
     * @return {Promise<TransactionObject>}
     */
  stakeVotingTokens({
    tokens,
  }, options) {
    return this.__sendTx(
      this.getContract()
        .methods.stakeVotingTokens(
          Numbers.toSmartContractDecimals(
            tokens,
            this.getERC20Contract().getDecimals(),
          ),
        ),
      options,
    );
  }

  /**
   * @typedef {Object} VotingContract~Pool
   * @property {Address} creator
   * @property {Bool} status
   * @property {Integer} optionsSize
   * @property {String} description
   * @property {Array | Address} voters
   * @property {Date} expirationTime
   */

  /**
     * Get Pool Information
     * @function
     * @param {Object} params
     * @param {Integer} params.poolId
     * @return {Promise<VotingContract~Pool>}
     */
  getPoolInformation = async ({ poolId }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getPoolInformation(poolId),
      { call: true },
    );

    return {
      _id: poolId,
      creator: res[0],
      status: res[1],
      optionsSize: parseInt(res[2], 10),
      descripton: res[3],
      voters: res[4],
      expirationTime: Numbers.fromSmartContractTimeToMinutes(res[5]),
    };
  };

  /**
     * Get Pool Winner
     * @function
     * @param {Object} params
     * @param {Integer} params.poolId
     * @return {Integer} Winner Id
     * @return {Integer} Winner Id Index
     */
  getPoolWinner = async ({ poolId }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getPoolWinner(poolId),
      { call: true },
    );

    return {
      winnerId: parseInt(res[0], 10),
      optionId: parseInt(res[1], 10),
    };
  };

  /**
     * Get Pool Winner
     * @function
     * @param {Object} params
     * @param {Integer} params.poolId Pool Id
     * @param {Integer} params.optionIndex Option Id for Pool
     * @return {Integer} Option Id
     */
  getPollOptionById = async ({ poolId, optionIndex }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getPollOptionById(poolId, optionIndex),
      { call: true },
    );

    return parseInt(res[0], 10);
  };

  /**
     * Get Pool History for Address
     * @function
     * @param {Object} params
     * @param {Address} params.address
     * @return {Array | Integer} Pool Ids
     */
  getPollHistory = async ({ address }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getPollHistory(address),
      { call: true },
    );

    return {
      pools: res[0].map(r => parseInt(r, 10)),
    };
  };

  /**
     * Get Pool Info for Voter
     * @function
     * @param {Object} params
     * @param {Integer} params.poolId
     * @param {Address} params.voter
     * @return {Integer} Vote
     * @return {Integer} Weigth (Token Value)
     */
  getPollInfoForVoter = async ({ poolId, voter }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getPollInfoForVoter(poolId, voter),
      { call: true },
    );

    return {
      vote: parseInt(res[0], 10),
      weigth: Numbers.toSmartContractDecimals(
        res[1],
        this.getERC20Contract().getDecimals(),
      ),
    };
  };

  /**
     * Check if a User has voted
     * @function
     * @param {Object} params
     * @param {Integer} params.poolId
     * @param {Address} params.voter
     * @return {Bool} HasVoted
     */
  userHasVoted = async ({ poolId, voter }) => {
    const res = await this.__sendTx(
      this.getContract().methods.userHasVoted(poolId, voter),
      { call: true },
    );

    return res[0];
  };

  /**
     * Get Locked Amount
     * @function
     * @param {Object} params
     * @param {Address} params.voter
     * @return {Integer} Locked Amount
     */
  getLockedAmount = async ({ voter }) => {
    const res = await this.__sendTx(
      this.getContract().methods.getLockedAmount(voter),
      { call: true },
    );

    return Numbers.toSmartContractDecimals(
      res[0],
      this.getERC20Contract().getDecimals(),
    );
  };

  /**
     * Withdraw Tokens from Voting
     * @param {Integer} params.tokens Tokens
     * @return {Promise<TransactionObject>}
     */
  withdrawTokens({
    tokens,
  }, options) {
    return this.__sendTx(
      this.getContract()
        .methods.withdrawTokens(
          Numbers.toSmartContractDecimals(
            tokens,
            this.getERC20Contract().getDecimals(),
          ),
        ),
      options,
    );
  }

  /**
   * @async
   * @function
   * @throws {Error} Contract is not deployed, first deploy it and provide a contract address
   * @void
   */
  __assert = async () => {
    if (!this.getAddress()) {
      throw new Error(
        'Contract is not deployed, first deploy it and provide a contract address',
      );
    }

    /* Use ABI */
    this.params.contract.use(voting, this.getAddress());

    /* Set Token Address Contract for easy access */
    if (!this.params.ERC20Contract) {
      this.params.ERC20Contract = new ERC20Contract({
        web3Connection: this.web3Connection,
        contractAddress: await this.erc20(),
      });
    }

    /* Assert Token Contract */
    await this.params.ERC20Contract.start();
    await this.params.ERC20Contract.__assert();
  };

  /**
   * Deploy the Staking Contract
   * @function
   * @param [Object] params
   * @param {Address} params.erc20Contract
   * @param {IContract~TxOptions} options
   * @return {Promise<*>}
   */
  deploy = async ({ erc20Contract }, options) => {
    const params = [ erc20Contract ];
    const res = await this.__deploy(params, options);
    this.params.contractAddress = res.contractAddress;
    /* Call to Backend API */
    await this.__assert();
    return res;
  };

  /**
   * @function
   * @return ERC20Contract|undefined
   */
  getERC20Contract = () => this.params.ERC20Contract;
}

export default VotingContract;