diff --git a/frontend/client/components/CreateProposal/index.tsx b/frontend/client/components/CreateProposal/index.tsx index 4cee2f33..7f3d49c0 100644 --- a/frontend/client/components/CreateProposal/index.tsx +++ b/frontend/client/components/CreateProposal/index.tsx @@ -12,11 +12,11 @@ import { RadioChangeEvent } from 'antd/lib/radio'; import TrusteeFields from './TrusteeFields'; import MilestoneFields, { Milestone } from './MilestoneFields'; import CreateSuccess from './CreateSuccess'; -import { computePercentage } from 'utils/helpers'; import { getAmountError } from 'utils/validators'; import MarkdownEditor from 'components/MarkdownEditor'; import * as Styled from './styled'; - +import { Wei, toWei } from 'utils/units'; +import BN from 'bn.js'; interface StateProps { crowdFundLoading: AppState['web3']['crowdFundLoading']; crowdFundError: AppState['web3']['crowdFundError']; @@ -74,8 +74,8 @@ const DEFAULT_STATE: State = { milestoneDeadline: 60 * 60 * 24 * 7, }; -function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: number) { - return computePercentage(raiseGoal, milestone.payoutPercent); +function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: Wei) { + return raiseGoal.divn(100).mul(new BN(milestone.payoutPercent)); } class CreateProposal extends React.Component { @@ -159,7 +159,7 @@ class CreateProposal extends React.Component { }; createCrowdFund = async () => { - const { contract, createCrowdFund, web3 } = this.props; + const { contract, createCrowdFund } = this.props; const { title, proposalBody, @@ -173,10 +173,9 @@ class CreateProposal extends React.Component { } = this.state; const backendData = { content: proposalBody, title, category }; - - const targetInWei = web3.utils.toWei(String(amountToRaise), 'ether'); + const targetInWei = toWei(amountToRaise, 'ether'); const milestoneAmounts = milestones.map(milestone => - milestoneToMilestoneAmount(milestone, targetInWei), + Wei(milestoneToMilestoneAmount(milestone, targetInWei)), ); const immediateFirstMilestonePayout = milestones[0].immediatePayout; diff --git a/frontend/client/components/Proposal/Contributors/index.tsx b/frontend/client/components/Proposal/Contributors/index.tsx new file mode 100644 index 00000000..5cdc9351 --- /dev/null +++ b/frontend/client/components/Proposal/Contributors/index.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Spin } from 'antd'; +import { CrowdFund } from 'modules/proposals/reducers'; +import UserRow from 'components/UserRow'; +import * as ProposalStyled from '../styled'; +import Placeholder from 'components/Placeholder'; +import UnitDisplay from 'components/UnitDisplay'; + +interface Props { + crowdFund: CrowdFund; +} + +const ContributorsBlock = ({ crowdFund }: Props) => { + let content; + if (crowdFund) { + if (crowdFund.contributors.length) { + content = crowdFund.contributors.map(contributor => ( + } + /> + )); + } else { + content = ( + + ); + } + } else { + content = ; + } + + return ( + + {crowdFund.contributors.length ? ( + <> + Contributors + {content} + + ) : ( + content + )} + + ); +}; + +export default ContributorsBlock; diff --git a/frontend/client/components/Proposal/index.tsx b/frontend/client/components/Proposal/index.tsx index cf7bce48..a4f3a060 100644 --- a/frontend/client/components/Proposal/index.tsx +++ b/frontend/client/components/Proposal/index.tsx @@ -15,6 +15,7 @@ import Milestones from './Milestones'; import CommentsTab from './Comments'; import UpdatesTab from './Updates'; import GovernanceTab from './Governance'; +import ContributorsTab from './Contributors'; // import CommunityTab from './Community'; import * as Styled from './styled'; import { withRouter } from 'next/router'; @@ -113,9 +114,12 @@ class ProposalDetail extends React.Component {
- + + + + )} diff --git a/frontend/client/components/UserRow/index.tsx b/frontend/client/components/UserRow/index.tsx index 0efe2253..4efa0732 100644 --- a/frontend/client/components/UserRow/index.tsx +++ b/frontend/client/components/UserRow/index.tsx @@ -5,11 +5,10 @@ import * as Styled from './styled'; interface Props { address: string; + secondary?: React.ReactNode; } -// TODO - don't hardcode monero image - -const UserRow = ({ address }: Props) => ( +const UserRow = ({ address, secondary }: Props) => ( @@ -18,7 +17,7 @@ const UserRow = ({ address }: Props) => ( - {/* user.title */} + {secondary && {secondary}} ); diff --git a/frontend/client/modules/proposals/reducers.ts b/frontend/client/modules/proposals/reducers.ts index f6293704..d935846f 100644 --- a/frontend/client/modules/proposals/reducers.ts +++ b/frontend/client/modules/proposals/reducers.ts @@ -14,7 +14,7 @@ export interface User { export interface Contributor { address: string; - contributionAmount: string; + contributionAmount: Wei; refundVote: boolean; refunded: boolean; proportionalContribution: string; diff --git a/frontend/client/modules/web3/actions.ts b/frontend/client/modules/web3/actions.ts index 19b75c85..84a20bfd 100644 --- a/frontend/client/modules/web3/actions.ts +++ b/frontend/client/modules/web3/actions.ts @@ -7,6 +7,7 @@ import { sleep } from 'utils/helpers'; import { fetchProposal, fetchProposals } from 'modules/proposals/actions'; import { PROPOSAL_CATEGORY } from 'api/constants'; import { AppState } from 'store/reducers'; +import { Wei } from 'utils/units'; type GetState = () => AppState; @@ -97,10 +98,10 @@ interface MilestoneData { } interface ProposalContractData { - ethAmount: number | string; // TODO: BigNumber + ethAmount: Wei; payOutAddress: string; trusteesAddresses: string[]; - milestoneAmounts: number[] | string[]; // TODO: BigNumber + milestoneAmounts: Wei[]; milestones: MilestoneData[]; durationInMinutes: number; milestoneVotingPeriodInMinutes: number; diff --git a/frontend/client/utils/helpers.ts b/frontend/client/utils/helpers.ts index 6921b17e..0b0ca13c 100644 --- a/frontend/client/utils/helpers.ts +++ b/frontend/client/utils/helpers.ts @@ -5,7 +5,3 @@ export function isNumeric(n: any) { export async function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } - -export function computePercentage(num: number, percent: number) { - return (num / 100) * percent; -} diff --git a/frontend/client/utils/units.ts b/frontend/client/utils/units.ts index fbe316f4..935fcca7 100644 --- a/frontend/client/utils/units.ts +++ b/frontend/client/utils/units.ts @@ -93,7 +93,13 @@ const fromWei = (wei: Wei, unit: UnitKey) => { return baseToConvertedUnit(wei.toString(), decimal); }; -const toWei = (value: string, decimal: number): Wei => { +const toWei = (value: string, unitType: number | UnitKey): Wei => { + let decimal; + if (typeof unitType === 'number') { + decimal = unitType; + } else if (typeof unitType === 'string') { + decimal = getDecimalFromEtherUnit(unitType); + } const wei = convertedToBaseUnit(value, decimal); return Wei(wei); }; diff --git a/frontend/client/web3interact/crowdFund.ts b/frontend/client/web3interact/crowdFund.ts index b8edde09..4996edd2 100644 --- a/frontend/client/web3interact/crowdFund.ts +++ b/frontend/client/web3interact/crowdFund.ts @@ -2,6 +2,7 @@ import Web3 from 'web3'; import { CrowdFund, Milestone, MILESTONE_STATE } from 'modules/proposals/reducers'; import { collectArrayElements } from 'utils/web3Utils'; import { Wei } from 'utils/units'; +import BN from 'bn.js'; export async function getCrowdFundState( crowdFundContract: any, @@ -26,7 +27,10 @@ export async function getCrowdFundState( ? 100 : balance.divn(100).isZero() ? 0 - : target.div(balance.divn(100)).toNumber(); + : balance + .mul(new BN(100)) + .div(target) + .toNumber(); const amountVotingForRefund = isRaiseGoalReached ? Wei(await crowdFundContract.methods.amountVotingForRefund().call({ from: account })) : Wei('0'); @@ -103,6 +107,11 @@ export async function getCrowdFundState( .call({ form: account }), ), ); + contributor.contributionAmount = Wei( + await crowdFundContract.methods + .getContributorContributionAmount(addr) + .call({ from: account }), + ); return contributor; }), );