Fix Funding Progress calculation

Fix BN types
Add contributors tab
Adjust UserRow to optionally show amount as secondary text
Add contributionAmount to contributors in crowdFund
General cleanup
This commit is contained in:
Daniel Ternyak 2018-09-16 01:46:40 -05:00
parent 329319692a
commit 3862b18670
No known key found for this signature in database
GPG Key ID: DF212D2DC5D0E245
9 changed files with 75 additions and 24 deletions

View File

@ -12,11 +12,11 @@ import { RadioChangeEvent } from 'antd/lib/radio';
import TrusteeFields from './TrusteeFields'; import TrusteeFields from './TrusteeFields';
import MilestoneFields, { Milestone } from './MilestoneFields'; import MilestoneFields, { Milestone } from './MilestoneFields';
import CreateSuccess from './CreateSuccess'; import CreateSuccess from './CreateSuccess';
import { computePercentage } from 'utils/helpers';
import { getAmountError } from 'utils/validators'; import { getAmountError } from 'utils/validators';
import MarkdownEditor from 'components/MarkdownEditor'; import MarkdownEditor from 'components/MarkdownEditor';
import * as Styled from './styled'; import * as Styled from './styled';
import { Wei, toWei } from 'utils/units';
import BN from 'bn.js';
interface StateProps { interface StateProps {
crowdFundLoading: AppState['web3']['crowdFundLoading']; crowdFundLoading: AppState['web3']['crowdFundLoading'];
crowdFundError: AppState['web3']['crowdFundError']; crowdFundError: AppState['web3']['crowdFundError'];
@ -74,8 +74,8 @@ const DEFAULT_STATE: State = {
milestoneDeadline: 60 * 60 * 24 * 7, milestoneDeadline: 60 * 60 * 24 * 7,
}; };
function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: number) { function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: Wei) {
return computePercentage(raiseGoal, milestone.payoutPercent); return raiseGoal.divn(100).mul(new BN(milestone.payoutPercent));
} }
class CreateProposal extends React.Component<Props, State> { class CreateProposal extends React.Component<Props, State> {
@ -159,7 +159,7 @@ class CreateProposal extends React.Component<Props, State> {
}; };
createCrowdFund = async () => { createCrowdFund = async () => {
const { contract, createCrowdFund, web3 } = this.props; const { contract, createCrowdFund } = this.props;
const { const {
title, title,
proposalBody, proposalBody,
@ -173,11 +173,11 @@ class CreateProposal extends React.Component<Props, State> {
} = this.state; } = this.state;
const backendData = { content: proposalBody, title, category }; const backendData = { content: proposalBody, title, category };
const targetInWei = toWei(amountToRaise, 'ether');
const targetInWei = web3.utils.toWei(String(amountToRaise), 'ether');
const milestoneAmounts = milestones.map(milestone => const milestoneAmounts = milestones.map(milestone =>
milestoneToMilestoneAmount(milestone, targetInWei), Wei(milestoneToMilestoneAmount(milestone, targetInWei)),
); );
console.log('milestoneAmounts', milestoneAmounts);
const immediateFirstMilestonePayout = milestones[0].immediatePayout; const immediateFirstMilestonePayout = milestones[0].immediatePayout;
const contractData = { const contractData = {

View File

@ -0,0 +1,33 @@
import React from 'react';
import { Spin } from 'antd';
import { CrowdFund } from 'modules/proposals/reducers';
import UserRow from 'components/UserRow';
import * as ProposalStyled from '../styled';
interface Props {
crowdFund: CrowdFund;
}
const ContributorsBlock = ({ crowdFund }: Props) => {
let content;
if (crowdFund) {
content = crowdFund.contributors.map(contributor => (
<UserRow
key={contributor.address}
address={contributor.address}
amount={contributor.contributionAmount}
/>
));
} else {
content = <Spin />;
}
return (
<ProposalStyled.SideBlock>
<ProposalStyled.BlockTitle>Contributors</ProposalStyled.BlockTitle>
<ProposalStyled.Block>{content}</ProposalStyled.Block>
</ProposalStyled.SideBlock>
);
};
export default ContributorsBlock;

View File

@ -15,6 +15,7 @@ import Milestones from './Milestones';
import CommentsTab from './Comments'; import CommentsTab from './Comments';
import UpdatesTab from './Updates'; import UpdatesTab from './Updates';
import GovernanceTab from './Governance'; import GovernanceTab from './Governance';
import ContributorsTab from './Contributors';
// import CommunityTab from './Community'; // import CommunityTab from './Community';
import * as Styled from './styled'; import * as Styled from './styled';
import { withRouter } from 'next/router'; import { withRouter } from 'next/router';
@ -113,9 +114,12 @@ class ProposalDetail extends React.Component<Props, State> {
<div style={{ marginTop: '1.5rem' }} /> <div style={{ marginTop: '1.5rem' }} />
<UpdatesTab proposalId={proposal.proposalId} /> <UpdatesTab proposalId={proposal.proposalId} />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Governance" key="governanc"> <Tabs.TabPane tab="Governance" key="governance">
<GovernanceTab proposal={proposal} /> <GovernanceTab proposal={proposal} />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Contributors" key="contributors">
<ContributorsTab crowdFund={proposal.crowdFund} />
</Tabs.TabPane>
</Tabs> </Tabs>
)} )}
</Styled.Container> </Styled.Container>

View File

@ -2,14 +2,14 @@ import React from 'react';
import ShortAddress from 'components/ShortAddress'; import ShortAddress from 'components/ShortAddress';
import Identicon from 'components/Identicon'; import Identicon from 'components/Identicon';
import * as Styled from './styled'; import * as Styled from './styled';
import { Wei, fromWei } from 'utils/units';
interface Props { interface Props {
address: string; address: string;
amount?: Wei;
} }
// TODO - don't hardcode monero image const UserRow = ({ address, amount }: Props) => (
const UserRow = ({ address }: Props) => (
<Styled.Container> <Styled.Container>
<Styled.Avatar> <Styled.Avatar>
<Identicon address={address} /> <Identicon address={address} />
@ -18,7 +18,9 @@ const UserRow = ({ address }: Props) => (
<Styled.InfoMain> <Styled.InfoMain>
<ShortAddress address={address} /> <ShortAddress address={address} />
</Styled.InfoMain> </Styled.InfoMain>
<Styled.InfoSecondary>{/* user.title */}</Styled.InfoSecondary> {amount && (
<Styled.InfoSecondary>{fromWei(amount, 'ether')} ETH</Styled.InfoSecondary>
)}
</Styled.Info> </Styled.Info>
</Styled.Container> </Styled.Container>
); );

View File

@ -14,7 +14,7 @@ export interface User {
export interface Contributor { export interface Contributor {
address: string; address: string;
contributionAmount: string; contributionAmount: Wei;
refundVote: boolean; refundVote: boolean;
refunded: boolean; refunded: boolean;
proportionalContribution: string; proportionalContribution: string;

View File

@ -7,6 +7,7 @@ import { sleep } from 'utils/helpers';
import { fetchProposal, fetchProposals } from 'modules/proposals/actions'; import { fetchProposal, fetchProposals } from 'modules/proposals/actions';
import { PROPOSAL_CATEGORY } from 'api/constants'; import { PROPOSAL_CATEGORY } from 'api/constants';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { Wei } from 'utils/units';
type GetState = () => AppState; type GetState = () => AppState;
@ -97,10 +98,10 @@ interface MilestoneData {
} }
interface ProposalContractData { interface ProposalContractData {
ethAmount: number | string; // TODO: BigNumber ethAmount: Wei;
payOutAddress: string; payOutAddress: string;
trusteesAddresses: string[]; trusteesAddresses: string[];
milestoneAmounts: number[] | string[]; // TODO: BigNumber milestoneAmounts: Wei[];
milestones: MilestoneData[]; milestones: MilestoneData[];
durationInMinutes: number; durationInMinutes: number;
milestoneVotingPeriodInMinutes: number; milestoneVotingPeriodInMinutes: number;

View File

@ -5,7 +5,3 @@ export function isNumeric(n: any) {
export async function sleep(ms: number) { export async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
export function computePercentage(num: number, percent: number) {
return (num / 100) * percent;
}

View File

@ -38,7 +38,7 @@ export const Units = {
gether: '1000000000000000000000000000', gether: '1000000000000000000000000000',
tether: '1000000000000000000000000000000', tether: '1000000000000000000000000000000',
}; };
const handleValues = (input: string | BN) => { const handleValues = (input: string | BN | number) => {
if (typeof input === 'string') { if (typeof input === 'string') {
return input.startsWith('0x') ? new BN(stripHexPrefix(input), 16) : new BN(input); return input.startsWith('0x') ? new BN(stripHexPrefix(input), 16) : new BN(input);
} }
@ -58,7 +58,7 @@ const Data = (input: string) => toBuffer(addHexPrefix(input));
const Nonce = (input: string | BN) => handleValues(input); const Nonce = (input: string | BN) => handleValues(input);
const Wei = (input: string | BN): Wei => handleValues(input); const Wei = (input: string | BN | number): Wei => handleValues(input);
const TokenValue = (input: string | BN) => handleValues(input); const TokenValue = (input: string | BN) => handleValues(input);
@ -93,7 +93,13 @@ const fromWei = (wei: Wei, unit: UnitKey) => {
return baseToConvertedUnit(wei.toString(), decimal); 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); const wei = convertedToBaseUnit(value, decimal);
return Wei(wei); return Wei(wei);
}; };

View File

@ -2,6 +2,7 @@ import Web3 from 'web3';
import { CrowdFund, Milestone, MILESTONE_STATE } from 'modules/proposals/reducers'; import { CrowdFund, Milestone, MILESTONE_STATE } from 'modules/proposals/reducers';
import { collectArrayElements } from 'utils/web3Utils'; import { collectArrayElements } from 'utils/web3Utils';
import { Wei } from 'utils/units'; import { Wei } from 'utils/units';
import BN from 'bn.js';
export async function getCrowdFundState( export async function getCrowdFundState(
crowdFundContract: any, crowdFundContract: any,
@ -26,7 +27,10 @@ export async function getCrowdFundState(
? 100 ? 100
: balance.divn(100).isZero() : balance.divn(100).isZero()
? 0 ? 0
: target.div(balance.divn(100)).toNumber(); : balance
.mul(new BN(100))
.divRound(target)
.toNumber();
const amountVotingForRefund = isRaiseGoalReached const amountVotingForRefund = isRaiseGoalReached
? Wei(await crowdFundContract.methods.amountVotingForRefund().call({ from: account })) ? Wei(await crowdFundContract.methods.amountVotingForRefund().call({ from: account }))
: Wei('0'); : Wei('0');
@ -103,6 +107,11 @@ export async function getCrowdFundState(
.call({ form: account }), .call({ form: account }),
), ),
); );
contributor.contributionAmount = Wei(
await crowdFundContract.methods
.getContributorContributionAmount(addr)
.call({ from: account }),
);
return contributor; return contributor;
}), }),
); );