From 302945f2c0d59893d0173ed943665ff63a25df56 Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Sun, 29 Mar 2020 09:09:48 -0500 Subject: [PATCH] bulk estimation gas for claim proceeds and claim fees (#6992) --- .../augur-ui/src/modules/common/constants.ts | 2 +- .../modal/containers/modal-claim-fees.ts | 39 ++++++++-- .../modal-claim-markets-proceeds.ts | 78 ++++++++++--------- .../augur-ui/src/modules/modal/proceeds.tsx | 23 +++++- .../actions/claim-markets-proceeds.ts | 20 ++--- .../reporting/actions/claim-reporting-fees.ts | 30 ++++++- 6 files changed, 133 insertions(+), 59 deletions(-) diff --git a/packages/augur-ui/src/modules/common/constants.ts b/packages/augur-ui/src/modules/common/constants.ts index 40f5e1a7c86..43012bfc867 100644 --- a/packages/augur-ui/src/modules/common/constants.ts +++ b/packages/augur-ui/src/modules/common/constants.ts @@ -711,7 +711,7 @@ export const NEW_MARKET_GAS_ESTIMATE = createBigNumber(2000000); export const MIGRATE_MARKET_GAS_ESTIMATE = createBigNumber(3000000); // TODO: Get actual gas estimate for migrating a market export const CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE = createBigNumber(1121349); // Gas cost for claiming proceeds from a categorical market with 8 outcomes (worst-case gas cost) export const CLAIM_MARKETS_PROCEEDS_GAS_LIMIT = createBigNumber(3000000); -export const CLAIM_FEES_GAS_COST = createBigNumber(250000); +export const CLAIM_FEES_GAS_COST = createBigNumber(500000); export const BUY_PARTICIPATION_TOKENS_GAS_LIMIT = createBigNumber(290000); export const MAX_BULK_CLAIM_MARKETS_PROCEEDS_COUNT = Math.floor( createBigNumber(CLAIM_MARKETS_PROCEEDS_GAS_LIMIT) diff --git a/packages/augur-ui/src/modules/modal/containers/modal-claim-fees.ts b/packages/augur-ui/src/modules/modal/containers/modal-claim-fees.ts index ddeedf43b0a..0cf07b709ce 100644 --- a/packages/augur-ui/src/modules/modal/containers/modal-claim-fees.ts +++ b/packages/augur-ui/src/modules/modal/containers/modal-claim-fees.ts @@ -6,6 +6,8 @@ import { formatGasCostToEther, formatAttoRep, formatAttoDai, + formatEther, + formatBlank, } from 'utils/format-number'; import { closeModal } from 'modules/modal/actions/close-modal'; import { Proceeds } from 'modules/modal/proceeds'; @@ -13,6 +15,7 @@ import { ActionRowsProps } from 'modules/modal/common'; import { redeemStake, redeemStakeBatches, + redeemStakeGas, } from 'modules/reporting/actions/claim-reporting-fees'; import { CLAIM_FEE_WINDOWS, @@ -37,11 +40,7 @@ import { addPendingData } from 'modules/pending-queue/actions/pending-queue-mana const mapStateToProps = (state: AppState) => { return { modal: state.modal, - gasCost: formatGasCostToEther( - CLAIM_FEES_GAS_COST, - { decimalsRounded: 4 }, - getGasPrice(state) - ), + gasCost: CLAIM_FEES_GAS_COST, GsnEnabled: state.appStatus.gsnEnabled, pendingQueue: state.pendingQueue || [], claimReportingFees: selectReportingWinningsByMarket(state), @@ -52,6 +51,7 @@ const mapStateToProps = (state: AppState) => { const mapDispatchToProps = (dispatch: ThunkDispatch) => ({ closeModal: () => dispatch(closeModal()), redeemStake: (options, callback) => redeemStake(options, callback), + redeemStakeGas: options => redeemStakeGas(options), disavowMarket: marketId => disavowMarket(marketId), addPendingData: (pendingId, queueName, status, hash, info) => dispatch(addPendingData(pendingId, queueName, status, hash, info)), @@ -77,6 +77,8 @@ const mergeProps = (sP: any, dP: any, oP: any) => { }; const submitAllTxCount = redeemStakeBatches(allRedeemStakeOptions); const claimableMarkets = claimReportingFees.claimableMarkets; + const showBreakdown = claimableMarkets.marketContracts.length > 1; + const totalRep = `${formatAttoRep(claimReportingFees.totalUnclaimedRep).formatted} REP`; if (!participationTokensOnly) { claimableMarkets.marketContracts.map(marketObj => { @@ -159,6 +161,8 @@ const mergeProps = (sP: any, dP: any, oP: any) => { }); } + let daiFormatted = formatBlank(); + if (claimReportingFees.participationContracts.unclaimedRep.gt(ZERO)) { const disputeWindowsPending = pendingQueue[CLAIM_STAKE_FEES] && @@ -167,7 +171,7 @@ const mergeProps = (sP: any, dP: any, oP: any) => { const repFormatted = formatAttoRep( claimReportingFees.participationContracts.unclaimedRep ); - const daiFormatted = formatAttoDai( + daiFormatted = formatAttoDai( claimReportingFees.participationContracts.unclaimedDai ); modalRows.push({ @@ -213,6 +217,17 @@ const mergeProps = (sP: any, dP: any, oP: any) => { return {}; } + const breakdown = showBreakdown ? [ + { + label: 'Total REP', + value: totalRep, + }, + { + label: 'Total DAI', + value: daiFormatted, + }, + ] : null; + return { title: isForking ? 'Release REP' : 'Claim Stake & Fees', submitAllTxCount: isForking ? 0 : submitAllTxCount, @@ -254,6 +269,18 @@ const mergeProps = (sP: any, dP: any, oP: any) => { } dP.closeModal(); }, + estimateGas: async () => { + if (breakdown) { + const gas = await dP.redeemStakeGas(allRedeemStakeOptions); + const displayfee = sP.GsnEnabled ? displayGasInDai(gas) : formatEther(gas).formattedValue; + return { + label: 'Transaction Fee', + value: String(displayfee), + }; + } + return null; + }, + breakdown, buttons: isForking || participationTokensOnly ? null diff --git a/packages/augur-ui/src/modules/modal/containers/modal-claim-markets-proceeds.ts b/packages/augur-ui/src/modules/modal/containers/modal-claim-markets-proceeds.ts index 5c1ff9f0043..ae26ed016d6 100644 --- a/packages/augur-ui/src/modules/modal/containers/modal-claim-markets-proceeds.ts +++ b/packages/augur-ui/src/modules/modal/containers/modal-claim-markets-proceeds.ts @@ -1,11 +1,9 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; -import { startClaimingMarketsProceeds } from 'modules/positions/actions/claim-markets-proceeds'; +import { startClaimingMarketsProceeds, claimMarketsProceedsGas } from 'modules/positions/actions/claim-markets-proceeds'; import { selectCurrentTimestampInSeconds } from 'appStore/select-state'; import { createBigNumber } from 'utils/create-big-number'; -import { getGasPrice } from 'modules/auth/selectors/get-gas-price'; import { - formatGasCostToEther, formatDai, formatEther, } from 'utils/format-number'; @@ -31,12 +29,6 @@ import { selectLoginAccountClaimablePositions } from 'modules/positions/selector import { displayGasInDai } from 'modules/app/actions/get-ethToDai-rate'; const mapStateToProps = (state: AppState) => { - const gasCost = formatGasCostToEther( - CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE, - { decimalsRounded: 4 }, - getGasPrice(state) - ); - const pendingQueue = state.pendingQueue || []; const accountMarketClaimablePositions: MarketClaimablePositions = selectLoginAccountClaimablePositions( state @@ -73,6 +65,12 @@ const mapStateToProps = (state: AppState) => { label: 'Profit', value: unclaimedProfit.full, }, + { + label: 'Transaction Fee', + value: state.appStatus.gsnEnabled + ? displayGasInDai(CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE) + : formatEther(CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE).formattedValue, + }, ], text: PROCEEDS_TO_CLAIM_TITLE, action: null, @@ -82,7 +80,7 @@ const mapStateToProps = (state: AppState) => { } return { modal: state.modal, - gasCost, + gasCost: CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE, currentTimestamp: selectCurrentTimestampInSeconds(state), claimableMarkets, totalUnclaimedProfit: @@ -90,6 +88,7 @@ const mapStateToProps = (state: AppState) => { totalUnclaimedProceeds: accountMarketClaimablePositions.totals.totalUnclaimedProceeds, GsnEnabled: state.appStatus.gsnEnabled, + account: state.loginAccount.address, }; }; @@ -97,43 +96,38 @@ const mapDispatchToProps = (dispatch: ThunkDispatch) => ({ closeModal: () => dispatch(closeModal()), startClaimingMarketsProceeds: ( marketIds: string[], + account: string, callback: NodeStyleCallback - ) => dispatch(startClaimingMarketsProceeds(marketIds, callback)), + ) => startClaimingMarketsProceeds(marketIds, account, callback), + estimateGas: ( + marketIds: string[], + address: string, + ) => claimMarketsProceedsGas(marketIds, address), }); const mergeProps = (sP: any, dP: any, oP: any) => { const markets = sP.claimableMarkets; const showBreakdown = markets.length > 1; - const totalGas = formatEther( - createBigNumber(sP.gasCost).times(markets.length) - ); const claimableMarkets = showBreakdown ? markets.map(m => ({ ...m, queueName: CLAIMMARKETSPROCEEDS, queueId: m.marketId, - action: () => dP.startClaimingMarketsProceeds([m.marketId], () => {}), + action: () => dP.startClaimingMarketsProceeds([m.marketId], sP.account, () => {}), })) : markets.map(m => ({ ...m, - action: () => dP.startClaimingMarketsProceeds([m.marketId], () => {}), + action: () => dP.startClaimingMarketsProceeds([m.marketId], sP.account, () => {}), queueName: CLAIMMARKETSPROCEEDS, queueId: m.marketId, properties: [ ...m.properties, - { - label: 'Transaction Fee', - value: sP.GsnEnabled - ? displayGasInDai(totalGas.value) - : totalGas.formattedValue, - }, ], })); const multiMarket = claimableMarkets.length > 1 ? 's' : ''; const totalUnclaimedProceedsFormatted = formatDai(sP.totalUnclaimedProceeds); const totalUnclaimedProfitFormatted = formatDai(sP.totalUnclaimedProfit); - const submitAllTxCount = Math.ceil( claimableMarkets.length / MAX_BULK_CLAIM_MARKETS_PROCEEDS_COUNT ); @@ -146,6 +140,17 @@ const mergeProps = (sP: any, dP: any, oP: any) => { return {}; } + const breakdown = showBreakdown ? [ + { + label: 'Total Proceeds', + value: totalUnclaimedProceedsFormatted.formatted, + }, + { + label: 'Total Profit', + value: totalUnclaimedProfitFormatted.formatted, + }, + ] : null; + return { title: PROCEEDS_TO_CLAIM_TITLE, descriptionMessage: [ @@ -156,20 +161,18 @@ const mergeProps = (sP: any, dP: any, oP: any) => { ], rows: claimableMarkets, submitAllTxCount, - breakdown: showBreakdown ? [ - { - label: 'Total Proceeds', - value: totalUnclaimedProceedsFormatted.formatted, - }, - { - label: 'Total Profit', - value: totalUnclaimedProfitFormatted.formatted, - }, - { - label: 'Transaction Fee', - value: sP.GsnEnabled ? displayGasInDai(totalGas.value) : totalGas.formattedValue, - }, - ] : null, + estimateGas: async () => { + if (breakdown) { + const gas = await dP.estimateGas(claimableMarkets.map(m => m.marketId), sP.account); + const displayfee = sP.GsnEnabled ? displayGasInDai(gas) : formatEther(gas).formattedValue; + return { + label: 'Transaction Fee', + value: String(displayfee), + }; + } + return null; + }, + breakdown, closeAction: () => { if (sP.modal.cb) { sP.modal.cb(); @@ -183,6 +186,7 @@ const mergeProps = (sP: any, dP: any, oP: any) => { action: () => { dP.startClaimingMarketsProceeds( claimableMarkets.map(m => m.marketId), + sP.account, sP.modal.cb ); dP.closeModal(); diff --git a/packages/augur-ui/src/modules/modal/proceeds.tsx b/packages/augur-ui/src/modules/modal/proceeds.tsx index 2c17df885a9..76e65c9276e 100644 --- a/packages/augur-ui/src/modules/modal/proceeds.tsx +++ b/packages/augur-ui/src/modules/modal/proceeds.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { DefaultButtonProps } from 'modules/common/buttons'; import { @@ -17,6 +17,7 @@ import { import Styles from 'modules/modal/modal.styles.less'; import { CLAIM_ALL_TITLE } from 'modules/common/constants'; +import { useEffect } from 'react'; interface ProceedsProps { closeAction: Function; @@ -26,6 +27,7 @@ interface ProceedsProps { submitAllTxCount: number; breakdown?: LinearPropertyLabelProps[]; descriptionMessage?: DescriptionMessageProps; + estimateGas: Function; } export const Proceeds = ({ @@ -36,7 +38,20 @@ export const Proceeds = ({ submitAllTxCount, breakdown, descriptionMessage, -}: ProceedsProps) => ( + estimateGas +}: ProceedsProps) => { + const [fullBreakdown, setBreakdown] = useState(breakdown); + useEffect(() => { + const timer = setTimeout(async () => { + const transactionFee = await estimateGas(); + if (transactionFee) { + setBreakdown([...fullBreakdown, transactionFee]); + } + }, 100); + return () => clearTimeout(timer); + },[]); + + return (
<main> @@ -47,7 +62,7 @@ export const Proceeds = ({ {/* // @ts-ignore */} {rows && <ActionRows rows={rows} />} - {breakdown && <Breakdown short rows={breakdown} />} + {breakdown && <Breakdown short rows={fullBreakdown} />} </main> <BulkTxLabel buttonName={CLAIM_ALL_TITLE} @@ -56,4 +71,4 @@ export const Proceeds = ({ /> {buttons && <ButtonsRow buttons={buttons} />} </div> -); +)}; diff --git a/packages/augur-ui/src/modules/positions/actions/claim-markets-proceeds.ts b/packages/augur-ui/src/modules/positions/actions/claim-markets-proceeds.ts index 2eb8ea710e5..dfe41bb633a 100644 --- a/packages/augur-ui/src/modules/positions/actions/claim-markets-proceeds.ts +++ b/packages/augur-ui/src/modules/positions/actions/claim-markets-proceeds.ts @@ -8,17 +8,15 @@ import { claimMarketsProceeds, claimMarketsProceedsEstimateGas, } from 'modules/contracts/actions/contractCalls'; -import { AppState } from 'appStore'; import { NodeStyleCallback } from 'modules/types'; -import { ThunkDispatch } from 'redux-thunk'; -import { Action } from 'redux'; +import { BigNumber } from 'utils/create-big-number'; export const startClaimingMarketsProceeds = ( marketIds: string[], + account: string, callback: NodeStyleCallback = logError -) => (dispatch: ThunkDispatch<void, any, Action>, getState: () => AppState) => { - const { loginAccount } = getState(); - if (!loginAccount.address || !marketIds || !marketIds.length) +) => { + if (!account || !marketIds || !marketIds.length) return callback(null); let i = 0; @@ -29,7 +27,7 @@ export const startClaimingMarketsProceeds = ( try { // TODO: Pass affiliate address to claimMarketsProceeds - groups.map(group => claimMarketsProceeds(group, loginAccount.address)); + groups.map(group => claimMarketsProceeds(group, account)); } catch (e) { console.error(e); } @@ -37,12 +35,14 @@ export const startClaimingMarketsProceeds = ( export default claimMarketsProceeds; -export const claimMarketsProceedsGas = ( +export const claimMarketsProceedsGas = async ( marketIds: string[], loginAccount: string -) => { +): Promise<BigNumber> => { try { - return claimMarketsProceedsEstimateGas(marketIds, loginAccount); + const value = await claimMarketsProceedsEstimateGas(marketIds, loginAccount); + console.log('gas estimate', value.toString()); + return value; } catch (error) { console.error('error could estimate gas', error); return CLAIM_MARKETS_PROCEEDS_GAS_ESTIMATE; diff --git a/packages/augur-ui/src/modules/reporting/actions/claim-reporting-fees.ts b/packages/augur-ui/src/modules/reporting/actions/claim-reporting-fees.ts index 3a08a17520a..b58b7c9f645 100644 --- a/packages/augur-ui/src/modules/reporting/actions/claim-reporting-fees.ts +++ b/packages/augur-ui/src/modules/reporting/actions/claim-reporting-fees.ts @@ -5,7 +5,8 @@ import { redeemUserStakesEstimateGas, forkAndRedeem, } from 'modules/contracts/actions/contractCalls'; - +import { BigNumber } from 'utils/create-big-number'; +import { CLAIM_MARKETS_PROCEEDS_GAS_LIMIT } from 'modules/common/constants'; export const CROWDSOURCER_BATCH_SIZE = 4; export const CROWDSOURCER_DISAVOWED_BATCH_SIZE = 1; export const DISPUTE_WINDOW_BATCH_SIZE = 10; @@ -113,3 +114,30 @@ async function runPayload( ? forkAndRedeem(reportingParticipants[0]) // should be batches of one : redeemUserStakes(reportingParticipants, disputeWindows); } + + +export const redeemStakeGas = async ( + options: ClaimReportingOptions, +): Promise<BigNumber> => { + try { + const { + reportingParticipants, + disputeWindows, + disavowed, + isForkingMarket, + } = options; + + return await Promise.all(batchContractIds(disputeWindows, reportingParticipants, disavowed, isForkingMarket) + .map(batch => + redeemUserStakesEstimateGas( + batch.disputeWindows, + batch.reportingParticipants, + ))).then((gas: BigNumber[]) => { + console.log('gas estimate', JSON.stringify(gas)); + return gas.reduce((p, g) => p.plus(g)); + }) + } catch (error) { + console.error('error could estimate gas', error); + return CLAIM_MARKETS_PROCEEDS_GAS_LIMIT; + } +};