From d58a5c9e5daead33dd98d12ac69ddba7c31aca1d Mon Sep 17 00:00:00 2001 From: moshmage Date: Mon, 20 Mar 2023 11:07:58 +0000 Subject: [PATCH] BEPRO 2.11 (#916) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bepro 1370 fix UI bounty hall logos main nav logo and bounty hero states (#896) * adding styles and logic bg * adding color to bountyTags * adjusting BountyTags component * update Bagde logic * adding opacity logic and refactoring style * adding opacity param to hero * adjusting space * adding import * adding color to hero * adding ? to colors primary * adjusting if association network * DEV-982 Know Your Client (#860) * DEV-982 add isKyc to bounties * DEV-982 add isKyc badge * DEV-982 add kyc session model * DEV-982 init kyc session * DEV-982 validate kyc session * DEV-982 remove tier settings * DEV-982 create helper find user by session * DEV-982 isolate helper query * DEV-982 add isKyc to bounties * DEV-982 change isKyc to tierList * DEV-982 add tierList to settings * DEV-982 validate tier per bounty * DEV-982 refresh step btn * DEV-982 restore loading state * DEV-982 changes request * DEV-982 fix validate tiers * DEV-982 fix check isKyc * DEV-982 restore package-lock * DEV-982 refresh token * DEV-982 add isKyc to bounties * DEV-982 add isKyc badge * DEV-982 init kyc session * DEV-982 validate kyc session * DEV-982 change isKyc to tierList * DEV-982 changes request * DEV-982 fix conflicts * DEV-982 fix conflicts * DEV-982 remove debugger * DEV-982 restore package-json * DEV-982 restore check * DEV-982 fix loading modal * DEV-982 fix size modal * doctors hands: Account for matching accounts before verifying a user * linted * clean dupes from rebase * fix loggers * make it so page.action on bounty redirects to profile and have the profile hold the kyc button * dont return if no session * make it so the timer actually makes sense and cleans when supposed to clean and creates only when needed * make text green, adds a contextual span if success * change labels * add close label * simpler isVerified logic * change timer from 30s to 3s * remove "identifying..." label from modal --------- Co-authored-by: clarkjoao <46800211+clarkjoao@users.noreply.github.com> Co-authored-by: clarkjoao Co-authored-by: Vitor Hugo Co-authored-by: Marcus Vinícius * Bepro 1414 is possible to config fees to 100 (#882) * adding validator * removing console.log * validating limits * using interpolation for limits * fix limits errors * fix condition to update network (#908) * Bepro 1465 explore icon not aligned with rest of nav (#909) * creating style mgt-3 * adding mgt-3 to main-nav and adjusting Icon * fix fundedAmount logic (#905) * fix order disabled disputed button (#904) * adding text-truncate (#917) * Bepro 1410 governance tab missing individual fees configuration (#902) * adding parameters * updating dappkit version * oracle exchange rate is not percent * calculate exchange rate and add colors to modal body * fix readonly alert visible out of a network * adding missing parameters * network can no longer update the treasury address * fix file name * adding limits to new parameters and checking solidity integer limit * change minimum to 1, because of contract things * only integer * fix * adding unable to change warning * using N scale * using lower case on address * using lower case on address * adding review suggestion * renaming validators * fix reading of undefined * fix reading of undefined * using calculateDistributedAmounts to check minimum value * fix condition * fix NaN --------- Co-authored-by: Marcus Vinícius Co-authored-by: clarkjoao <46800211+clarkjoao@users.noreply.github.com> Co-authored-by: clarkjoao Co-authored-by: Vitor Hugo --- .env.example | 21 +- components/badge.tsx | 11 +- components/bounty-hero.tsx | 17 +- components/bounty/bounty-tags.tsx | 13 +- components/bounty/issue-body.tsx | 2 +- components/bounty/issue-edit-tag.tsx | 5 +- .../bounty/tabs-sections/item-sections.tsx | 10 +- components/create-bounty-details.tsx | 2 +- components/create-bounty-modal.tsx | 59 ++++- components/create-proposal.tsx | 46 ++-- .../network-contract-settings.tsx | 91 +++++-- .../network-parameter-input.tsx | 23 +- .../custom-network/new-network-stepper.tsx | 40 ++- components/dropdown.tsx | 51 ++++ components/issue-amount-info.tsx | 5 +- components/issue-description.tsx | 5 +- components/issue-list-item.tsx | 15 +- components/main-nav.tsx | 6 +- components/modals/kyc-session.tsx | 105 ++++++++ components/nav-avatar.tsx | 4 +- components/oracles-actions.tsx | 14 +- components/page-actions.tsx | 51 ++-- .../governance-settings.tsx | 13 +- .../profile/my-network-settings/index.tsx | 85 ++++--- .../my-network-settings/registry-settings.tsx | 117 +++++++-- components/proposal-action-card.tsx | 2 +- components/proposal-progress-bar.tsx | 6 +- components/transaction-type.tsx | 4 + components/transactions-icon.tsx | 4 + contexts/bounty-effects.tsx | 5 + contexts/global-effects.tsx | 5 + contexts/network-settings.tsx | 231 ++++++++++-------- contexts/reducers/change-current-bounty.ts | 8 +- contexts/reducers/change-current-user.ts | 7 +- .../20221230112020-add-bounty-kyc-option.js | 21 ++ .../20221230115552-add-kyc-session.js | 54 ++++ db/models/index.js | 2 + db/models/issue.model.js | 19 ++ db/models/kyc-session.model.js | 35 +++ db/models/user.js | 5 +- helpers/{contants.ts => constants.ts} | 8 +- helpers/custom-network.ts | 39 ++- helpers/handleNetworksValuesApi.ts | 2 +- helpers/network.ts | 48 ++++ helpers/query/findUserBySession.ts | 27 ++ helpers/registry.ts | 33 +++ interfaces/application-state.ts | 6 +- interfaces/enums/transaction-types.ts | 4 + interfaces/issue-data.ts | 12 +- interfaces/kyc-session.ts | 35 +++ interfaces/network.ts | 11 +- middleware/admin-route.ts | 2 +- middleware/issue-route.ts | 2 +- next.config.js | 13 +- package-lock.json | 32 ++- package.json | 3 +- pages/[network]/profile/index.tsx | 7 +- pages/api/issue/index.ts | 8 +- pages/api/kyc/init.ts | 53 ++++ pages/api/kyc/validate.ts | 65 +++++ pages/api/network/index.ts | 4 +- pages/api/nft/index.ts | 2 +- pages/api/search/count/index.ts | 33 +++ pages/api/search/issues/index.ts | 19 +- pages/api/search/tokens/index.ts | 38 +++ pages/api/tokens/index.ts | 2 +- pages/setup.tsx | 2 +- public/locales/en/bounty.json | 16 +- public/locales/en/common.json | 4 + public/locales/en/custom-network.json | 27 +- public/locales/en/my-oracles.json | 4 +- public/locales/en/setup.json | 5 +- scripts/settings/save-from-env.js | 2 + services/api.ts | 24 +- services/dao-service.ts | 4 +- styles/styles.scss | 8 + styles/variables.scss | 3 + types/dappkit.d.ts | 8 +- types/settings.d.ts | 10 + x-hooks/use-api.tsx | 64 +++-- x-hooks/use-authentication.tsx | 25 +- x-hooks/use-bounty.tsx | 20 ++ 82 files changed, 1534 insertions(+), 419 deletions(-) create mode 100644 components/dropdown.tsx create mode 100644 components/modals/kyc-session.tsx create mode 100644 db/migrations/20221230112020-add-bounty-kyc-option.js create mode 100644 db/migrations/20221230115552-add-kyc-session.js create mode 100644 db/models/kyc-session.model.js rename helpers/{contants.ts => constants.ts} (74%) create mode 100644 helpers/network.ts create mode 100644 helpers/query/findUserBySession.ts create mode 100644 helpers/registry.ts create mode 100644 interfaces/kyc-session.ts create mode 100644 pages/api/kyc/init.ts create mode 100644 pages/api/kyc/validate.ts create mode 100644 pages/api/search/count/index.ts create mode 100644 pages/api/search/tokens/index.ts diff --git a/.env.example b/.env.example index 43fb705a6a..2602759e23 100644 --- a/.env.example +++ b/.env.example @@ -126,4 +126,23 @@ ELASTIC_APM_ACTIVE= ELASTIC_APM_LOG_LEVEL= # true|false - should we index Logger.trace() into elastic -ELASTIC_INDEX_STACK_TRACE= \ No newline at end of file +ELASTIC_INDEX_STACK_TRACE= + +# Enables KYC per bounty provided by Synaps +NEXT_PUBLIC_ENABLE_KYC=true + +# key-pair for using synaps +NEXT_KYC_API=https://individual-api.synaps.io/v3 +NEXT_SYNAPS_KEY= +NEXT_SYNAPS_CLIENT_ID= +#types/settings.d.ts#L17 ex: [{"id": "1234", "name": "Phone", "steps_id":["12345678"]}] +NEXT_SYNAPS_TIER_LIST= + +# Leaderboard points per type +NEXT_PUBLIC_BOUNTY_CLOSED_DEV= +NEXT_PUBLIC_BOUNTY_CLOSED_OWNER= +NEXT_PUBLIC_BOUNTY_OPENED= +NEXT_PUBLIC_BOUNTY_CANCELED= +NEXT_PUBLIC_PROPOSAL_CREATED= +NEXT_PUBLIC_PROPOSAL_ACCEPTED= +NEXT_PUBLIC_PROPOSAL_REJECTED= diff --git a/components/badge.tsx b/components/badge.tsx index e3ff6c8490..fb4e47cdd4 100644 --- a/components/badge.tsx +++ b/components/badge.tsx @@ -1,4 +1,4 @@ -import { ReactElement } from "react"; +import { CSSProperties, ReactElement } from "react"; import { Badge as ReactBadge } from "react-bootstrap"; interface BadgeProps { @@ -6,20 +6,23 @@ interface BadgeProps { color?: string; className?: string; children?: ReactElement + style?: CSSProperties; } export default function Badge({ label, color = "primary", className, - children + children, + style }: BadgeProps) { return( {label ? label : children} ); -} \ No newline at end of file +} diff --git a/components/bounty-hero.tsx b/components/bounty-hero.tsx index 27cd9667d4..aa7564f39a 100644 --- a/components/bounty-hero.tsx +++ b/components/bounty-hero.tsx @@ -11,14 +11,15 @@ import GithubInfo from "components/github-info"; import PriceConversor from "components/price-conversor"; import Translation from "components/translation"; -import {useAppState} from "contexts/app-state"; - import {getIssueState} from "helpers/handleTypeIssue"; import {truncateAddress} from "helpers/truncate-address"; +import {useAppState} from "../contexts/app-state"; +import Badge from "./badge"; + export default function BountyHero() { const {t} = useTranslation(["bounty", "common"]); - + const {state} = useAppState(); function renderPriceConversor() { @@ -52,6 +53,11 @@ export default function BountyHero() { })} /> + {state.currentBounty?.data?.isKyc + ? : null}
- +
} diff --git a/components/bounty/bounty-tags.tsx b/components/bounty/bounty-tags.tsx index 286fb78412..9d48bb106b 100644 --- a/components/bounty/bounty-tags.tsx +++ b/components/bounty/bounty-tags.tsx @@ -2,10 +2,14 @@ import Badge from "components/badge"; interface BountyTagsProps { tags: string[]; + color?: string; + opacity?: boolean; } export default function BountyTags({ - tags + tags, + color, + opacity = true } : BountyTagsProps) { if (!tags) return <>; @@ -15,9 +19,10 @@ export default function BountyTags({ )} ); -} \ No newline at end of file +} diff --git a/components/bounty/issue-body.tsx b/components/bounty/issue-body.tsx index 3a7ad683e7..f9aa5fd8d7 100644 --- a/components/bounty/issue-body.tsx +++ b/components/bounty/issue-body.tsx @@ -11,7 +11,7 @@ import IssueProposalProgressBar from "components/issue-proposal-progress-bar"; import { useAppState } from "contexts/app-state"; import { addToast } from "contexts/reducers/change-toaster"; -import { BODY_CHARACTERES_LIMIT } from "helpers/contants"; +import { BODY_CHARACTERES_LIMIT } from "helpers/constants"; import useApi from "x-hooks/use-api"; import { useBounty } from "x-hooks/use-bounty"; diff --git a/components/bounty/issue-edit-tag.tsx b/components/bounty/issue-edit-tag.tsx index eaf02883d6..f21e8ed51c 100644 --- a/components/bounty/issue-edit-tag.tsx +++ b/components/bounty/issue-edit-tag.tsx @@ -6,13 +6,12 @@ import { useTranslation } from "next-i18next"; import { PROGRAMMING_LANGUAGES } from "assets/bounty-labels"; +import BountyTags from "components/bounty/bounty-tags"; import { ContextualSpan } from "components/contextual-span"; import { useAppState } from "contexts/app-state"; -import { MAX_TAGS } from "helpers/contants"; - -import BountyTags from "./bounty-tags"; +import { MAX_TAGS } from "helpers/constants"; interface IssueEditTagProps { isEdit: boolean; diff --git a/components/bounty/tabs-sections/item-sections.tsx b/components/bounty/tabs-sections/item-sections.tsx index f608f2c4d6..e13f597dd0 100644 --- a/components/bounty/tabs-sections/item-sections.tsx +++ b/components/bounty/tabs-sections/item-sections.tsx @@ -33,8 +33,8 @@ function ItemSections({ data, isProposal }: ItemProps) { const { getURLWithNetwork } = useNetwork(); const branchProtectionRules = state.Service?.network?.repos?.active?.branchProtectionRules; - const approvalsRequired = - branchProtectionRules ? + const approvalsRequired = + branchProtectionRules ? branchProtectionRules[state.currentBounty?.data?.branch]?.requiredApprovingReviewCount || 0 : 0; const canUserApprove = state.Service?.network?.repos?.active?.viewerPermission !== "READ"; @@ -52,7 +52,7 @@ function ItemSections({ data, isProposal }: ItemProps) { }; const status = [] - const proposal = + const proposal = state.currentBounty?.data?.mergeProposals?.find((proposal) => proposal.contractId === +item?.contractId); const isDisputed = !!proposal?.isDisputed const isMerged = item?.isMerged; @@ -82,7 +82,7 @@ function ItemSections({ data, isProposal }: ItemProps) { return ( - { shouldRenderApproveButton && + { shouldRenderApproveButton &&
(0); const [isBountyType, setisBountyType] = useState(true); const [rewardChecked, setRewardChecked] = useState(false); + const [isKyc, setIsKyc] = useState(false); + const [tierList, setTierList] = useState([]); const [transactionalToken, setTransactionalToken] = useState(); const [bountyDescription, setBountyDescription] = useState(""); const [progressPercentage, setProgressPercentage] = useState(0); @@ -134,6 +138,10 @@ export default function CreateBountyModal() { } } + function handleIsKYCChecked(e) { + setIsKyc(e.target.checked); + } + function renderDetails(review = false) { return ( { setisBountyType(false); setRewardChecked(true); + setIsKyc(false); + setTierList([]); setIssueAmount(ZeroNumberFormatValues); }} > @@ -254,6 +264,38 @@ export default function CreateBountyModal() { {rewardChecked && renderBountyToken(false, "reward")} )} + {isBountyType && Settings?.kyc?.isKycEnabled ? ( + <> +
+ + + + +
+ {isKyc && Settings?.kyc?.tierList?.length ? ( + { + setTierList(Array.isArray(opt) ? opt.map((i) => +i.value) : [+opt.value]) + } + } + options={Settings?.kyc?.tierList.map((i) => ({ + value: i.id, + label: i.name, + }))} + /> + ) : null} + + ) : null}
); @@ -382,6 +424,13 @@ export default function CreateBountyModal() { isRewardAmount ) return true; + + if ( + currentSection === 1 && + isKyc && Settings?.kyc?.tierList?.length && !tierList.length + ) + return true; + if (currentSection === 2 && (!repository || !branch)) return true; if (currentSection === 3 && !isTokenApproved) return true; return currentSection === 3 && isLoadingCreateBounty; @@ -412,6 +461,8 @@ export default function CreateBountyModal() { setRewardAmount(ZeroNumberFormatValues); setRepository(undefined); setBranch(null); + setIsKyc(false); + setTierList([]); setCurrentSection(0); } @@ -472,7 +523,9 @@ export default function CreateBountyModal() { body: payload.body, creator: payload.githubUser, repositoryId: payload.repositoryId, - tags: selectedTags + tags: selectedTags, + isKyc: isBountyType ? isKyc : false, + tierList: isBountyType ? tierList : null, }, Service?.network?.active?.name) .then((cid) => cid) diff --git a/components/create-proposal.tsx b/components/create-proposal.tsx index 2bfde360c6..cf812a4e96 100644 --- a/components/create-proposal.tsx +++ b/components/create-proposal.tsx @@ -15,6 +15,7 @@ import PullRequestLabels from "components/pull-request-labels"; import ReactSelect from "components/react-select"; import ReadOnlyButtonWrapper from "components/read-only-button-wrapper"; +import calculateDistributedAmounts from "helpers/calculateDistributedAmounts"; import sumObj from "helpers/sumObj"; import {pullRequest} from "interfaces/issue-data"; @@ -114,6 +115,10 @@ export default function NewProposal({amountTotal, pullRequests = []}) { const { getUserWith, processEvent } = useApi(); const currentBounty = useBounty(); + const bountyAmount = BigNumber(amountTotal); + const proposerFeeShare = state.Service?.network?.amounts?.proposerFeeShare || 0; + const mergeCreator = state.Service?.network?.amounts?.mergeCreatorFeeShare || 0; + const treasury = state.Service.network.amounts?.treasury; function handleChangeDistrib(params: { [key: string]: number }): void { setDistrib((prevState) => { @@ -179,41 +184,26 @@ export default function NewProposal({amountTotal, pullRequests = []}) { } else { handleInputColor("success"); } + + const proposalDetails = + currentDistrbuition.prAddressAmount.map(({ amount, address }) => ({ percentage: amount, recipient: address })); + + const distributedAmounts = + calculateDistributedAmounts(treasury, mergeCreator, proposerFeeShare, bountyAmount, proposalDetails); + + if (distributedAmounts.proposals.some(({ value, percentage }) => + BigNumber(value).lt(1e-15) && BigNumber(percentage).gt(0))) { + handleInputColor("error"); + setShowExceptionalMessage(true); + } } + if ((currentAmount > 0 && currentAmount < 100) || currentAmount > 100) { handleInputColor("error"); } if (currentAmount === 0) { handleInputColor("normal"); } - - if (currentAmount === 100) { - const bountyAmount = BigNumber(amountTotal); - const proposerFeeShare = BigNumber(state.Service?.network?.amounts?.proposerFeeShare || 0); - const mergeCreator = BigNumber(state.Service?.network?.amounts?.mergeCreatorFeeShare || 0); - const closeFee = BigNumber(state.Service.network.amounts?.treasury?.closeFee || 0); - const subtract = - [proposerFeeShare, mergeCreator, closeFee] - .reduce((p, c) => p.plus(c.multipliedBy(bountyAmount).dividedBy(100)), BigNumber(0)); - - const availableAmount = bountyAmount.minus(subtract); - - for (let i = 0; i < participants.length; i++) { - const githubHandle = participants[i].githubHandle; - const proposalValue = BigNumber(obj[githubHandle]); - - if (!obj[githubHandle]) - continue; - - const value = proposalValue.multipliedBy(availableAmount).dividedBy(100); - - if (value.lt(1e-15)) { - handleInputColor("error"); - setShowExceptionalMessage(true); - i = participants.length; - } - } - } } function handleInputColor(name: string) { diff --git a/components/custom-network/network-contract-settings.tsx b/components/custom-network/network-contract-settings.tsx index 0ac5a98194..ec0a210a50 100644 --- a/components/custom-network/network-contract-settings.tsx +++ b/components/custom-network/network-contract-settings.tsx @@ -1,3 +1,4 @@ +import BigNumber from "bignumber.js"; import {useTranslation} from "next-i18next"; import NetworkParameterInput from "components/custom-network/network-parameter-input"; @@ -5,76 +6,112 @@ import NetworkParameterInput from "components/custom-network/network-parameter-i import {useAppState} from "contexts/app-state"; import {useNetworkSettings} from "contexts/network-settings"; -import {formatNumberToCurrency} from "helpers/formatNumber"; - +import {formatNumberToCurrency, formatNumberToNScale} from "helpers/formatNumber"; +import { NETWORK_LIMITS } from "helpers/network"; export default function NetworkContractSettings() { - const {t} = useTranslation(["common", "custom-network"]); - const {state} = useAppState(); + const { t } = useTranslation(["common", "custom-network"]); - const {fields, settings, LIMITS} = useNetworkSettings(); - - const handleDraftTimeChange = value => fields.parameter.setter({ label: "draftTime", value }); - const handleDisputeTimeChange = - value => fields.parameter.setter({ label: "disputableTime", value }); - const handleCouncilAmountChange = - value => fields.parameter.setter({ label: "councilAmount", value }); - const handlePercentageForDisputeChange = - value => fields.parameter.setter({ label: "percentageNeededForDispute", value }); + const { state } = useAppState(); + const { fields, settings } = useNetworkSettings(); + + const onChange = (label) => (value) => fields.parameter.setter({label, value}); const networkTokenSymbol = state.Service?.network?.networkToken?.symbol || t("misc.$token"); + const totalNetworkToken = BigNumber(state.Service?.network?.amounts?.totalNetworkToken); const parameterInputs = [ { label: t("custom-network:dispute-time"), description: t("custom-network:errors.dispute-time", { - min: LIMITS?.disputableTime?.min, - max: formatNumberToCurrency(LIMITS?.disputableTime?.max, 0) + min: NETWORK_LIMITS.disputableTime.min, + max: formatNumberToCurrency(NETWORK_LIMITS.disputableTime.max, 0) }), symbol: t("misc.seconds"), value: settings?.parameters?.disputableTime?.value, error: settings?.parameters?.disputableTime?.validated === false, decimals: 0, - onChange: handleDisputeTimeChange + onChange: onChange("disputableTime") }, { label: t("custom-network:percentage-for-dispute"), - description: t("custom-network:errors.percentage-for-dispute", { - max: LIMITS?.percentageNeededForDispute?.max - }), + description: t("custom-network:errors.percentage-for-dispute", NETWORK_LIMITS.percentageNeededForDispute), symbol: "%", value: settings?.parameters?.percentageNeededForDispute?.value, error: settings?.parameters?.percentageNeededForDispute?.validated === false, - onChange: handlePercentageForDisputeChange + onChange: onChange("percentageNeededForDispute") }, { label: t("custom-network:redeem-time"), description: t("custom-network:errors.redeem-time", { - min: LIMITS?.draftTime?.min, - max: formatNumberToCurrency(LIMITS?.draftTime?.max, 0) + min: NETWORK_LIMITS.draftTime.min, + max: formatNumberToCurrency(NETWORK_LIMITS.draftTime.max, 0) }), symbol: t("misc.seconds"), value: settings?.parameters?.draftTime?.value, error: settings?.parameters?.draftTime?.validated === false, decimals: 0, - onChange: handleDraftTimeChange + onChange: onChange("draftTime") }, { label: t("custom-network:council-amount"), description: t("custom-network:errors.council-amount", { token: networkTokenSymbol, - min: formatNumberToCurrency(LIMITS?.councilAmount?.min, 0), - max: formatNumberToCurrency(LIMITS?.councilAmount?.max, 0) + min: formatNumberToCurrency(NETWORK_LIMITS.councilAmount.min, 0), + max: formatNumberToCurrency(NETWORK_LIMITS.councilAmount.max, 0) }), symbol: networkTokenSymbol || "Token", value: settings?.parameters?.councilAmount?.value, error: settings?.parameters?.councilAmount?.validated === false, - onChange: handleCouncilAmountChange + onChange: onChange("councilAmount") + }, + { + label: t("custom-network:cancelable-time.label"), + description: t("custom-network:cancelable-time.description", { + min: formatNumberToCurrency(NETWORK_LIMITS.cancelableTime.min, 0) + }), + symbol: t("misc.seconds"), + value: settings?.parameters?.cancelableTime?.value, + error: settings?.parameters?.cancelableTime?.validated === false, + decimals: 0, + onChange: onChange("cancelableTime") + }, + { + label: t("custom-network:oracle-exchange-rate.label"), + description: t("custom-network:oracle-exchange-rate.description", NETWORK_LIMITS.oracleExchangeRate), + symbol: "", + value: settings?.parameters?.oracleExchangeRate?.value, + error: settings?.parameters?.oracleExchangeRate?.validated === false, + decimals: 0, + onChange: onChange("oracleExchangeRate"), + disabled: totalNetworkToken.gt(0), + helperText: totalNetworkToken.gt(0) ? t("custom-network:oracle-exchange-rate.unable-to-change", { + amount: formatNumberToNScale(totalNetworkToken.toFixed()), + symbol: networkTokenSymbol + }) : "" + }, + { + label: t("custom-network:merger-fee.label"), + description: t("custom-network:merger-fee.description", NETWORK_LIMITS.mergeCreatorFeeShare), + symbol: "%", + value: settings?.parameters?.mergeCreatorFeeShare?.value, + error: settings?.parameters?.mergeCreatorFeeShare?.validated === false, + decimals: 4, + onChange: onChange("mergeCreatorFeeShare") + }, + { + label: t("custom-network:proposer-fee.label"), + description: t("custom-network:proposer-fee.description", NETWORK_LIMITS.proposerFeeShare), + symbol: "%", + value: settings?.parameters?.proposerFeeShare?.value, + error: settings?.parameters?.proposerFeeShare?.validated === false, + decimals: 4, + onChange: onChange("proposerFeeShare") } ]; return ( -
+
{ parameterInputs.map(input => ) }
); diff --git a/components/custom-network/network-parameter-input.tsx b/components/custom-network/network-parameter-input.tsx index 33fdd68c3f..4efa4b0ca8 100644 --- a/components/custom-network/network-parameter-input.tsx +++ b/components/custom-network/network-parameter-input.tsx @@ -14,7 +14,8 @@ interface NetworkParameterInputProps { onBlur?: () => void; decimals?: number; className?: string; - disabled?: boolean + disabled?: boolean; + helperText?: string; } export default function NetworkParameterInput({ @@ -25,26 +26,28 @@ export default function NetworkParameterInput({ value, ...props } : NetworkParameterInputProps) { + const [inputValue, setInputValue] = useState(null); - const [inputValue, setInputValue] = useState(null) + const debounce = useRef(null); - const debounce = useRef(null) - const handleChange = (values: NumberFormatValues) => { if(values.floatValue === inputValue) return; - setInputValue(values.floatValue) + + setInputValue(values.floatValue); - clearTimeout(debounce.current) + clearTimeout(debounce.current); debounce.current = setTimeout(() => { - onChange(values.floatValue) - }, 500) + onChange(values.floatValue); + }, 500); }; - useEffect(()=>{if(value !== inputValue) setInputValue(value)},[value]) + useEffect(() => { + if(value !== inputValue) setInputValue(value); + },[value]); return( -
+
{ @@ -140,7 +172,7 @@ function NewNetwork() { return error; }); - setCreatingNetwork(6); + setCreatingNetwork(10); cleanStorage?.(); await processEvent("registry", "registered", payload.name.toLowerCase(), { fromBlock: registrationTx.blockNumber }) .then(() => router.push(getURLWithNetwork("/", { network: payload.name.toLowerCase().replaceAll(" ", "-") }))) diff --git a/components/dropdown.tsx b/components/dropdown.tsx new file mode 100644 index 0000000000..8a2305498c --- /dev/null +++ b/components/dropdown.tsx @@ -0,0 +1,51 @@ +import {useEffect, useState} from "react"; +import Select, {Props as ReactSelectProps} from 'react-select'; + +import {useTranslation} from "next-i18next"; + +type DropdownOption = { + label: string; + value: string; +} + +interface DropDownProps extends ReactSelectProps { + label?: string + options: DropdownOption[], + onSelected?: (option: DropdownOption | DropdownOption[]) => void, +} + +export default function DropDown({ + label, + options: defaultOptions, + onSelected, + className, + ...props +}: DropDownProps) { + const { t } = useTranslation("common"); + + const [option, setOption] = useState(); + const [options, setOptions] = useState(defaultOptions || []); + + const handleChange = (newValue) => { + setOption(newValue); + onSelected?.(newValue) + }; + + useEffect(()=> setOptions(defaultOptions) ,[defaultOptions]) + + return ( +
+ {label ? : null} +