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:
parent
329319692a
commit
3862b18670
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue