import * as React from "react"; import { compose } from "redux"; import { BigNumber, formatEther, parseEther, TxHash } from "@joincivil/typescript-types"; import { TwoStepEthTransaction } from "@joincivil/core"; import { ChallengeProposalCommitVote as ChallengeProposalCommitVoteComponent, TChallengeProposalCommitVoteProps as ChallengeProposalCommitVoteComponentProps, ChallengeProposalReviewVote, ChallengeProposalReviewVoteProps, ModalContent, ModalUnorderedList, ModalListItem, } from "@joincivil/components"; import { CivilHelper, CivilHelperContext } from "../../apis/CivilHelper"; import { InjectedTransactionStatusModalProps, hasTransactionStatusModals } from "../utility/TransactionStatusModalsHOC"; import { fetchSalt } from "../../helpers/salt"; import { saveVote } from "../../helpers/vote"; import { ChallengeDetailProps, ChallengeVoteState } from "./ChallengeProposalDetail"; enum TransactionTypes { APPROVE_VOTING_RIGHTS = "APPROVE_VOTING_RIGHTS", COMMIT_VOTE = "COMMIT_VOTE", } const transactionLabels = { [TransactionTypes.APPROVE_VOTING_RIGHTS]: "Approve For Proposal Challenge", [TransactionTypes.COMMIT_VOTE]: "Challenge Proposal", }; const multiStepTransactionLabels = { [TransactionTypes.APPROVE_VOTING_RIGHTS]: "1 of 2", [TransactionTypes.COMMIT_VOTE]: "2 of 2", }; const transactionSuccessContent = { [TransactionTypes.APPROVE_VOTING_RIGHTS]: [undefined, undefined], [TransactionTypes.COMMIT_VOTE]: [ "Your proposal challenge was submitted!", <> , ], }; const transactionRejectionContent = { [TransactionTypes.APPROVE_VOTING_RIGHTS]: [ "There was a problem with your proposal challenge", "Before proposing a new value, you need to confirm the approval of your proposal challenge token deposit in your MetaMask wallet.", ], [TransactionTypes.COMMIT_VOTE]: [ "Your proposal challenge was not submitted", "To submit a proposal challenge, you need to confirm the transaction in your MetaMask wallet.", ], }; const transactionErrorContent = { [TransactionTypes.APPROVE_VOTING_RIGHTS]: [ "There was a problem with your proposal challenge", <> Please check the following and retry your transaction You have sufficient CVL in your account for the Parameter Proposal Challenge Deposit , ], [TransactionTypes.COMMIT_VOTE]: [ "The was an problem with submitting your proposal challenge", <> Please retry your transaction , ], }; const transactionStatusModalConfig = { transactionLabels, multiStepTransactionLabels, transactionSuccessContent, transactionRejectionContent, transactionErrorContent, }; class ChallengeProposalCommitVote extends React.Component< ChallengeDetailProps & ChallengeProposalCommitVoteComponentProps & InjectedTransactionStatusModalProps, ChallengeVoteState > { public static contextType = CivilHelperContext; public context: CivilHelper; public constructor( props: ChallengeDetailProps & ChallengeProposalCommitVoteComponentProps & InjectedTransactionStatusModalProps, ) { super(props); this.state = { isReviewVoteModalOpen: false, voteOption: undefined, numTokens: undefined, }; } public componentWillMount(): void { this.props.setTransactions(this.getTransactions()); this.props.setHandleTransactionSuccessButtonClick(this.props.handleClose); } public render(): JSX.Element { const salt = fetchSalt(this.props.challengeID, this.props.user); const challengeProposalCommitVoteProps = { ...this.props, numTokens: this.state.numTokens, voteOption: this.state.voteOption, salt, onInputChange: this.updateCommitVoteState, onCommitMaxTokens: () => this.commitMaxTokens(), challengeID: this.props.challengeID.toString(), onReviewVote: this.handleReviewVote, transactions: this.getTransactions(), postExecuteTransactions: this.onChallengeProposalCommitVoteSuccess, }; return ( <> {this.renderReviewVoteModal()} ); } private renderReviewVoteModal(): JSX.Element { const { challenge } = this.props; const proposalURL = `https://${window.location.hostname}/parameterizer/${this.props.propID}`; const salt = fetchSalt(this.props.challengeID, this.props.user); const props: ChallengeProposalReviewVoteProps = { parameterName: this.props.parameterDisplayName, proposalURL, challengeID: this.props.challengeID.toString(), open: this.state.isReviewVoteModalOpen, salt, numTokens: this.state.numTokens, voteOption: this.state.voteOption, userAccount: this.props.user, commitEndDate: challenge.poll.commitEndDate.toNumber(), revealEndDate: challenge.poll.revealEndDate.toNumber(), transactions: this.getTransactions(), postExecuteTransactions: this.closeReviewModalAndChallengeDrawer, handleClose: this.closeReviewVoteModal, }; return ; } private updateCommitVoteState = (data: any, callback?: () => void): void => { if (callback) { this.setState({ ...data }, callback); } else { this.setState({ ...data }); } }; private commitMaxTokens(): void { let numTokens: BigNumber; if (!this.props.votingBalance!.isZero()) { numTokens = this.props.votingBalance!; } else { numTokens = this.props.balance!.add(this.props.votingBalance!); } const numTokensString = formatEther(numTokens); this.setState(() => ({ numTokens: numTokensString })); } private getTransactions = (): any[] => { return [ { transaction: async () => { this.props.updateTransactionStatusModalsState({ isWaitingTransactionModalOpen: true, isTransactionProgressModalOpen: false, isTransactionSuccessModalOpen: false, isTransactionErrorModalOpen: false, isTransactionRejectionModalOpen: false, transactionType: TransactionTypes.APPROVE_VOTING_RIGHTS, }); return this.approveVotingRights(); }, handleTransactionHash: (txHash: TxHash) => { this.props.updateTransactionStatusModalsState({ isWaitingTransactionModalOpen: false, isTransactionProgressModalOpen: true, }); }, handleTransactionError: this.props.handleTransactionError, }, { transaction: async () => { this.props.updateTransactionStatusModalsState({ isWaitingTransactionModalOpen: true, isTransactionProgressModalOpen: false, isTransactionSuccessModalOpen: false, transactionType: TransactionTypes.COMMIT_VOTE, }); return this.commitVoteOnChallenge(); }, handleTransactionHash: (txHash: TxHash) => { this.props.updateTransactionStatusModalsState({ isWaitingTransactionModalOpen: true, isTransactionProgressModalOpen: false, }); }, handleTransactionError: this.props.handleTransactionError, }, ]; }; private onChallengeProposalCommitVoteSuccess = (): void => { this.props.updateTransactionStatusModalsState({ isWaitingTransactionModalOpen: false, isTransactionProgressModalOpen: false, isTransactionSuccessModalOpen: true, }); this.props.handleClose(); }; private approveVotingRights = async (): Promise | void> => { const numTokens = parseEther(this.state.numTokens!.toString()); return this.context.approveVotingRightsForCommit(numTokens); }; private commitVoteOnChallenge = async (): Promise> => { const voteOption: BigNumber = new BigNumber(this.state.voteOption as string); const saltStr = fetchSalt(this.props.challengeID, this.props.user); const salt: BigNumber = new BigNumber(saltStr as string); const numTokens = parseEther(this.state.numTokens!.toString()); saveVote(this.props.challengeID, this.props.user, voteOption); return this.context.commitVote(this.props.challengeID, voteOption, salt, numTokens); }; private handleReviewVote = () => { this.setState({ isReviewVoteModalOpen: true }); }; private closeReviewVoteModal = () => { this.setState({ isReviewVoteModalOpen: false }); }; private closeReviewModalAndChallengeDrawer = () => { this.setState({ isReviewVoteModalOpen: false }, () => { this.props.handleClose(); }); }; } export default compose>( hasTransactionStatusModals(transactionStatusModalConfig), )(ChallengeProposalCommitVote);